文章目錄
- 1. 定義
- 2. 為什么要有反射
- 2. 用途
- 3. 反射相關的類
- 3.1 總覽圖
- 3.2 Class 類相關的方法
- 3.3 Filed 類相關的方法
- 3.4 Method 類相關的方法
- 3.5 Constructor 類相關的方法
- 4. 獲取 Class 物件
- 4.1 Class 類(反射機制的起源)
- 4.2 獲取 Class 物件三種方式
- 5. 反射的使用
- 6. 反射的優點和缺點
1. 定義
Java 的反射(Reflection)機制是在運行狀態中,對任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個物件,都能夠呼叫它的任意方法和屬性,因此,通過它我們就能夠修改部分型別資訊,而這種動態獲取資訊以及動態呼叫物件方法的功能稱為 Java 語言的反射機制
Java 的反射機制可以形象的理解為安檢的機器或者是照妖鏡,因為它和安檢機器和照妖鏡一樣都能夠顯露出事物內部的屬性
注意:
Java 的反射是在運行期間獲取型別的資訊,是在運行時的一種機制
2. 為什么要有反射
疑問: Java 已經有了封裝為什么還要有反射呢?這貌似不就破壞了封裝的特性嗎?
解答: 這里截取了 Oracle 官方檔案的一些話來回答這個問題
- 反射讓開發人員可以通過外部類的全路徑名創建物件,并使用這些類,實作一些擴展的功能
- 反射讓開發人員可以列舉出類的全部成員,包括建構式、屬性、方法,以幫助開發者寫出正確的代碼
- 測驗時可以利用反射 API 訪問類的私有成員,以保證測驗代碼覆寫率
畢竟,沒有最好,只有更好!反射機制就可以讓我們不斷地變得更好
2. 用途
這里介紹幾點關于反射的用途,例如:
-
在日常的第三方應用開發程序中,經常會遇到某個類的某個成員變數、方法是私有的或是只對系統應用開發,這時就可以利用 Java 的反射機制,通過反射來獲取所需的私有成員或方法
-
反射最重要的用途就是開發各種通用框架,比如在 Spring 中,我們會將所有的類 Bean 交給 Spring 容器管理,無論是 XML 配置 Bean,還注解配置,當我們從容器中獲取 Bean 來依賴注入時,容器會讀取配置,而配置中給的就是類的資訊,Spring 會根據這些資訊,需要創建哪些 Bean 就動態的創建這些類
-
Java 程式中有許多物件在運行時會出現兩種型別:運行時型別和編譯時型別,例如
Person p=new Student();這句代碼中,p 在編譯時型別為 Person,但在運行時型別為 Student,程式需要在運行時發現物件和類的真實資訊,而通過使用反射,程式就能夠正確判斷出該物件和類是屬于哪些類
3. 反射相關的類
3.1 總覽圖
Java 類的成員包括以下三類:成員變數、成員方法、構造方法,反射相關的類就是和這幾個成員相關

下面將介紹一些關于這幾類相關的方法,但是并不全,想查詢全部相關方法,可以去幫助檔案了解
3.2 Class 類相關的方法
| 方法 | 說明 |
|---|---|
getClassLoader() | 獲得類的加載器 |
getDeclaredClasses() | 回傳一個陣列,陣列中包含該類中所有類和介面類的物件(包括私有的) |
forName(String className) | 根據類名回傳類的物件 |
newInstance() | 創建類的實體 |
getName() | 獲得類的完整路徑名字 |
3.3 Filed 類相關的方法
| 方法 | 說明 |
|---|---|
getFiled(String name) | 獲得某個公有的屬性物件 |
getFileds() | 獲得所有公有的屬性物件 |
getDeclaredFiled(String name) | 獲得某個屬性物件 |
getDeclaredFileds() | 獲得所有屬性物件 |
3.4 Method 類相關的方法
| 方法 | 說明 |
|---|---|
getMethod(String name,Class...<?> parameterTypes) | 獲得該類某個公有的方法 |
getMethods() | 獲得該類所有公有的方法 |
getDeclaredMethod(String name,Class...<?> parameterTypes) | 獲得該類某個方法 |
getDeclaredMethods() | 獲得該類所有方法 |
3.5 Constructor 類相關的方法
| 方法 | 說明 |
|---|---|
getConstructor(Class...<?> parameterTypes) | 獲得該類中與引數型別匹配的公有構造方法 |
getConstructors() | 獲得該類的所有公有構造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 獲得該類中與引數型別匹配的構造方法 |
getDeclaredConstructor() | 獲得該型別所有的構造方法 |
4. 獲取 Class 物件
4.1 Class 類(反射機制的起源)
Class 類代表類的物體,在運行的 Java 應用程式中表示類和介面
Java 檔案被編譯后,會生成 .class 位元組碼檔案,而位元組碼檔案最終又被加載到了 JVM 中(具體加載到了 JVM 的方法區),之后 JVM 就會將要使用的位元組碼檔案決議為一個物件,而這個物件就是 java.lang.Class,它存盤在 JVM 的堆中,
因此,當程式運行時,每個 Java 檔案最終就變成了 Class 類物件的一個實體,我們通過 Java 的反射機制應用到這個實體,就可以去獲得甚至去添加、改變這個類的屬性和行為,使得這個類成為一個動態的類
注意:
- Class 是一個實實在在的類,位于
java.lang包中- 每個 Java 類運行時都在 JVM 里表現為一個 Class 物件
- 陣列也同樣會被映射為 Class 物件的一個類,所有具有相同元素型別和維數的陣列都共享該 Class 物件
- 基本型別和 void 同樣也會被表現為 Class 物件
以下均為對下面的 Student 類來獲取 Class 物件進行示例(可以先跳過這段代碼)
package demo;
public class Student{
// 私有屬性 name
private String name = "lb";
// 公有屬性 age
public int age = 18;
// 不帶引數的構造方法
public Student(){
System.out.println("Student()");
}
private Student(String name,int age) {
this.name = name; this.age = age;
System.out.println("Student(String,name)");
}
private void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
private void function(String str) {
System.out.println(str);
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' +
", age=" + age +
'}';
}
}
4.2 獲取 Class 物件三種方式
在反射之前,我們需要做的第一步就是先拿到當前需要反射的類的 Class 物件,然后通過 Class 物件的核心方法,達到反射的目的,
-
第一種(使用最多,前提是已明確類的全路徑): 使用
Class.forName("類的全路徑名");靜態方法獲取try{ Class c1=Class.forName("demo.Student"); }catch(ClassNotFoundException e){ e.printStackTrace(); }要處理例外是因為,Class 的 forName 方法中拋出了
ClassNotFoundException例外,防止輸入的字串不能找到對應的類
-
第二種(僅適合在編譯前就已經明確要操作的 Class): 使用類的 class 屬性獲取
Class c2=Student.class;該方式說明了任何一個類都有一個隱藏的靜態成員變數 class
-
第三種(不通用,需要構造類的物件): 使用物件的
getClass()方法獲取Student student=new Student(); Class c3=student.getClass();
注意:
一個類對應一個 Class 物件,可以通過對上述三個獲取的 Class 物件進行參考值的比較來證明
5. 反射的使用
接下來將以上述的 Student 類為例,來對它進行反射的操作示例
注意:
所有和反射相關的包都在
java.lang.reflect包下,使用前需要導包
補充:
將此物件的 accessible 標志設定為指示的布林值
值為 true,則指示反射的物件在使用時應該取消 Java 語言訪問檢查,
值為 false,則指示反射的物件應該實施 Java 語言訪問檢查、
實際上
setAccessible是啟用和禁用訪問安全檢查的開關,并不是為 true 就能訪問,為 false 就不能訪問,由于 JDK 的安全檢查耗時較多,所以通過setAccessible(true)的方式關閉安全檢查就可以達到提升反射速度的目的
示例:
public class ReflectClassDemo {
// 創建物件
public static void reflectNewInstance(){
Class<?> c=null;
try {
// 獲取 Class 物件
c=Class.forName("demo.Student");
// 通過獲取的物件來創建 Student 的物件
Student student=(Student) c.newInstance();
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 反射私有構造方法
public static void reflectPrivateConstructor(){
Class<?> c=null;
try {
// 獲取 Class 的物件
c=Class.forName("demo.Student");
// 獲取 Student 中私有的構造方法
Constructor<?> constructor=c.getDeclaredConstructor(String.class,int.class);
// 設定為 true 后可以修改訪問權限
constructor.setAccessible(true);
// 通過獲取的私有構造方法來創建 Student 物件
Student student=(Student) constructor.newInstance("張三",18);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 反射私有屬性
public static void reflectPrivateFiled(){
Class c=null;
try {
// 獲取物件
c=Class.forName("demo.Student");
// 獲取私有屬性
Field filed=(Field) c.getDeclaredField("name");
filed.setAccessible(true);
// 通過獲取的物件實體化
Student student=(Student) c.newInstance();
// 修改 student 這個物件的 filed 欄位,將其值修改為 張三
filed.set(student,"張三");
String name=(String) filed.get(student);
System.out.println("反射私有屬性修改了 name: "+name);
} catch (ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 反射私有方法
public static void reflectPrivateMethod(){
Class c=null;
try {
// 獲取物件
c=Class.forName("demo.Student");
// 獲取私有方法
Method method=c.getDeclaredMethod("function", String.class);
// 通過獲取到物件實體化
Student student=(Student) c.newInstance();
method.setAccessible(true);
// 給實體化物件的私有方法 function 賦值
method.invoke(student,"給私有方法 function 傳引數");
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
6. 反射的優點和缺點
優點:
- 對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個物件,都能夠呼叫它的任意一個方法
- 增加程式的靈活性和擴展性,降低耦合性,提高自適力
- 反射已經運用在了很多流行的闊加,如:Struts、Hibernate、Spring 等等
缺點:
- 使用反射會導致程式的效率降低
- 反射技術繞過源代碼的技術,因而會帶來維護的問題
- 反射代碼相比于直接的代碼更加復雜
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/385585.html
標籤:java
