CodeGym /課程 /JAVA 25 SELF /取得類別、欄位、方法的資訊

取得類別、欄位、方法的資訊

JAVA 25 SELF
等級 62 , 課堂 1
開放

1. 取得欄位資訊

getFields()getDeclaredFields() 有何不同

在 Java 中,每個類別都有欄位(fields)— 定義在類別內的變數。透過反射,我們可以得知它們的名稱、型別、存取修飾詞(public/private),並在執行期間加以存取。主要入口點是 Class<?> 物件。

Class 類別中用於處理欄位的主要方法:

方法 回傳內容
getFields()
類別、其父類別與介面的所有 public 欄位陣列
getDeclaredFields()
所有 在此類別中宣告的欄位陣列

類比:getFields() 就像只參觀展示大廳(public),而 getDeclaredFields() 連技術區(privateprotected、package-private)也會帶你看。

範例:列出類別的所有欄位

假設我們有一個類別:

public class Person {
    public String name;
    private int age;
    protected String email;
}

取得欄位資訊:

import java.lang.reflect.Field;

Class<?> clazz = Person.class;

// 所有 public 欄位(包含繼承而來的)
System.out.println("Public fields:");
for (Field field : clazz.getFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

// 此類別中宣告的所有欄位(包含 private,僅限本類別)
System.out.println("\nDeclared fields:");
for (Field field : clazz.getDeclaredFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

結果:

Public fields:
name : String

Declared fields:
name : String
age : int
email : String

如何取得欄位的修飾詞?

每個欄位(Field)都有修飾詞(public/private/protectedstaticfinal 等)。可以透過 getModifiers() 取得,並用 Modifier 轉成字串:

import java.lang.reflect.Modifier;

for (Field field : clazz.getDeclaredFields()) {
    int mods = field.getModifiers();
    System.out.println(field.getName() + " : " + Modifier.toString(mods));
}

2. 取得方法資訊

方法 getMethods()getDeclaredMethods()

方法是物件可以執行的動作。透過反射可以得知類別擁有哪些方法、其參數、回傳型別、修飾詞與註解。

方法 回傳內容
getMethods()
類別及其父類別(包含 Object)的所有 public 方法
getDeclaredMethods()
在此類別中宣告的所有方法(包含 private

範例:列出類別的所有方法

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());
}

結果(對於 Person):

Public methods:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait

Declared methods:
(沒有,如果 Person 沒有宣告自己的方法)

Person 中新增方法:

public class Person {
    public String name;
    private int age;
    protected String email;

    public void sayHello() {
        System.out.println("Hi!");
    }
}

現在 getDeclaredMethods() 也會顯示它。

如何取得方法的參數與回傳型別?

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(");");
}

輸出:

public void sayHello();

3. 取得建構子資訊

建構子是用來建立物件的特殊方法。

方法 回傳內容
getConstructors()
所有 public 建構子
getDeclaredConstructors()
類別中宣告的所有建構子

範例:列出類別的所有建構子

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(");");
}

如果類別只有預設建構子,也會被列出。

4. 註解:如何得知類別、方法或欄位上標註了什麼

註解是可加在類別、方法、欄位與參數上的特殊標記。透過反射可以得知元素上有哪些註解。

取得註解

  • 對於類別:clazz.getAnnotations()
  • 對於方法:method.getAnnotations()
  • 對於欄位:field.getAnnotations()

範例:檢查是否存在註解 @Deprecated

for (Method method : clazz.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Deprecated.class)) {
        System.out.println(method.getName() + " is @Deprecated");
    }
}

範例:列出類別的所有註解

for (var annotation : clazz.getAnnotations()) {
    System.out.println(annotation);
}

5. 實作:用小程式分析類別結構

把上述內容整合,寫一個小工具,根據類別名稱輸出其結構:欄位、方法、建構子與註解。

程式碼範例: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("請輸入類別的完整名稱(例如,java.util.ArrayList):");
        String className = scanner.nextLine();

        Class<?> clazz = Class.forName(className);

        System.out.println("\n== 類別: " + clazz.getName() + " ==");

        // 類別的註解
        System.out.println("註解:");
        for (Annotation annotation : clazz.getAnnotations()) {
            System.out.println("  " + annotation);
        }

        // 欄位
        System.out.println("\n欄位:");
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println("  " + Modifier.toString(field.getModifiers())
                    + " " + field.getType().getSimpleName()
                    + " " + field.getName());
        }

        // 方法
        System.out.println("\n方法:");
        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(");");
        }

        // 建構子
        System.out.println("\n建構子:");
        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(");");
        }
    }
}

這怎麼運作?

  • 使用者輸入類別的完整名稱(例如 "java.util.ArrayList" 或自己的類別)。
  • 程式動態載入該類別,並輸出其註解、欄位、方法與建構子。
  • 試試看 JDK 的標準類別 — 你會發現裡面藏了很多有趣的東西!

6. 實用細節與視覺化

視覺化示意:透過反射可以取得哪些類別資訊

graph TD
    A[Class<?>] --> B["getFields/getDeclaredFields"]
    A --> C[getMethods/getDeclaredMethods]
    A --> D[getConstructors/getDeclaredConstructors]
    A --> E[getAnnotations]
    B --> F[Field: 類型、名稱、修飾詞]
    C --> G[Method: 回傳型別、參數]
    D --> H[Constructor: 參數]
    E --> I["Annotation[]"]

表格:在哪裡查詢資訊

想知道什麼 透過反射如何取得
所有 public 欄位
clazz.getFields()
所有宣告的欄位
clazz.getDeclaredFields()
所有 public 方法
clazz.getMethods()
所有宣告的方法
clazz.getDeclaredMethods()
所有 public 建構子
clazz.getConstructors()
所有宣告的建構子
clazz.getDeclaredConstructors()
類別的註解
clazz.getAnnotations()
方法/欄位的註解
method.getAnnotations(), field.getAnnotations()
修飾詞
Modifier.toString(field.getModifiers())

7. 使用反射時的常見錯誤

錯誤 1:混淆 getFields()getDeclaredFields()
如果你要找的是 private 欄位,請使用 getDeclaredFields(),而不是 getFields()。前者會回傳此類別中宣告的所有欄位,後者只回傳 public 欄位(而且包含繼承而來的!)。

錯誤 2:沒有處理受檢例外(checked exceptions)。
許多反射 API 方法會拋出例外(例如 ClassNotFoundExceptionSecurityException)。別忘了處理它們,或在方法簽章中宣告。

錯誤 3:忽略了存取修飾詞。
要存取 private 欄位與方法,必須先呼叫 setAccessible(true),否則會得到 IllegalAccessException。範例:

Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // 開放存取 private 欄位
int value = (int) field.get(person);

錯誤 4:只期望看到自己宣告的方法/欄位。
getMethods()getFields() 會回傳不只目前類別的 public 成員,還包含所有父類別(包含 Object)的成員。如果你只想看到自己寫的東西,這點可能會讓你驚訝。

錯誤 5:沒有正確檢查註解是否存在。
請使用 isAnnotationPresent() 來檢查特定註解,而不是沒必要地手動遍歷註解陣列。

1
任務
JAVA 25 SELF, 等級 62, 課堂 1
上鎖
秘密裝置稽核 🕵️
秘密裝置稽核 🕵️
1
任務
JAVA 25 SELF, 等級 62, 課堂 1
上鎖
古代儀式目錄 📜
古代儀式目錄 📜
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION