Java反射機(jī)制
在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語(yǔ)言的反射機(jī)制。
反射的本質(zhì)就是:在運(yùn)行時(shí),把 Java 類中的各種成分映射成一個(gè)個(gè)的 Java 對(duì)象。
反射機(jī)制很重要的一點(diǎn)就是“運(yùn)行時(shí)”,其使得我們可以在程序運(yùn)行時(shí)加載、探索以及使用編譯期間完全未知的 .class 文件。換句話說(shuō),Java 程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的 .class 文件,然后獲悉其完整構(gòu)造,并生成其對(duì)象實(shí)體、或?qū)ζ?fields(變量)設(shè)值、或調(diào)用其 methods(方法)。
Class類將一個(gè)類的組成封裝成各個(gè)屬性,并實(shí)現(xiàn)了各個(gè)getXxx()方法。
public static void getInfo(Class cls) throws NoSuchMethodException {
//構(gòu)造方法
cls.getConstructor();
//獲取某個(gè)方法
cls.getMethod("");
//包含的方法
cls.getMethods();
//獲取某個(gè)屬性
cls.getField("");
//包含的屬性
cls.getFields();
//實(shí)現(xiàn)的接口
cls.getInterfaces();
//包含的Annotation
cls.getAnnotations();
//內(nèi)部類
cls.getDeclaredClasses();
//外部類
cls.getDeclaringClass();
//獲取類名
cls.getName();
//獲取包名
cls.getPackage();
//獲取修飾符
cls.getModifiers();
}
創(chuàng)建使用類
public class FatherClass {
protected String mFatherName;
protected int mFatherAge;
public FatherClass() {
}
}
public class SonClass extends FatherClass {
private String mSonName;
protected int mSonAge;
public String mSonBirthday;
@GET("https:\\www.baidu.com")
private <T,K> String toStr(T t, K k) {
return t.toString() + k.toString();
}
public String getmSonName() {
return mSonName;
}
public void setmSonName(String mSonName) {
this.mSonName = mSonName;
}
}
各種操作實(shí)現(xiàn):
-
通過(guò)反射獲取類的三種方式
/**
* 通過(guò)反射獲取類的三種方式
* @throws ClassNotFoundException
*/
private void getClassWay() throws ClassNotFoundException {
Class cls = null;
cls = FatherClass.class;
cls = new FatherClass().getClass();
cls = Class.forName("com.example.genericannotaionreflect.reflactdemo.FatherClass");
}
-
反射創(chuàng)建實(shí)例
public static void getConstructor() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<?> cls = StringBuilder.class;
StringBuilder sb = (StringBuilder) cls.newInstance();
sb.append("hello");
System.out.println(sb.toString());
Class<?> cls2 = String.class;
//獲取String類帶一個(gè)String參數(shù)的構(gòu)造器
Constructor constructor = cls2.getConstructor(String.class);
//根據(jù)構(gòu)造器constructor創(chuàng)建實(shí)例
String str = (String) constructor.newInstance("hello");
System.out.println(str);
}
獲取類的所有變量信息
/**
* 通過(guò)反射獲取類的所有變量
*/
public static void printFileds(Class cls) {
System.out.println("類名:" + cls.getName());
// 獲取所有 public 訪問(wèn)權(quán)限的變量
// 包括本類聲明的和從父類繼承的
Field[] fields = cls.getFields();
// 獲取所有本類聲明的變量,包含各種訪問(wèn)權(quán)限
Field[] declaredFields = cls.getDeclaredFields();
for (Field field: declaredFields) {
//獲取訪問(wèn)權(quán)限并輸出
field.setAccessible(true);
int modifier = field.getModifiers();
System.out.println("屬性訪問(wèn)權(quán)限是否是 PROTECTED: " + Modifier.isProtected(modifier));
//輸出變量的類型及變量名
System.out.println("屬性類型:" + field.getType().getName() + "屬性名:" + field.getName());
}
}
//調(diào)用
ClassReflact.printFileds(FatherClass::class.java)
打?。?/p>
類名:com.example.genericannotaionreflect.reflactdemo.FatherClass
屬性訪問(wèn)權(quán)限是否是 PROTECTED: true
屬性類型:int屬性名:mFatherAge
屬性訪問(wèn)權(quán)限是否是 PROTECTED: true
屬性類型:java.lang.String屬性名:mFatherName
getFields() :獲取所有 public 訪問(wèn)權(quán)限的變量,非public的獲取不到
getDeclaredFields():獲取所有本類聲明的變量,包含各種訪問(wèn)權(quán)限
-
獲取類的所有方法的所有元素
@RequiresApi(api = Build.VERSION_CODES.P)
public static void printMethods(Class cls) {
System.out.println("類名:" + cls.getName());
// 獲取所有 public 訪問(wèn)權(quán)限的方法
// 包括本類聲明的和從父類繼承的
Method[] methods = cls.getMethods();
// 獲取所有本類聲明的方法,包含各種訪問(wèn)權(quán)限
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method method : declaredMethods) {
method.setAccessible(true);
System.out.println("方法名: " + method.getName());
System.out.println("屬性訪問(wèn)權(quán)限是否是 public: " + Modifier.isProtected(method.getModifiers()));
//獲取方法返回值類型
Class<?> returnType = method.getReturnType();
System.out.println( "返回類型: " + returnType.getName());
//獲取方法所有參數(shù)
Parameter[] parameters = method.getParameters();
for (Parameter parameter: parameters) {
System.out.println("擁有參數(shù):" + parameter.getName() + "--" + parameter.getType().getName());
}
//獲取方法拋出的異常
Class<?>[] exceptionTypes = method.getExceptionTypes();
//獲取方法注解
Annotation[] annotations = method.getAnnotations();
//方法是否有某個(gè)注解
if (method.isAnnotationPresent(GET.class)) {
GET annotation = method.getAnnotation(GET.class);
System.out.println(annotation.value());
}
//獲取方法參數(shù)類型的泛型參數(shù)
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type type: genericParameterTypes) {
System.out.println(type.getTypeName());
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value();
}
//調(diào)用
ClassReflact.printMethods(SonClass::class.java)
打?。?/p>
類名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
屬性訪問(wèn)權(quán)限是否是 public: false
返回類型: java.lang.String
擁有參數(shù):arg0--java.lang.Object
擁有參數(shù):arg1--java.lang.Object
https:\www.baidu.com
T
K
類名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
屬性訪問(wèn)權(quán)限是否是 public: false
返回類型: java.lang.String
擁有參數(shù):arg0--java.lang.Object
擁有參數(shù):arg1--java.lang.Object
https:\www.baidu.com
T
K
方法名: getmSonName
屬性訪問(wèn)權(quán)限是否是 public: false
返回類型: java.lang.String
方法名: setmSonName
屬性訪問(wèn)權(quán)限是否是 public: false
返回類型: void
擁有參數(shù):arg0--java.lang.String
java.lang.String
-
反射執(zhí)行某個(gè)對(duì)象的私有方法
public static void invokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class cls = SonClass.class;
SonClass sonClass = (SonClass) cls.newInstance();
Method method = cls.getDeclaredMethod("setmSonName", String.class); //獲取setmSonName方法
method.setAccessible(true); //可以訪問(wèn)對(duì)象的私有方法
method.invoke(sonClass, "lala"); //使用 invoke 反射調(diào)用私有方法,傳需要操作的對(duì)象和方法的各個(gè)參數(shù)
System.out.println(sonClass.getmSonName());
}
-
反射操作對(duì)象屬性值
public static void modifyField() throws NoSuchFieldException, IllegalAccessException {
Class cls = SonClass.class;
Field field = cls.getDeclaredField("mSonAge");
field.setAccessible(true);
SonClass sonClass = new SonClass();
field.set(sonClass, 18);
System.out.println("age: " + sonClass.mSonAge);
}
java 反射為什么會(huì)耗性能
1.反射調(diào)用過(guò)程中會(huì)產(chǎn)生大量的臨時(shí)對(duì)象,這些對(duì)象會(huì)占用內(nèi)存,可能會(huì)導(dǎo)致頻繁 gc,從而影響性能。
2.反射調(diào)用方法時(shí)會(huì)從方法數(shù)組中遍歷查找,并且會(huì)檢查可見性等操作會(huì)耗時(shí)。
3.反射在達(dá)到一定次數(shù)時(shí),會(huì)動(dòng)態(tài)編寫字節(jié)碼并加載到內(nèi)存中,這個(gè)字節(jié)碼沒(méi)有經(jīng)過(guò)編譯器優(yōu)化,也不能享受JIT優(yōu)化。
4.反射一般會(huì)涉及自動(dòng)裝箱/拆箱和類型轉(zhuǎn)換,都會(huì)帶來(lái)一定的資源開銷。
參考:
https://juejin.cn/post/6844904098207105038
參考:https://juejin.cn/post/6844904005294882830