怎麼理解java反射?
概述
Java 反射是可以讓我們在執行時間取得類別的方法、屬性、父類別、介面等類別的內部資訊的機制。也就是說,反射本質上是一個「反著來」的過程。當我們透過new創建一個類別的實例時,實際上是由Java虛擬機根據這個類別的Class物件在運行時構建出來的,而反射是透過一個類別的Class物件來獲取它的定義信息,從而我們可以訪問到它的屬性、方法,知道這個類別的父類別、實作了哪些介面等資訊。
Class類別
我們知道使用javac能夠將.java檔案編譯為.class文件,這個.class檔案包含了我們對類別的原始定義資訊(父類別、介面、建構器、屬性、方法等)。 .class檔案在運行時會被ClassLoader載入到Java虛擬機器(JVM)中,當一個.class檔案被載入後,JVM會為之產生一個Class對象,我們在程式中透過new實例化的物件其實是在運行時根據對應的Class物件建構出來的。確切的說,這個Class物件其實是java.lang.Class
在下面的講解中,我們將以People類別和Student類別為例:
public class People { private String name; private int age; public People(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public void speak() { System.out.println(getName() + " " + getAge()); } } public class Student extends People { private int grade; public Student(String name, int age) { super(name, age); } public Student(String name, int age, int grade) { super(name, age); this.grade = grade; } public int getGrade() { return grade; } public void setGrade(int grade) { this.grade = grade; } private void learn(String course) { System.out.println(name + " learn " + course); } }
透過類別名稱取得Class物件
若在編譯期間知道一個類別的名字,我們可以這樣取得它的Class物件:
Class<People> peopleClass = People.class;
還有一種根據類別的完整路徑名取得Class物件的方法如下所示:
//假设People类在com.test包中 Class<People> peopleClass = Class.forName("com.test.People");
注意,Class.forName()方法的參數必須是一個類別的全路徑名。實際上,只要我們「import com.test.People",就可以直接透過」People.class"取得他的Class對象,而不用寫出全路徑這麼麻煩。 (若在呼叫Class.forName()方法時,沒有在classpath找到對應的類,會拋出ClassNotFoundException。)
透過物件本身取得其Class物件
People people = new People("Bill", 18); Class<People> peopleClass = people.getClass();
透過反射取得類的建構器
一旦我們取得了People的Class 對象,我們便可以透過這個Class 物件取得到People類別的原始定義資訊。首先,我們來取得People類別的建構器對象,有了這個建構器對象,我們便能夠建構出一個People對像出來。例如,我們可以在Student.java中加入以下程式碼:
public static void main(String[] args) { Class<People> pClass = People.class; try { Constructor<People> constructor = pClass.getConstructor(String.class, int.class); People people = constructor.newInstance("Bill", 18); people.speak(); } catch (Exception e) { } }
在上面,我們呼叫getConstructor方法來取得一個People類別的建構器對象,由於我們想要取得的建構器的形參類型為String和int,所以我們傳入String.class和int.class。有了建構器對象,我們便可以呼叫newInstance方法來建立一個people對象。
注意,當透過反射取得到類別的Constructor、Method、Field物件後,在呼叫這些物件的方法之前,先將此物件的accessible 標誌設為true,以取消Java 語言存取檢查,可以提升反射速度。如以下程式碼所示:
Constructor<People> constructor = peopleClass.getConstructor(String.class, int.class); // 设置 constructor 的 Accessible属性为ture以取消Java的访问检查 constructor.setAccessible(true);
透過反射取得類別中宣告的方法
取得目前類別中宣告的方法(不包括從父類別繼承來的)
要取得目前類別中宣告的所有方法可以透過Class 中的getDeclaredMethods 函數,它會取得到目前類別中宣告的所有方法(包括private、public、static等各種方法),它會傳回一個Method物件數組,其中的每個Method物件即表示了一個類別中聲明的方法。想要取得指定的方法,可以呼叫getDeclaredMethod(String name, Class...
private static void showDeclaredMethods() { Student student = new Student("Bill", 18); //获取Student类声明的所有方法 Method[] methods = student.getClass().getDeclaredMethods(); try { //获取learnMethod对象(封装了learn方法) Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class); //获取learn方法的参数列表并打印出来 Class<?>[] paramClasses = learnMethod.getParameterTypes() ; for (Class<?> class : paramClasses) { System.out.println("learn方法的参数: " + class.getName()); } //判断learn方法是否为private System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers())); //调用learn方法 learnMethod.invoke(student, "Java Reflection"); } catch (Exception e) { } }
取得目前類別和父類別中宣告的公有方法
要取得目前類別以及父類別中宣告的所有public 方法可以呼叫getMethods 函數,而要取得某個指定的public方法,可以呼叫getMethod方法。請看以下程式碼:
private static void showMethods() { Student student = new Student("mr.simple"); // 获取所有public方法(包括Student本身的和从父类继承来的) Method[] methods = student.getClass().getMethods(); try { //注意,通过 getMethod只能获取public方法,若尝试获取private方法则会抛出异常 Method learnMethod = student.getClass().getMethod("learn", String.class); } catch (Exception e) { } }
透過反射取得類別中定義的屬性
取得屬性與取得方法是類似的,只不過把對getMethods() / getDeclaredMethods()方法的呼叫換成了對getFields() / getDeclaredFields()方法的呼叫。
取得目前類別中定義的屬性(不包括從父類別繼承來的屬性)
要取得目前類別中定義的所有屬性(包括private、public、static等各種屬性)可以呼叫Class物件的getDeclaredFields函數;要取得指定的屬性,可以呼叫getDeclaredField。如以下程式碼所示:
private static void showDeclaredFields() { Student student = new Student("Bill", 18); // 获取当前类中定义的所有属性 Field[] fields = student.getClass().getDeclaredFields(); try { // 获取指定的属性 Field gradeField = student.getClass().getDeclaredField("grade"); // 获取属性值 System.out.println("The grade is : " + gradeField.getInt(student)); // 设置属性值 gradeField.set(student, 10); } catch (Exception e) { } }
取得目前類別和父類別中定義的public屬性
要取得目前類別和父類別中定義的所有public 屬性可以呼叫Class物件的getFields 函數,而要取得某個指定的public屬性,可以呼叫getField方法,如以下程式碼所示:
private static void showFields() { Student student = new Student("Bill", 18); // 获取当前类和父类的所有public属性 Field[] publicFields = student.getClass().getFields(); }
透過反射取得類別的父類別及類別所實作的介面
获取父类
调用Class对象的getSuperClass方法即可,如以下代码所示:
Student student = new Student("Bill", 18); Class<?> superClass = student.getClass().getSuperclass();
获取所实现的接口
要知道一个类实现了哪些接口,只需调用Class对象的getInterfaces方法,如以下代码所示:
private static void showInterfaces() { Student student = new Student("Bill", 19); Class<?>[] interfaces = student.getClass().getInterfaces(); }
以上是怎麼理解java反射的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了各種Java垃圾收集算法(串行,並行,CMS,G1,ZGC),它們的性能影響和適合大量堆的應用。

本文討論了Java虛擬機(JVM),詳細介紹了其在不同平台運行Java程序中的作用。它說明了JVM的內部流程,密鑰組件,內存管理,垃圾收集和性能Optimizatio

Java的Nashorn Engine可以在Java應用程序中啟用JavaScript腳本。關鍵步驟包括設置Nashorn,管理腳本和優化性能。主要問題涉及安全性,內存管理和未來兼容性

Java的Try-with-Resources通過自動關閉文件流或數據庫連接等資源來簡化資源管理,從而提高代碼可讀性和可維護性。

Java枚舉代表固定的值集,通過自定義方法和構造函數提供類型安全性,可讀性和其他功能。它們增強了代碼組織,可用於開關語句中以進行有效的價值處理。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

Dreamweaver Mac版
視覺化網頁開發工具

禪工作室 13.0.1
強大的PHP整合開發環境

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3漢化版
中文版,非常好用