反射的概念
java 的放射機制:在程序運行時,程序有能力獲取一個類的所有方法和屬性;并且對于任意一個對象,可以調用它的任意方法或者獲取其屬性
通俗解析:java 文件需要編譯成. class 文件才能被 jvm 加載使用, 對象的. class 數據在 jvm 里就是 Class;我們如果能拿到這個 Class對象,就能獲取該 Class對應的對象類型,及在該類型聲明的方法和屬性值;還可以根據 Class創建相應的類型對象,通過 Field,Method 反過來操作對象
java 相關類介紹
類名 | 描述 |
---|---|
Class | 代表類的實體,在運行的 Java 應用程序中表示類或者接口 |
Field | 類的成員變量(成員變量也稱為類的屬性) |
Method | 類的方法 |
Constructor | 類的構造方法 |
獲取 Class 的三種方法
- 1 通過已知的類型獲取 class
//?根據Example?獲取Class?=》Example.class
public?Class?getExample(){
????Class?clazz?=?Example.class;return?clazz;
}
- 2 通過實例對象獲取 class
public?Class?getExampleByInstance(){
????Example?example?=?new?Example();
????// getClass是Object類里面的方法;《?》?是通配符
????Class?clazz?=?example.getClass();return?(Class)clazz;
}
- 3 通過 Class.forName 獲取全路徑指定類名的 class
/\*\*?forName0?本地方法,C++實現,jvm調用
?\* 1 className 是個類名? 2 initialize 是否延遲加載? 3 loader 加載器
?\*/
private?static?native?Class?forName0(String?className,?boolean?initialize,
????????ClassLoader?loader,?Class?caller)?throws?ClassNotFoundException;
public?static?Class?forName(String?className)?throws?ClassNotFoundException?{
????????Class?caller?=?Reflection.getCallerClass();
????????return?forName0(className,?true,?ClassLoader.getClassLoader(caller),?caller);
????}
//?兩個forName方法最終都會調用forName0方法去加載class???
public?static?Class?forName(String?name,
????????boolean?initialize,?ClassLoader?loader)?throws?ClassNotFoundException?{
????????....
????????return?forName0(name,?initialize,?loader,?caller);
????}
//?示例:通過java.lang.Integer?
public?Class?getInteger()throws?ClassNotFoundException{
????Class?clazz?=?Class.forName("java.lang.Integer");return?(Class)clazz;
}
JAVA 反射 API
- Class 常用操作方法
//獲取所有的構造方法?/?private?public
public?Constructor\[\]?getDeclaredConstructors()
//獲取特定的構造方法?/?private?public
public?Constructor?getDeclaredConstructor(Class...?parameterTypes)????
//獲取類的父類
public?native?Class?getSuperclass()????
//獲取類實現的接口
private?Class\[\]?getInterfaces(boolean?cloneArray)??
//獲取在類內定義的內部類或接口
public?Class\[\]?getDeclaredClasses()
//獲取所有的方法
public?Method\[\]?getDeclaredMethods()?throws?SecurityException
//根據方法名和參數獲得特定的方法
public?Method?getDeclaredMethod(String?name,?Class...?parameterTypes)??
//獲取類型的定義的所有屬性
public?Field\[\]?getFields()?throws?SecurityException
//?根據屬性命名獲得特定的Field
public?Field?getField(String?name)?
- Method 常用的操作方法
//獲得方法的放回類型
public?Class?getReturnType()???
//獲得方法的傳入參數類型
public?Class\[\]?getParameterTypes()???
//obj是實例對象,args是方法,反過來由Method控制對象的方法調用
public?Object?invoke(Object?obj,?Object...?args)
- Field 常用的操作方法
//屬性與obj相等則返回true
public?boolean?equals(Object?obj)
//獲得obj中對應的屬性值
public?Object?get(Object?obj)
//設置obj中對應屬性值
public?void?set(Object?obj,?Object?value)?
- Constructor
//根據傳遞的參數創建類的對象:initargs?構造方法參數
public?T?newInstance(Object...?initargs)?
- 1 根據 class 創建對象
//方式一?clazz.newInstance()
Class?clazz?=?Example.class;
Example?example?=?clazz.newInstance();
//方式二?先獲取再由Constructor:clazz.getConstructors()/getConstructor(...)?
//再由Constructor.newInstance?方法構造對象
-----------------------------------------
public?class?Example?{
????private?int?value;
????public?Example(){?}?//?如果只聲明有參構造函數,clazz.newInstance()會報錯
????public?Example(Integer?value){??this.value??=?value;??}
????static?public?void?main(String\[\]?args)?throws?Exception{
????????Class?clazz?=?Example.class;
????????//根據指定構造函數參數獲取Constructor
????????Constructor?constructor?=?clazz.getConstructor(Integer.class);
????????Example?example?=?constructor.newInstance(100);
????????System.out.println(example.value);
????}
}????
- 2 由 class 獲取 Field,并操作實例的屬性
public?class?Example?{
????private?int?value?,?count;
????static?public?void?main(String\[\]?args)?throws?Exception{
????????Class?clazz?=?Example.class;
????????//獲取所有的屬性,getField只能獲取public的屬性
????????Field\[\]?fs?=?clazz.getDeclaredFields();
????????//根據名稱獲取指定?Field
????????Field?value?=?clazz.getDeclaredField("value");
????????Example?example?=?clazz.newInstance();
????????//使用反射機制可以打破封裝性,導致了java對象的屬性不安全??
????????value.setAccessible(true);?//setAccessible(true)讓private的參數可賦值操作
????????//由Field反過去設置example的值
????????value.set(example,100);
????????System.out.println(example.value);
????}
}
- 3 由 class 獲取 Method,并反射調用實例方法
public?class?Example?{
????public?static?void?main(String\[\]?args)?throws?Exception?{
????????Class?clazz?=?Example.class;
????????Example?example?=?clazz.newInstance();
????????Method\[\]?methods?=?clazz.getDeclaredMethods();
????????//getDeclaredMethod和getMethod是:getMethod只能返回public的方法
????????Method?method?=?clazz.getDeclaredMethod("hello",?String.class);
????????method.setAccessible(true);
????????method.invoke(example,?"cscw");
????}
????private?void?hello(String?name)?{?System.out.println(name?+?"?Hello!");?}
}
-----
cscw?Hello!
反射機制應用的場景
1 動態拓展:假設有同一組類是實現相同的接口,并且類的加載方式不限制。當我們需要那種具體類實現的功能時,只需加載. class 文件,并獲取對應的 Class對象。可以由 Class 或者 Constructor 實例化對象 instance;根據接口定義,可以獲取 Class里的某一方法 Method,并配合 instance 反射調用功能方法
2 Spring 的 IOC 就是基于反射機制實現
3 JDK 的動態代理
反射和 JDK 動態代理
- 在 Java 的 java.lang.reflect 包下提供了一個 Proxy 類和一個 InvocationHandler 接口。通過這個類和接口可以生成 JDK 動態代理類或動態代理對象
public?interface?InvocationHandler?{
?//所有方法都會調用此代理方法
????Object?invoke(Object?var1,?Method?var2,?Object\[\]?var3)?throws?Throwable;
}??
public?class?Proxy?implements?Serializable{
?...
????//根據interfaces和InvocationHandler生成代理對象
????public?static?Object?newProxyInstance(ClassLoader?loader,
??????Class\[\]?interfaces,?InvocationHandler?h)?
????...????
}
JDK 的動態代理由 Proxy 和 InvocationHandler 實現;而被代理對象必須實現一個接口。代理對象由 Proxy 生成,可轉為接口 interface 的實現類對象 OBJ。當調用 OBJ 的方法時,則會觸發 InvocationHandler.invoke,參數依次為「代理對象」,「Method 對象」, 和「方法 Method 所需的參數」。在 invoke 方法可以加入拓展的邏輯,如日志記錄操作;「并可以在 invoke 里利用反射的技術調用被代理對象方法」
示例
public?class?ExampleFactory?implements?InvocationHandler{
????private?T?target;?//被代理對象
????public?T?bind(T?obj){
????????target?=?obj;return?(T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
???????????obj.getClass().getInterfaces(),this);
????}
????/\*\*?Object?o?是代理對象;?o的方法調用?->?ExampleFactory.invoke?
????\*??invoke(...)?->?在invoke方法里面?反射調用代理對象方法+增強邏輯
????\*/
????@Override
????public?Object?invoke(Object?o,?Method?method,?Object\[\]?objects)?throws?Throwable?{
????????//增強邏輯
????????System.out.println("log?start");
????????//反射調用被代理對象方法
????????Object?result?=?method.invoke(target,objects);
????????System.out.println("log?end");return?result;
????}
}
-----------
public?interface?Face?{
????void?hello(String?name);
}
---------
//被代理對象必須實現一個接口,并由接口方法對方提供功能
public?class?Example?implements?Face?{
?public?void?hello(String?name)?{
????????System.out.println(name?+?"?Hello!");
????}
????public?static?void?main(String\[\]?args)??{
???????//ExampleFactory?相當于一個中介人
????????ExampleFactory?factory?=?new?ExampleFactory<>();
????????//example?是代理對象
????????Face?example?=?exampleProxy.bind(new?Example());
????????example.hello("思婷");
????}
}
-----log?start
思婷?Hello!log?end