一篇瞭解Java反射
反射
首先從執行原理了解為什麼要用反射,當我們執行一段程式碼時,程式碼經過javac編譯得到.class
的位元組碼檔案,再經過類載入器的loadClass()方法建立Class類物件到堆中;當我們例項化一個物件時,該物件會自動匹配到對應堆中的Class類物件,進而呼叫方法,操作屬性等。至此程式結束。
但透過上述方式,我們寫好程式執行後,如果突然需要載入另一個類時,就需要停止執行並要寫一段程式碼去例項化新需求的類,對伺服器來說會造成一定的影響;這時就體現出了反射的優勢,反射機制可以透過修改配置檔案,而無需修改原始碼對類進行重新載入。
反射是動態獲取資訊及動態呼叫物件方法的方式,Java本身是一種靜態語言,而經過反射後讓Java有了一定的動態性,變為一種“準動態語言”。
反射例項理解
透過外部檔案配置,在不修改原始碼的情況下,來控制程式反射例項看不懂的話,後邊自Class類開始會有各方法的分佈介紹
A.java
package Sentiment.refelction; public class A { private String name="refelction"; public int age=10; public void a(){ System.out.println("this is =>A"); } public void b(){ System.out.println("this is =>B"); } }
re.properties(配置檔案)
path=Sentiment.refelction.A method=a
此時如果我們想呼叫a()方法,有如下幾種方法:
傳統方式
A cat = new A(); cat.a();
I/O流根據配置檔案讀取
該方式讀取後由於path型別為String,所以無法正常讀取到我們的類方法
Properties properties = new Properties(); properties.load(new FileInputStream("re.properties")); String path = properties.get("path").toString(); //Sentiment.refelction.A String method = properties.get("method").toString(); // a new path().a(); //報錯
反射
這時就可以透過反射將String型別的path,轉為Class型別的物件進行呼叫
//(1) 獲取Class型別的物件c1 Class c1 = Class.forName(path); //System.out.println(c1); //(2) 透過c1 得到載入的類 Sentiment.refelction.A的物件例項 Object o = c1.newInstance(); System.out.println("o的執行型別是:"+o.getClass()); //(3)透過c1 得到載入的類Sentiment.refelction.A 的method的方法物件"a" Method method1 =c1.getMethod(method); //(4)透過method1 呼叫方法物件來實現呼叫方法 method1.invoke(o);//傳統方法 物件.方法() ,反射 方法.invoke(物件)
此時如果使用者,想要呼叫A.java中的b()方法,就體現出了反射的優勢。
還是先看傳統方法,需要將a改為b,即修改原始碼
A cat = new A(); cat.b();
而反射機制則可以透過修改配置檔案,而無需修改原始碼。在使用者需求較大時,可以完全體現反射優勢
path=Sentiment.refelction.A method=a
反射優缺點
優點
可以動態的建立和使用物件(也是框架底層核心).使用靈活,沒有反射機制,框架技術就失去底層支撐。
缺點
使用反射基本是解釋執行,對執行速度有影響.
執行同一內容耗時對比
Class類
介紹
1.Class也是類,也繼承Object類
2.Class類物件不是new出來的,而是系統建立的 (都是透過Classloader類建立的)
3.每個類的Class類物件,在記憶體中只有一份,因為類只載入一次
public class Class01 { public static void main(String[] args) throws ClassNotFoundException { //每個類的Class類物件,在記憶體中只有一份,因為類只載入一次 Class c1 = Class.forName("Sentiment.refelction.A"); Class c2 = Class.forName("Sentiment.refelction.A"); System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); } }
4.每個類的例項都會記得自己是由哪個Class 例項所生成(如最開始的執行原理圖所示Cat會找到對應的Cat類物件)
5.透過Class物件可以完整地得到一個類的完整結構,透過一系列API
6.Class物件是存放在堆的
7.類的位元組碼二進位制資料,是放在方法區的,有的地方稱為類的後設資料(包括方法程式碼變數名,方法名,訪問許可權等等)
Class類常用方法
Car.java
package Sentiment.refelction; public class Car { public String brand="寶馬"; public int price = 5000000; public String color = "白色"; @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + ", price=" + price + ", color='" + color + '\'' + '}'; } }
Class02.java
package Sentiment.refelction; import java.lang.reflect.Field; public class Class02 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { String path="Sentiment.refelction.Car"; //1. 獲取Car類對應的Class物件 Class c1 = Class.forName(path); //2. 輸出c1 System.out.println(c1); //顯示c1物件是哪個類的Class物件 Sentiment.refelction.Car System.out.println(c1.getClass());//輸出c1執行型別 java.lang.Class //3.獲取包名 System.out.println(c1.getPackage().getName()); //4.得到類的全路徑 System.out.println(c1.getName()); //5. 透過c1建立物件例項 Car car = (Car)c1.newInstance(); System.out.println(car); //輸出car時自動呼叫toString()方法 //6. 透過反射獲取屬性 Field brand = c1.getField("brand"); System.out.println(brand.get(car)); //7.透過反射給屬性賦值 brand.set(car,"賓士"); System.out.println(brand.get(car)); //8.獲取所有屬性及屬性值 Field[] fields = c1.getFields(); for(Field f : fields){ System.out.print(f.getName()+":"); //屬性名 System.out.print(f.get(car)+" "); //屬性值 } } }
獲取Class類物件
1.已知一個類的全類名,且該類在類路徑下,可透過Class類的靜態方法forName()獲取,可能跑出ClassNotFoundException
Class c1=Class.forName("Sentiment.refelction.Car")
多用於配置檔案,讀取類全路徑,載入類
2.若已知具體的類,透過類的class獲取,該方式最為安全可靠,程式效能最高
Class c2=Car.class
多用於引數傳遞,比如透過反射得到對應構造器物件
3.前提:已知某個類的例項,呼叫該例項的getClass()方法獲取Class物件
Car car = new Car(); Class c3 = car.getClass();
多用於透過建立好的物件,獲取Class物件.
4.透過類載入器來獲取Class類物件
//(1)先得到載入器 ClassLoader classLoader = car.getClass().getClassLoader(); //(2)透過類載入器得到Class物件 Class c4 =classLoader.loadClass(path);
5.基本資料(int, char, boolean,float,double,byte,long,short)按如下方式得到Class類物件
Class c5 = 基本資料型別.class
6.基本資料型別對應的包裝類,可以透過type得到Class類物件
Class c6 = 包裝類.type
GetClass.java
package Sentiment.refelction; public class GetClass { public static void main(String[] args) throws ClassNotFoundException { //1.Class.forName) String path="Sentiment.refelction.Car"; //透過讀取配置檔案獲取 Class c1 = Class.forName(path); System.out.println(c1); //2.類名.class,用於引數傳遞 Class c2 = Car.class; System.out.println(c2); //3.物件.getClass(),用於有物件例項 Car car = new Car(); Class c3 = car.getClass(); System.out.println(c3); //4.透過類載入器來獲取Class類物件 //(1)先得到載入器car ClassLoader classLoader = car.getClass().getClassLoader(); //(2)透過類載入器得到Class物件 Class c4 =classLoader.loadClass(path); System.out.println(c4); //5.基本資料(int, char, boolean,float,double,byte,long,short)按如下方式得到Class類物件 Class c5 = int.class; Class<Character> characterClass = char.class; //6.基本資料型別對應的包裝類,可以透過type得到Class類物件 Class<Integer> type = Integer.TYPE; Class<Character> type1 = Character.TYPE; System.out.println(type); }
類載入
分類
靜態載入:編譯時載入相關的類,如果沒有該類就會報錯,依賴性很強
動態載入:執行時載入需要的類,如果執行時不用該類則不回載入,也不會報錯,降低了依賴性
先看看常規的靜態載入方法ClassLoad.java
package Sentiment.refelction; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Scanner; public class ClassLoad { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); String key = scanner.next(); switch (key){ case "1": Dog dog = new Dog(); dog.cry(); break; case "2": System.out.println("ok"); break; default: System.out.println("Do nothing!"); }} }
當編譯時,即使還沒有輸入key=1
,也就是還沒有呼叫new Dog()
就會報錯找不到該類
而當使用動態載入,也就是反射機制時
package Sentiment.refelction; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Scanner; public class ClassLoad { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); String key = scanner.next(); switch (key){ //靜態載入 case "1": //Dog dog = new Dog(); //dog.cry(); //break; //反射動態載入 case "2": Class c1 = Class.forName("Person"); Object o = c1.newInstance(); Method method = c1.getMethod("hi"); method.invoke(o); System.out.println("ok"); break; default: System.out.println("Do nothing!"); }} }
在沒有呼叫Person類和hi方法的情況下,可以直接執行
當輸入3時會呼叫default方法,並不會載入Person類,只有在key=2
需要載入Person類時,才會報錯
類載入時機
- 當建立物件時(new) //靜態載入
- 當子類被載入時,父類也載入 //靜態載入
- 呼叫子類中的靜態成員時****** //靜態載入**
- 透過反射****** //動態載入**
透過反射獲取類的結構資訊
java.lang.Class類API
- getName:獲取全類名
- getSimpleName:獲取簡單類名
- getFields:獲取所有pubulic修飾的屬性,包括本類以及父類的
- getDeclaredFields:獲取本類中所有屬性
- getMethods:獲取所有public修飾的方法,包含本類以及父類的(父類包括Object類)
- getDeclaredMethods:獲取本類中的所有方法
- getConstrctors:獲取所有本類的public修飾的構造器
- getDeclaredConstructors:獲取本類中的所有構造器
- getPackage:以Package形式返回包資訊
- getSuperClass:以Class形式返回父類資訊
- getInterfaces:以Class[]形式返回介面資訊
- getAnnotations:以Annotation[] 形式返回註解資訊
上述方法應用例項:ReflectionUtils.java
package Sentiment.refelction; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionUtils { public static void main(String[] args) throws ClassNotFoundException { new ReflectionUtils().api(); } public void api() throws ClassNotFoundException { Class c1 = Class.forName("Sentiment.refelction.Person");//獲取Class物件 //getName:獲取全類名 System.out.println(c1.getName()); //getSimpleName:獲取簡單類名 System.out.println(c1.getSimpleName()); //getFields:獲取所有pubulic修飾的屬性,包括本類以及父類的 Field[] fields = c1.getFields(); for (Field field : fields) { System.out.println("本類以及父類的屬性="+field.getName()); } //getDeclaredFields:獲取本類中所有屬性 Field[] declaredFields = c1.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本類中所有屬性="+declaredField.getName()); } //getMethods:獲取所有public修飾的方法,包含本類以及父類的(父類包括Object類) Method[] methods = c1.getMethods(); for (Method method : methods) { System.out.println("本類以及父類的public方法="+method.getName()); } //getDeclaredMethods:獲取本類中的所有方法 Method[] declaredMethods = c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本類中的所有方法="+declaredMethod); } //getConstrctors:獲取所有本類的public修飾的構造器 Constructor[] constructors = c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println("本類的public構造器="+constructor); } //getDeclaredConstructors:獲取本類中的所有構造器 Constructor[] declaredConstructors = c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println("本類中的所有構造器= "+declaredConstructor); } //getPackage:以Package形式返回包資訊 System.out.println("包資訊"+c1.getPackage()); //getSuperClass:以Class形式返回父類資訊 System.out.println("父類的class物件= "+c1.getSuperclass()); //getInterfaces:以Class[]形式返回介面資訊 Class[] interfaces = c1.getInterfaces(); for (Class anInterface : interfaces) { System.out.println("介面資訊= "+anInterface); } //getAnnotations:以Annotation[]形式返回註解資訊 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("註解資訊= "+annotation); } } } interface IA{ } interface IB{ } class B{ public String hobby; public void hi(){ } public B() { } } @Deprecated class Person extends B implements IA,IB{ //屬性 public String name; protected int age; String job; private double sal; //方法 public void m1(){ } protected void m2(){ } void m3(){ } private void m4(){ } //構造方法 public Person() { } public Person(String name) { this.name = name; } }
執行結果(各方法間用---
分割)
Sentiment.refelction.Person ----------------------------- Person ----------------------------- 本類以及父類的屬性=name 本類以及父類的屬性=hobby ----------------------------- 本類中所有屬性=name 本類中所有屬性=age 本類中所有屬性=job 本類中所有屬性=sal ----------------------------- 本類以及父類的public方法=m1 本類以及父類的public方法=hi 本類以及父類的public方法=wait 本類以及父類的public方法=wait 本類以及父類的public方法=wait 本類以及父類的public方法=equals 本類以及父類的public方法=toString 本類以及父類的public方法=hashCode 本類以及父類的public方法=getClass 本類以及父類的public方法=notify 本類以及父類的public方法=notifyAll ----------------------------- 本類中的所有方法=protected void Sentiment.refelction.Person.m2() 本類中的所有方法=void Sentiment.refelction.Person.m3() 本類中的所有方法=private void Sentiment.refelction.Person.m4() 本類中的所有方法=public void Sentiment.refelction.Person.m1() ----------------------------- 本類的public構造器=public Sentiment.refelction.Person() 本類的public構造器=public Sentiment.refelction.Person(java.lang.String) ----------------------------- 本類中的所有構造器= public Sentiment.refelction.Person() 本類中的所有構造器= public Sentiment.refelction.Person(java.lang.String) ----------------------------- 包資訊package Sentiment.refelction ----------------------------- 父類的class物件= class Sentiment.refelction.B ----------------------------- 介面資訊= interface Sentiment.refelction.IA 介面資訊= interface Sentiment.refelction.IB ----------------------------- 註解資訊= @java.lang.Deprecated() -----------------------------
java.lang.feflect.Field類API
- getModifiers:以int形式返回修飾符(預設修飾符—>0,public—>1,priva—>2,protected—>4,static—>8,final—>16。若為混合型別則相加計算 例:public+static = 1+8 = 9)
- getType:以Class形式返回型別
- getName:返回屬性名
java.lang.reflect.Method類API
- getModifiers:以int形式返回修飾符
- getReturnType:以Class形式獲取返回型別
- getName:返回方法名
- getParameterTypes:以Class[]返回引數型別陣列(即形參型別)
java.lang.reflect.Constructor類API
- getModifiers:以int形式返回修飾符
- getName:返回方法名
- getParameterTypes:以Class[]返回引數型別陣列
上述三類API例項程式碼:ReflectionUtils02.java
package Sentiment.refelction; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionUtils02 { public static void main(String[] args) throws ClassNotFoundException { new ReflectionUtils02().api2(); } public void api2() throws ClassNotFoundException { Class c1 = Class.forName("Sentiment.refelction.Student");//獲取Class物件 System.out.println("------------------------------"); System.out.println("java.lang.feflect.Field類API"); System.out.println("------------------------------"); //getDeclaredFields:獲取本類中所有屬性 Field[] declaredFields = c1.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本類中所有屬性="+declaredField.getName()+" 該類的屬性值="+declaredField.getModifiers()+" 型別是="+declaredField.getType()); } System.out.println("------------------------------"); System.out.println("java.lang.reflect.Method類API"); System.out.println("------------------------------"); //getDeclaredMethods:獲取本類中的所有方法 Method[] declaredMethods = c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本類中的所有方法="+declaredMethod.getName()+" 該方法的屬性值="+declaredMethod.getModifiers()+" 該方法的返回型別="+declaredMethod.getReturnType()); //方法的形參型別 Class[] parameterTypes = declaredMethod.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println("形參型別= "+parameterType); } } System.out.println("------------------------------"); System.out.println("java.lang.reflect.Constructor類API"); System.out.println("------------------------------"); //getDeclaredConstructors:獲取本類中的所有構造器 Constructor[] declaredConstructors = c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println("本類中的所有構造器= "+declaredConstructor.getName()+" 該構造器的屬性值= "+declaredConstructor.getModifiers()); Class[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println("形參型別= "+parameterType); } } }} class Student { //屬性 int age; public String name; private String grade; protected static double score; //方法 public void m1(int age ,String name,double score){ } protected String m2(){ return null; } void m3(){ } private void m4(){ } //構造方法 Student() { } public Student(int age) { this.age = age; } private Student(int age, String name, String grade) { this.age = age; this.name = name; this.grade = grade; } }
執行結果(以---
做各類分割)
反射爆破
反射建立物件
- 透過public的無參構造器建立例項
Object o = c3.newInstance();
- 透過pulic的有參構造建立例項
Constructor constructor = c3.getConstructor(String.class); //先得到構造器 Object tana = constructor.newInstance("tana"); //建立例項,傳入實參
- 透過非public的有參構造器建立物件
setAccessible
爆破Constructor declaredConstructor = c3.getDeclaredConstructor(int.class,String.class); //先得到構造器 declaredConstructor.setAccessible(true); //爆破訪問private構造器/方法/屬性 Object mumu = declaredConstructor.newInstance(100, "mumu"); //建立例項,傳入實參
例項程式碼:ReflectionCreate.java
package Sentiment.refelction; import java.lang.reflect.Constructor; public class ReflectionCreate { public static void main(String[] args) throws Exception { //獲取類的Class物件 Class<?> c3 = Class.forName("Sentiment.refelction.User"); //透過public的無參構造器建立例項 Object o = c3.newInstance(); System.out.println(o); //透過pulic的有參構造建立例項 Constructor constructor = c3.getConstructor(String.class); Object tana = constructor.newInstance("tana"); System.out.println("tana = "+tana); //透過非public的有參構造器建立物件 Constructor<?> declaredConstructor = c3.getDeclaredConstructor(int.class,String.class); declaredConstructor.setAccessible(true); Object mumu = declaredConstructor.newInstance(100, "mumu");; System.out.println("mumu = "+mumu); } } class User{ //屬性 private int age=10; private String name="Sentiment"; //構造方法 public User() { } public User(String name) { this.name = name; } private User(int age, String name) { this.age = age; this.name = name; } public String toString() { return "User{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
執行結果
User{age=10, name='Sentiment'} tana = User{age=10, name='tana'} mumu = User{age=100, name='mumu'}
反射訪問類成員
獲取public屬性
Field age = c4.getField("age"); //age為屬性名 age.set(o,100); //修改屬性值;o為物件例項 System.out.println(age.get(o));
- 獲取屬性:Class類物件.getField("屬性值");
- 修改屬性值:屬性.set(o,值);
- 輸出屬性值:屬性.get(o);
獲取private屬性
Field name = c4.getDeclaredField("name"); //name為屬性名 name.setAccessible(true); //爆破 name.set(o,"tana"); //修改屬性值,o為物件例項 System.out.println(name.get(null)); //static修飾的物件,也可以用null代替
- 獲取屬性:Class類物件.getDeclaredField("屬性值");****
- 爆破:屬性.setAccessible(true);
例項程式碼:ReflectionCreate02.java
package Sentiment.refelction; import java.lang.reflect.Field; public class ReflectionCreate02 { public static void main(String[] args) throws Exception{ Class c4 = Class.forName("Sentiment.refelction.human"); Object o = c4.newInstance(); //獲取public屬性 Field age = c4.getField("age"); age.set(o,100); //修改屬性值 System.out.println(age.get(o)); //獲取private屬性 Field name = c4.getDeclaredField("name"); name.setAccessible(true); name.set(o,"tana"); //修改屬性值 System.out.println(name.get(null)); } } class human{ //屬性 public int age=10; private static String name ="Sentiment"; //構造器 public human() { } @Override public String toString() { return "human{" + "age=" + age +", name"+name+'}'; } }
執行結果
100 tana
反射呼叫方法
呼叫public方法
Method say1 = c5.getMethod("say1", String.class); //say1為String型別的方法 say1.invoke(o,"Sentiment");
- 獲取方法:Class類物件.getMethod("方法名", 方法型別.class);;
- 呼叫方法:物件.invoke(例項化物件,"實參值");
呼叫private方法
Method say2 = c5.getDeclaredMethod("say2", int.class, String.class, char.class); //say2方法及其型別 say2.setAccessible(true); System.out.println(say2.invoke(o,10,"tana",'M'));
- 獲取方法:Class類物件.getDeclaredMethod("方法名", 方法型別.class);;
- 呼叫方法:物件.invoke(例項化物件,"實參值");
- 爆破:method.setAccessible(true);
例項程式碼:ReflectionCreate03.java
package Sentiment.refelction; import java.lang.reflect.Method; public class ReflectionCreate03 { public static void main(String[] args) throws Exception{ Class c5 = Class.forName("Sentiment.refelction.man"); Object o = c5.newInstance(); //呼叫public方法 Method say1 = c5.getMethod("say1", String.class); say1.invoke(o,"Sentiment"); //呼叫private方法 Method say2 = c5.getDeclaredMethod("say2", int.class, String.class, char.class); say2.setAccessible(true); System.out.println(say2.invoke(o,10,"tana",'M')); System.out.println(say2.invoke(null,20,"mumu",'M')); //靜態方法可以用null代替例項物件 } } class man{ //屬性 public int age; private static String name; //構造方法 public man() { } //方法 public void say1(String a){ System.out.println("this is =>"+a); } private static String say2(int a,String b, char c){ return a+" "+b+" "+c; } }
反射練習
Work1
- 定義個Test類,其中定義私有屬性name,賦值為"xxxxxx"
- Test類中建立公有方法getName()內容為return name
- 建立Test的Class類,並獲得私有屬性name,修改私有屬性name的值,並呼叫getName()方法輸出name的值
參考
Work1.java
package Sentiment.refelction; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Work1 { public static void main(String[] args) throws Exception{ Class c1 = Class.forName("Sentiment.refelction.Test"); Object o = c1.newInstance(); Field name = c1.getDeclaredField("name"); name.setAccessible(true); name.set(o,"tana"); Method getName = c1.getMethod("getName"); System.out.println(getName.invoke(o)); } } class Test{ private String name = "Sentiment"; public String getName(){ return name; } }
Work2
- 獲取java.io.File的class物件,並輸出該類的所有構造器方法
- 透過newInstance建立File物件,在自己電腦中建立個
a.txt
參考
Work2.java
package Sentiment.refelction; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class Work2 { public static void main(String[] args) throws Exception{ Class c2 = Class.forName("java.io.File"); Constructor[] c = c2.getDeclaredConstructors(); for (Constructor Constructor : c) { System.out.println(Constructor); } //獲取構造器public java.io.File(java.lang.String) Constructor declaredConstructor = c2.getDeclaredConstructor(String.class); String path="D:\\a.txt"; //例項化類 Object path1 = declaredConstructor.newInstance(path); //獲取createNewFile方法 Method createNewFile = c2.getMethod("createNewFile"); createNewFile.invoke(path1); System.out.println(path1.getClass()); System.out.println("建立檔案成功:"+path); } }
相關文章
- 你瞭解Java反射嗎?2019-09-18Java反射
- Java反射—方法的反射、深入瞭解泛型2017-06-28Java反射泛型
- Java反射詳解篇--一篇入魂2022-03-25Java反射
- Java中反射的概述及瞭解ClassLoader2020-12-14Java反射
- 一文帶你瞭解Java反射機制2019-07-29Java反射
- java反射詳解2020-07-29Java反射
- java反射全解2019-08-03Java反射
- Java 反射詳解2018-08-10Java反射
- Java反射-註解2019-01-19Java反射
- java註解,反射2017-04-01Java反射
- java反射簡解2016-08-06Java反射
- 瞭解Java中的鎖,看這一篇就夠了!2020-04-04Java
- Java安全第一篇 | 反射看這一篇就夠了2022-03-21Java反射
- Java反射與註解2018-08-08Java反射
- Java 註解和反射2020-11-20Java反射
- java反射詳解(轉)2017-12-01Java反射
- java反射案例講解2016-08-07Java反射
- Java註解和反射2024-05-01Java反射
- 一篇文章讓你徹底瞭解Java內部類2018-08-23Java
- 一篇文章瞭解大前端2019-02-24前端
- 你必須瞭解的反射——反射來實現實體驗證2019-01-04反射
- Java之註解與反射2021-07-21Java反射
- Java 反射機制詳解2016-01-24Java反射
- 快速瞭解Java多執行緒,有這一篇就夠了2020-10-29Java執行緒
- 一篇帶你瞭解TCP/IP 概念2021-07-25TCP
- 一篇文章瞭解JsBridge2018-02-27JS
- 一篇部落格讓你瞭解RxJava2017-07-20RxJava
- java反射——反射Annotation2016-09-14Java反射
- Java程式設計:一步步教你如何深入瞭解神秘的Java反射機制2018-08-09Java程式設計反射
- Java註解與反射的使用2020-12-27Java反射
- 【Java基礎】反射和註解2016-07-22Java反射
- 【譯】8. Java反射——註解2013-09-12Java反射
- Java註解與反射機制2024-06-25Java反射
- Java反射和註解基本用法2024-03-13Java反射
- 瞭解 MongoDB 看這一篇就夠了2020-01-08MongoDB
- 一篇文章瞭解Redis資料庫2019-09-25Redis資料庫
- 瞭解HandlerThread這一篇就夠了2019-02-26thread
- python爬蟲瞭解第一篇2019-02-16Python爬蟲