反射的概念
- java的放射機制:在程序運行時,程序有能力獲取一個類的全部方法和屬性;而且對於任意一個對象,能夠調用它的任意方法或者獲取其屬性
- 通俗解析:java文件須要編譯成.class文件才能被jvm加載使用,對象的.class數據在jvm裏就是Class<T>;咱們若是能拿到這個Class<T>對象,就能獲取該Class<T>對應的對象類型,及在該類型聲明的方法和屬性值;還能夠根據Class<T>建立相應的類型對象,經過Field,Method反過來操做對象
- java相關類介紹
類名 |
描述 |
Class<T> |
表明類的實體,在運行的Java應用程序中表示類或者接口 |
Field |
類的成員變量(成員變量也稱爲類的屬性) |
Method |
類的方法 |
Constructor<T> |
類的構造方法 |
獲取Class的三種方法
// 根據Example 獲取Class =》Example.class
public Class<Example> getExample(){
Class<Example> clazz = Example.class;
return clazz;
}
public Class<Example> getExampleByInstance(){
Example example = new Example();
// getClass是Object類裏面的方法;《?》 是通配符
Class<?> clazz = example.getClass();
return (Class<Example>)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<Integer> getInteger()throws ClassNotFoundException{
Class<?> clazz = Class.forName("java.lang.Integer");
return (Class<Integer>)clazz;
}
JAVA反射API
//獲取全部的構造方法 / private public
public Constructor<?>[] getDeclaredConstructors()
//獲取特定的構造方法 / private public
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//獲取類的父類
public native Class<? super T> 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)
//得到方法的放回類型
public Class<?> getReturnType()
//得到方法的傳入參數類型
public Class<?>[] getParameterTypes()
//obj是實例對象,args是方法,反過來由Method控制對象的方法調用
public Object invoke(Object obj, Object... args)
//屬性與obj相等則返回true
public boolean equals(Object obj)
//得到obj中對應的屬性值
public Object get(Object obj)
//設置obj中對應屬性值
public void set(Object obj, Object value)
//根據傳遞的參數建立類的對象:initargs 構造方法參數
public T newInstance(Object... initargs)
//方式一 clazz.newInstance()
Class<Example> 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<Example> clazz = Example.class;
//根據指定構造函數參數獲取Constructor
Constructor<Example> constructor = clazz.getConstructor(Integer.class);
Example example = constructor.newInstance(100);
System.out.println(example.value);
}
}
public class Example {
private int value , count;
static public void main(String[] args) throws Exception{
Class<Example> 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<Example> 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<T>對象。能夠由Class或者Constructor實例化對象instance;根據接口定義,能夠獲取Class<T>裏的某一方法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<T> implements InvocationHandler{
private T target;
public T bind(T obj){
target = obj;
return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),this);
}
@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<Face> 至關於一箇中介人
ExampleFactory<Face> factory = new ExampleFactory<>();
//example 是代理對象
Face example = exampleProxy.bind(new Example());
example.hello("思婷");
}
}
-----
log start
思婷 Hello!
log end
歡迎指正文中錯誤
關注公衆號,一塊兒交流
參考文章