系列博客專欄:
- JVM系列博客專欄
- SpringBoot系列博客
1、什么是Java反射機制?
在程式運行中動態地獲取類的相關屬性,同時呼叫物件的方法和獲取屬性,這種機制被稱之為Java反射機制
下面給出一個反射的簡單例子:
import lombok.Data;
@Data
public class User {
public String username;
private String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
public static void reflectionSimpleExample() throws Exception{
User user = new User();
System.out.println(user.toString());
// 獲取對應類的class
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
System.out.println(obj);
// 獲取成員變數,注意要是public的
Field field = cls.getField("username");
System.out.println(field.get(obj));
}
2、反射機制原理
Java反射是Java實作動態語言的關鍵,也就是通過反射實作類動態加載
- 靜態加載: 在編譯時加載相關的類,如果找不到類就會報錯,依賴性比較強
- 動態加載:在運行時加載需要的類,在專案跑起來之后,呼叫才會報錯,降低了依賴性
例子:靜態加載,如下代碼,如果找不到類的情況,代碼編譯都不通過
User user = new User();
而動態加載,就是反射的情況,是可以先編譯通過的,然后在呼叫代碼時候,也就是運行時才會報錯
Class<?> cls = Class.forName("com.example.core.example.reflection.User");
Object obj = cls.newInstance();
Exception in thread “main” java.lang.ClassNotFoundException: com.example.core.example.reflection.User
java中的反射允許程式在執行期借助jdk中Reflection API來獲取類的內部資訊,比如成員變數、成員方法、構造方法等等,并能操作類的屬性和方法
java中反射的實作和jvm和類加載機制有一定的關系,加載好類之后,在jvm的堆中會產生一個class型別的物件,這個class類包括了類的完整結構資訊,通過這個class物件就可以獲取到類的結構資訊,所以形象地稱之為java反射

3、Class類介紹
3.1、Class類基本介紹
然后這個Class類是什么?看下uml類圖:

Class也是類,因此也繼承Object類
Class不是直接new出來的,而是經過系統創建的
User user = new User();
打個斷點,debug進行看看原始碼,可以看到傳統的直接new一個物件也是通過類加載器loadClass拿到的

java反射方式,通用通過除錯看看:
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
同樣本質也是通過ClassLoad再通過類的全類名

3.2、Class類物件的獲取方法
- Class.forname()
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
應用場景:多用于讀取類全路徑,加載類
- 具體類.class
已經知道具體類的情況,通過具體類的class屬性獲取
Class u = User.class;
應用場景:多用于用于引數傳遞
- 物件.getClass
已經創建好物件的情況,直接通過物件實體獲取class物件
Class cls = user.getClass();
應用場景:通過創建好的物件,獲取Class物件
- ClassLoader獲取
ClassLoader cl = user.getClass().getClassLoader();
Class cls = cl.loadClass("com.example.core.example.reflection.domain.User");
- 基本資料型別
Class cls = int.class;
- 包裝類
基本資料型別對應的包裝類可以通過.TYPE得到Class類物件
Class cls = Integer.TYPE;
3.3 、可以獲取Class物件的型別
- 1、外部類,成員內部類,靜態內部類,區域內部類,匿名內部類
- 2、interface:介面
- 3、陣列
- 4、enum:列舉
- 5、annotation:注解
- 6、基本資料型別
- 7、void
- 8、Class
- … ,等等
import com.example.core.example.reflection.domain.User;
import lombok.ToString;
import java.util.List;
public class GetClassObjectExample {
public static void main(String[] args) {
// 外部類
Class<User> cls1 = User.class;
// 介面
Class<List> cls2 = List.class;
// 陣列
Class<Integer[]> cls3 = Integer[].class;
// 二維陣列
Class<String[][]> cls4 = String[][].class;
// 注解
Class<lombok.ToString> cls5 = ToString.class;
// 列舉
Class<Thread.State> cls6 = Thread.State.class;
// 基本資料型別
Class<Long> cls7 = Long.class;
// void 資料型別
Class<Void> cls8 = Void.class;
// Class
Class<Class> cls9 = Class.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
4、java反射的作用?
- 可以通過外部類的全路徑名創建物件,并使用這些類
- 可以列舉出類的全部成員,包括建構式、屬性、方法
- 利用反射 API 訪問類的私有成員
5、反射API主要類
- 1、java.lang.Class:代表一個類,表示某個類在jvm堆中的物件
- 2、 java.lang.reflect.Method:代表類的方法
- 3、 java.lang.reflect.Field:代表類的成員變數
- 4、 java.lang.reflect.Constructor:代表類額構造方法
6、Java反射的優缺點
- 優點:使用Java反射可以靈活動態地創建和使用物件,反射是框架的底層支撐
- 缺點:使用Java反射,基本就是解釋執行的,對執行速度是有影響的
7、反射呼叫的優化方法
前面介紹了Java反射雖然很靈活,但是缺點就是呼叫時候比較慢,相對直接new物件來說,情況是怎么樣的?下面通過例子進行試驗:
import com.example.core.example.reflection.domain.User;
import java.lang.reflect.Method;
public class TestReflectionExample {
private static final Integer TOTAL_COUNT = 1000000;
public static void main(String[] args) throws Exception{
test0();
test1();
test2();
}
public static void test0() {
long start = System.currentTimeMillis();
User user = new User();
for (int i = 0 ; i < TOTAL_COUNT; i++) {
user.hello();
}
System.out.println(String.format("傳統呼叫方法執行時間:%d" , System.currentTimeMillis() - start));
}
public static void test1() throws Exception{
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
Method hello = cls.getMethod("hello");
for (int i = 0 ; i < TOTAL_COUNT; i++) {
hello.invoke(obj);
}
System.out.println(String.format("反射方法呼叫執行時間:%d" , System.currentTimeMillis() - start));
}
public static void test2() throws Exception{
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
Method hello = cls.getMethod("hello");
// 關鍵,取消呼叫反射方法時的安全檢查
hello.setAccessible(true);
for (int i = 0 ; i < TOTAL_COUNT; i++) {
hello.invoke(obj);
}
System.out.println(String.format("優化后的反射方法呼叫執行時間:%d" , System.currentTimeMillis() - start));
}
}
傳統呼叫方法執行時間:19
反射方法呼叫執行時間:112
優化后的反射方法呼叫執行時間:50
8、反射的基本使用例子
- java.lang.Class類
| 方法名 | 作用 |
|---|---|
| getName | 獲取全類名 |
| getSimpleName | 獲取簡單類名 |
| getFields | 獲取所有public修飾的屬性、包括本類以及父類的 |
| getDeclaredFields | 獲取本類中所有的屬性 |
| getMethods | 獲取所有的public修飾的方法,包括本類以及父類的 |
| getDeclaredMethod | 獲取本類中所有方法 |
| getConstructors | 獲取所有public修飾的構造器,只有本類 |
| getDeclaredConstructors | 獲取本類中所有構造器 |
| getPackage | 以Package形式回傳 包資訊 |
| getSuperClass | 以Class形式回傳父資訊 |
| getInterfaces | 以Class[]形式回傳父介面資訊 |
| getAnnotations | 以Annotation[]形式回傳注解資訊 |
String allClassName = "com.example.core.example.reflection.domain.User";
// 通過全類名獲取class物件
Class<?> cls = Class.forName(allClassName);
System.out.println(cls);
// 通過物件獲取class
System.out.println(cls.getClass());
// 獲取全類名
System.out.println(cls.getName());
// 獲取包名
System.out.println(cls.getClass().getPackage().getName());
// 獲取物件實體
Object obj = cls.newInstance();
System.out.println(obj);
- java.lang.reflect.Field類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式回傳修飾符,默認修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getType | 以Class形式回傳型別 |
| getName | 回傳屬性名稱 |
// 獲取類屬性
Field field = cls.getField("username");
field.set(obj , "admin");
System.out.println(field.get(obj));
// 獲取所有類屬性,private的成員變數沒有權限訪問
Field[] fields = cls.getFields();
for (Field field1 : fields) {
System.out.println(field1.get(obj));
}
// 獲取所有類屬性包括private成員變數
Field[] allFields = cls.getDeclaredFields();
for (Field afield : allFields) {
// 開放權限,私有的成員變數也能列印出來
afield.setAccessible(true);
System.out.println(afield.get(obj));
}
- java.lang.reflect.Method類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式回傳修飾符,默認修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getName | 回傳方法名 |
| getReturnType | 以class形式回傳型別 |
| getParmeterTypes | 以Class[] 回傳引數型別陣列 |
// 獲取class方法,同樣默認情況不能獲取private的方法
Method method = cls.getMethod("hello");
System.out.println(method.invoke(obj));
- java.lang.reflect.Constructor類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式回傳修飾符,默認修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getName | 回傳方法名 |
| getParmeterTypes | 以Class[] 回傳引數型別陣列 |
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
System.out.println(obj);
9、反射開放權限操作
在我們使用Java反射獲取class的private成員變數或者方法時,這種情況是不允許獲取的,不過可以通過開放權限的方式來處理,通過設定setAccessible(true);即可
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* <pre>
* 開放java反射權限,允許呼叫類的private屬性或者方法
* </pre>
* <p>
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2021/08/09 19:10 修改內容:
* </pre>
*/
public class AccessibleReflectionExample {
public static void main(String[] args) throws Exception{
test();
}
public static void test() throws Exception{
// 創建class實體物件
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
// 獲取私有成員變數
Field pwd = cls.getDeclaredField("password");
// 開放私有變數的訪問權限
pwd.setAccessible(true);
pwd.set(obj , "123456");
// 私有方法呼叫
Method method = cls.getDeclaredMethod("priToString");
method.setAccessible(true);
System.out.println(method.invoke(obj));
}
}
Exception in thread “main” java.lang.IllegalAccessException: Class com.example.core.example.reflection.AccessibleReflectionExample can not access a member of class com.example.core.example.reflection.domain.User with modifiers “private”
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/293173.html
標籤:java
上一篇:【建議收藏】畢設/私活/大佬必備,一個掙錢的標準開源前后端分離【springboot+vue+redis+Spring Security】腳手架--若依框架
下一篇:c重戰——第八站(分支結構設計)
