CodeGym /Các khóa học /JAVA 25 SELF /Lấy thông tin về lớp, trường, phương thức

Lấy thông tin về lớp, trường, phương thức

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Lấy thông tin về các trường

Khác nhau giữa getFields()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ì
getFields()
Mảng tất cả các trường public của lớp, các lớp cha và interface của nó
getDeclaredFields()
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()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ì
getMethods()
Tất cả public method của lớp và các lớp cha (bao gồm Object)
getDeclaredMethods()
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ì
getConstructors()
Tất cả public constructor
getDeclaredConstructors()
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
clazz.getFields()
Tất cả các trường được khai báo
clazz.getDeclaredFields()
Tất cả các phương thức public
clazz.getMethods()
Tất cả các phương thức được khai báo
clazz.getDeclaredMethods()
Tất cả các constructor public
clazz.getConstructors()
Tất cả các constructor được khai báo
clazz.getDeclaredConstructors()
Annotation của lớp
clazz.getAnnotations()
Annotation của phương thức/trường
method.getAnnotations(), field.getAnnotations()
Modifiers
Modifier.toString(field.getModifiers())

7. Những lỗi thường gặp khi làm việc với reflection

Lỗi số 1: Nhầm getFields()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()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.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION