1. Lấy thông tin về các trường
Khác nhau giữa getFields() và getDeclaredFields() là gì
Trong Java, mỗi lớp có các trường (fields) — các biến được khai báo bên trong lớp. Với reflection, ta có thể biết tên, kiểu, các modifier truy cập của chúng (public/private), cũng như truy cập vào chúng lúc runtime. Cổng vào chính là đối tượng Class<?>.
Các phương thức chính của lớp Class để làm việc với trường:
| Phương thức | Trả về gì |
|---|---|
|
Mảng tất cả các trường public của lớp, các lớp cha và interface của nó |
|
Mảng tất cả các trường được khai báo trong chính lớp này |
Tương tự như sau: getFields() — giống như tham quan chỉ khu trưng bày (public), còn getDeclaredFields() — bao gồm cả khu kỹ thuật (private, protected, package-private).
Ví dụ: in ra tất cả các trường của lớp
Giả sử chúng ta có một lớp:
public class Person {
public String name;
private int age;
protected String email;
}
Lấy thông tin về các trường:
import java.lang.reflect.Field;
Class<?> clazz = Person.class;
// Tất cả các trường public (bao gồm cả kế thừa)
System.out.println("Public fields:");
for (Field field : clazz.getFields()) {
System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}
// Tất cả các trường được khai báo (bao gồm private, chỉ của chính lớp)
System.out.println("\nDeclared fields:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}
Kết quả:
Public fields:
name : String
Declared fields:
name : String
age : int
email : String
Làm thế nào để biết các modifier của trường?
Mỗi trường (Field) có các modifier (public/private/protected, static, final, v.v.). Có thể lấy chúng qua getModifiers() và chuyển thành chuỗi bằng Modifier:
import java.lang.reflect.Modifier;
for (Field field : clazz.getDeclaredFields()) {
int mods = field.getModifiers();
System.out.println(field.getName() + " : " + Modifier.toString(mods));
}
2. Lấy thông tin về phương thức
Các phương thức getMethods() và getDeclaredMethods()
Phương thức là các hành động mà một đối tượng có thể thực hiện. Với reflection, ta có thể biết lớp có những phương thức nào, tham số, kiểu trả về, các modifier và các annotation của chúng.
| Phương thức | Trả về gì |
|---|---|
|
Tất cả public method của lớp và các lớp cha (bao gồm Object) |
|
Tất cả method được khai báo trong lớp này (bao gồm private) |
Ví dụ: in ra tất cả các phương thức của lớp
import java.lang.reflect.Method;
System.out.println("Public methods:");
for (Method method : clazz.getMethods()) {
System.out.println(method.getName());
}
System.out.println("\nDeclared methods:");
for (Method method : clazz.getDeclaredMethods()) {
System.out.println(method.getName());
}
Kết quả (cho Person):
Public methods:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait
Declared methods:
(không có gì nếu Person không khai báo phương thức nào của riêng mình)
Thêm một phương thức vào Person:
public class Person {
public String name;
private int age;
protected String email;
public void sayHello() {
System.out.println("Hi!");
}
}
Bây giờ getDeclaredMethods() cũng sẽ hiển thị phương thức này.
Làm thế nào để biết tham số và kiểu trả về của phương thức?
for (Method method : clazz.getDeclaredMethods()) {
System.out.print(Modifier.toString(method.getModifiers()) + " ");
System.out.print(method.getReturnType().getSimpleName() + " ");
System.out.print(method.getName() + "(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
Kết quả:
public void sayHello();
3. Lấy thông tin về constructor
Constructor là các phương thức đặc biệt dùng để tạo đối tượng.
| Phương thức | Trả về gì |
|---|---|
|
Tất cả public constructor |
|
Tất cả constructor được khai báo trong lớp |
Ví dụ: in ra tất cả các constructor của lớp
import java.lang.reflect.Constructor;
System.out.println("Constructors:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
System.out.print(clazz.getSimpleName() + "(");
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
Nếu lớp chỉ khai báo constructor mặc định, nó cũng sẽ được in ra.
4. Annotation: làm sao biết lớp, phương thức hoặc trường được gắn gì
Annotation là các nhãn đặc biệt có thể gắn vào lớp, phương thức, trường và tham số. Với reflection, ta có thể biết một phần tử có những annotation nào.
Lấy annotation
- Cho lớp: clazz.getAnnotations()
- Cho phương thức: method.getAnnotations()
- Cho trường: field.getAnnotations()
Ví dụ: kiểm tra sự hiện diện của annotation @Deprecated
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Deprecated.class)) {
System.out.println(method.getName() + " is @Deprecated");
}
}
Ví dụ: in ra tất cả annotation của lớp
for (var annotation : clazz.getAnnotations()) {
System.out.println(annotation);
}
5. Thực hành: chương trình nhỏ để phân tích cấu trúc lớp
Hãy gom tất cả lại và viết một tiện ích nhỏ, theo tên lớp sẽ in ra cấu trúc của nó: trường, phương thức, constructor và annotation.
Ví dụ mã: ClassInspector
import java.lang.reflect.*;
import java.util.Scanner;
public class ClassInspector {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("Nhập tên đầy đủ của lớp (ví dụ: java.util.ArrayList): ");
String className = scanner.nextLine();
Class<?> clazz = Class.forName(className);
System.out.println("\n== Lớp: " + clazz.getName() + " ==");
// Annotation của lớp
System.out.println("Annotation:");
for (Annotation annotation : clazz.getAnnotations()) {
System.out.println(" " + annotation);
}
// Trường
System.out.println("\nTrường:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(" " + Modifier.toString(field.getModifiers())
+ " " + field.getType().getSimpleName()
+ " " + field.getName());
}
// Phương thức
System.out.println("\nPhương thức:");
for (Method method : clazz.getDeclaredMethods()) {
System.out.print(" " + Modifier.toString(method.getModifiers())
+ " " + method.getReturnType().getSimpleName()
+ " " + method.getName() + "(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
// Constructor
System.out.println("\nConstructor:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
System.out.print(" " + Modifier.toString(constructor.getModifiers())
+ " " + clazz.getSimpleName() + "(");
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
}
}
Nó hoạt động như thế nào?
- Người dùng nhập tên đầy đủ của lớp (ví dụ, "java.util.ArrayList" hoặc lớp của chính mình).
- Chương trình nạp lớp động và in ra các annotation, trường, phương thức và constructor của nó.
- Hãy thử với các lớp chuẩn của JDK — bạn sẽ thấy có rất nhiều điều thú vị ẩn bên trong!
6. Những lưu ý hữu ích và trực quan hóa
Sơ đồ trực quan: có thể biết gì về một lớp thông qua reflection
graph TD
A[Class<?>] --> B["getFields/getDeclaredFields"]
A --> C[getMethods/getDeclaredMethods]
A --> D[getConstructors/getDeclaredConstructors]
A --> E[getAnnotations]
B --> F[Field: kiểu, tên, modifiers]
C --> G[Method: kiểu trả về, tham số]
D --> H[Constructor: tham số]
E --> I["Annotation[]"]
Bảng: tìm thông tin ở đâu
| Muốn biết gì | Cách lấy bằng reflection |
|---|---|
| Tất cả các trường public | |
| Tất cả các trường được khai báo | |
| Tất cả các phương thức public | |
| Tất cả các phương thức được khai báo | |
| Tất cả các constructor public | |
| Tất cả các constructor được khai báo | |
| Annotation của lớp | |
| Annotation của phương thức/trường | |
| Modifiers | |
7. Những lỗi thường gặp khi làm việc với reflection
Lỗi số 1: Nhầm getFields() và getDeclaredFields().
Nếu bạn cần tìm trường private, hãy dùng getDeclaredFields(), không phải getFields(). Cái đầu trả về tất cả trường được khai báo trong lớp, còn cái sau — chỉ các trường public (và cả những trường được kế thừa!).
Lỗi số 2: Không xử lý các ngoại lệ “checked”.
Nhiều phương thức của reflection ném ngoại lệ (ví dụ, ClassNotFoundException hoặc SecurityException). Đừng quên xử lý chúng hoặc khai báo trong chữ ký của phương thức.
Lỗi số 3: Không tính đến các modifier truy cập.
Truy cập vào các trường và phương thức private chỉ khả dụng sau khi gọi setAccessible(true), nếu không sẽ có IllegalAccessException. Ví dụ:
Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // Mở quyền truy cập tới trường private
int value = (int) field.get(person);
Lỗi số 4: Kỳ vọng chỉ thấy các phương thức/trường của riêng mình.
getMethods() và getFields() trả về các thành viên public không chỉ của lớp hiện tại, mà còn của tất cả lớp cha của nó, bao gồm cả Object. Điều này có thể gây bất ngờ nếu bạn chỉ mong thấy những gì mình đã viết.
Lỗi số 5: Không kiểm tra sự hiện diện của annotation đúng cách.
Hãy dùng isAnnotationPresent() để kiểm tra một annotation cụ thể, thay vì duyệt mảng annotation một cách không cần thiết.
GO TO FULL VERSION