定義
JAVA反射機制是在運行狀態中,對于任何一個類,都能夠知道這個類的所有屬性和方法;
對于任何一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為 Java 語言的反射機制,
使用場景
反射是在運行時獲取確定型別,系結物件,
常見的兩個使用場景
- 運行時獲取物件的所有資訊
- 泛型擦除
在運行時獲取類
這里的運行時指的是程式在運行后,相應的還有編譯時,編譯時是編譯器將源代碼翻譯成機器能識別的代碼,
舉個例子說明編譯時和運行時的區別:

泛型擦除
val list = arrayListOf<Int>(1,2,3)
val clz = Class.forName("java.util.ArrayList")
val method = clz.getMethod("add", Object::class.java)
method.invoke(list,"hhh")
Log.e(TAG,"list:${list.toString()}")
輸出:
list:[1, 2, 3, hhh]
反射的利弊
利
- 運行時型別的判斷
- 動態類加載,動態代理使用反射,
弊
- 性能問題,反射相當于一系列解釋操作,
- JVM 不會去優化這部分代碼
- 代碼不易閱讀
為什么會出現性能問題?
Method#invoke方法會對引數做封裝和解封操作- 需要檢查方法可見性
- 需要校驗引數
- 反射方法難以行內
JVM無法優化:Java檔案中介紹
invoke方法原始碼:
class Class {
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 檢查方法權限
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
// 2. 獲取方法
Method method = getMethod0(name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 回傳方法的拷貝
return getReflectionFactory().copyMethod(method);
}
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 檢查方法權限
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
// 2. 獲取方法
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 回傳方法的拷貝
return getReflectionFactory().copyMethod(method);
}
}
如何使用
想要使用反射機制,就必須要先獲取到該類的位元組碼檔案物件
(.class),通過位元組碼檔案物件,就能夠通過該類中的方法獲取到我們想要的所有資訊(方法,屬性,類名,父類名,實作的所有介面等等),每一個類對應著一個位元組碼檔案也就對應著一個Class型別的物件,
獲取位元組碼檔案物件
- 1、根據類名:
類名.class - 2、根據物件:
物件.getClass() - 3、根據全限定類名:
Class.forName(全限定類名)
常用的是第一種,
獲取建構式
建構式分為兩種:帶參和不帶參
- 獲取不帶參建構式
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getDeclaredConstructor();
Object user = constructor.newInstance();
- 獲取帶參建構式
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor(int.class,String.class);
Object user = constructor.newInstance(1,"張三");
獲取成員變數
成員變數一般為公有和私有
- 獲取公有成員變數
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Field declaredField = classs.getField("userName");
// 賦新值
declaredField.set(user, "李四");
- 獲取私有成員變數
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Field declaredField = classs.getDeclaredField("userName");
// 因為屬性是私有的,所有需要打開可見權限
declaredField.setAccessible(true);
// 賦新值
declaredField.set(user, "李四");
獲取方法
成員方法一般為公有和私有,同時方法也分有參和無參
- 獲取公有成員無參方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getMethod("getUserName");
method.invoke(user);
- 獲取公有成員有參方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getMethod("getUserName", String.class);
method.invoke(user, "王五");
- 獲取私有成員無參方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getDeclaredMethod("getUserName");
// 因為屬性是私有的,所有需要打開可見權限
method.setAccessible(true);
method.invoke(user);
- 獲取私有成員有參方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getDeclaredMethod("getUserName",String.class);
// 因為屬性是私有的,所有需要打開可見權限
method.setAccessible(true);
method.invoke(user, "王五");
Tip
- 一般來說,建構式大部分是 public ,而成員變數和方法大部分是 private,public 權限直接正常呼叫即可、private需要在呼叫前增加
setAccessible(true) - 獲取建構式、成員變數、成員方法都有一個獲取所有的方法,根據需要去呼叫,如:
getConstructor:獲取單個建構式,getConstructors:獲取所有建構式
總結
反射的作用是在運行時動態獲取物件資訊,
實作步驟:
- 獲取
Class:有三種方式,常用的是Class.forName("類全名稱") - 獲取建構式:通過
Class物件呼叫 如:clz.getConstructor()得到建構式 - 創建物件:通過建構式創建物件 如:
con.newInstance()
沒有 Declared 前綴會回傳所有的 public 方法,包括父類的方法,
而加 Declared 前綴會回傳所有自己定義的方法,public,protected,private 都在此,但是不包括父類的方法,
這也正是 getMethod 和 getDeclaredMethod 的區別,
- 獲取私有建構式
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getDeclaredConstructor();
// 因為屬性是私有的,所有需要打開可見權限
constructor.setAccessible(true);
Object user = constructor.newInstance();
- 獲取私有成員變數
...
Field declaredField = classs.getDeclaredField("userName");
// 因為屬性是私有的,所有需要打開可見權限
declaredField.setAccessible(true);
- 獲取私有成員方法
...
Method method = classs.getDeclaredMethod("getUserName");
// 因為屬性是私有的,所有需要打開可見權限
method.setAccessible(true);
反射耗時的原因:
invoke方法需要做封裝和解封操作- 需要型別檢查、權限檢查
- 需要校驗引數
- 需要檢查方法的可見性
JVM無法做優化
參考
- Reflection:Java反射機制的應用場景
- 深入淺出Java反射原理和使用場景
- java反射機制詳解
- Java 反射 -超詳細講解(附原始碼)
- java反射為什么慢
- 強大的反射庫 FreeReflection
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/233137.html
標籤:其他
