反射可讓咱們在運行時動態的加載類,訪問、調用類的字段、方法、構造器。反射機制是構建框架的基礎,如Spring框架核心,各類RPC框架等。java
首先咱們須要知道在java中,類的信息是由Class對象來描述的,每一個類對應一個Class對象,它表示一個類或者接口(一個annotation也算是一個接口)在運行時的類型信息。Class只有一個private的構造器,你並不能手動的去建立它,Class對象只能由JVM在加載一個類或者是在調用一個方法的時候去建立它。 獲取一個類的Class對象有如下幾種方式:安全
- Class.forName(String className)
- obj.getClass()
- ClassName.class
相較於forName()的方式,.class方式能得到編譯期的檢查更加安全。以上三種方式均可以得到類的Class對象,可是他們之間也有一些差異,Class.forName(String className)默認會對這個類進行初始化操做,而.class的方式則不會自動初始化該Class對象。網絡
import java.util.Random; public class InitObj1 { public static final int value1 = 1;//編譯期常量,不須要初始化 public static final int value2 = new Random().nextInt();//須要初始化 static{ System.out.println("static block initializing."); } }
public class InitObj2 { public static final int value1 = 1; static{ System.out.println("static block initializing."); } }
public class ClassInitTest { public static void main(String[] args) throws ClassNotFoundException { System.out.println("----經過.class獲取Class對象-----"); Class c1 = InitObj1.class; System.out.println("created c1"); System.out.println("value1:" + InitObj1.value1); System.out.println("value2:" + InitObj1.value2); System.out.println("----經過forName獲取Class對象-----"); Class c2 = Class.forName("me.l4j.thinkingInJava.reflectionAPI.InitObj2"); System.out.println("created c2"); System.out.println("value1:" + InitObj2.value1); } }
輸出:
----經過.class獲取Class對象-----
created c1
value1:1
static block initializing.
value2:-2130950913
----經過forName獲取Class對象-----
static block initializing.
created c2
value1:1框架
從結果能夠看到.class獲取Class引用不會引起初始化,延遲到了對value2的訪問,可是forName()方法會當即引起初始化。dom
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; /** * Created by lsj on 2017-9-6. */ public class ReflectString { public static void main(String[] args) { Class<?> strC = String.class; //獲取類名 System.out.println(strC.getName()); //獲取實現的接口 Class<?>[] inter = strC.getInterfaces(); System.out.println("String implements interfaces:"); for (Class c : inter) System.out.println(" --" + c.getName()); //getXXX方法只能獲取到public,getDeclaredXXX能夠獲取到全部定義了的元素 //獲取構造器 Constructor<?>[] cons = strC.getDeclaredConstructors(); System.out.println("All Constructors:"); for (Constructor csr : cons) { System.out.println(" constructor name:"+csr.getName()); System.out.println(" constructor modifier:" + Modifier.toString(csr.getModifiers())); Arrays.stream(csr.getParameterTypes()).forEach(x-> System.out.println(" param:"+x.getName())); System.out.println(" ------------------------------------"); } //獲取字段 Field[] fields = strC.getDeclaredFields(); System.out.println("All fields:"); for (Field f : fields) { System.out.println(" name:" + f.getName()); System.out.println(" modifier:"+Modifier.toString(f.getModifiers())); System.out.println(" ------------------------------------"); } //獲取方法 Method[] methods = strC.getDeclaredMethods(); System.out.println("All methods:"); for (Method m : methods){ System.out.println(" name:"+m.getName()); System.out.println(" modifier:"+Modifier.toString(m.getModifiers())); System.out.println(" returnType:"+m.getReturnType().getName()); Arrays.stream(m.getParameterTypes()).forEach(x-> System.out.println(" param:"+x.getName())); Arrays.stream(m.getAnnotations()).forEach(x-> System.out.println(" annotation:"+x.annotationType().getName())); System.out.println(" ------------------------------------"); } // 建立一個String對象cs,向上轉型爲CharSequence,其類型信息任然是一個String CharSequence cs = new String("str"); Class csClass = cs.getClass(); System.out.println(csClass.getName()); } }
對類型的判斷主要有istanceof關鍵字、Class.isInstance(Object obj)方法和"=="三種方式。ide
import java.io.Serializable; public class ClassType { public static void main(String[] args) { //String和StringBuilder都實現了CharSequence接口和Serializable接口,將str和sb都向上轉型爲一個CharSequence CharSequence str = new String(); CharSequence sb = new StringBuilder(); //istanceof和isInstance()表示這個對象是不是這個類或者接口的實例,即這個對象是不是這個類型 System.out.println("str instanceof CharSequence:" + (str instanceof CharSequence)); System.out.println("str instanceof String:" + (str instanceof String)); System.out.println("str instanceof Serializable:" + (str instanceof Serializable)); System.out.println(); System.out.println("CharSequence.class.isInstance(str): "+CharSequence.class.isInstance(str)); System.out.println("String.class.isInstance(str): "+String.class.isInstance(str)); System.out.println(); //==只能與對象實際類型相同時才能返回true System.out.println("String.class == str.getClass(): " + (String.class == str.getClass())); System.out.println("CharSequence.class == str.getClass(): " + (CharSequence.class == str.getClass())); //雖然str和sb都向上轉型爲CharSequence,可是他們兩的實際類型並不相同 System.out.println("str.getClass(): "+str.getClass()); System.out.println("sb.getClass(): "+sb.getClass()); } }
instanceof
和isInstance()
保持了類型的概念(是不是這個類,或是不是這個類的派生類),而==比較的是實際的class對象。工具
由於知道一隻狗必定是一隻動物,因此編譯器容許自由的向上轉型,不須要任何顯示的轉換。可是一直動物是否是一隻狗這是不肯定的,在進行「向下轉型」的時候須要顯式的進行轉換(Dog)object
,若是這個object並不屬於Dog,那麼在運行時會觸發ClassCastException
異常。在進行「向下轉型」時咱們能夠先使用instanceof
或者isInstance()
進行類型檢查,以免觸發異常。 在JAVA SE5開始咱們可使用Class引用的cast()
進行轉型,暫沒有發現這種轉型語法的特異功能。ui
Animal a = new Dog(); Class<?> dogCls = Dog.class; Dog d = dogCls.cast(a);
經過java反射機制,咱們能夠動態的建立對象,體現出很大的靈活性。相較於靜態編譯,經過反射執行的操做要慢得多。 java在java.lang.reflect包下提供了反射的工具和接口,reflect包只是java反射機制的一部分,其餘還包括前面提到的Class類以及Object類等。this
在reflect包中Field、Method和Constructor這三個類比較經常使用。咱們能夠經過Constrcutor建立新的對象(Class引用的newInstance
方法也能夠建立對象,可是須要這個類有無參構造器),利用Field中的set()
和get()
方法來讀取和修改字段,以及使用invoke()
方法來調用與Method關聯的方法。下面的實例展現了這三個類的基本用法:操作系統
import java.util.List; public class Student { private String name; private int age; private List<String> courses; public Student(String name, int age) { this.name = name; this.age = age; } public Student(){ this.name = "John Doe"; this.age = -1; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getCourses() { return courses; } public void setCourses(List<String> courses) { this.courses = courses; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", courses=" + courses + '}'; } }
import java.lang.reflect.*; import java.util.Arrays; public class ReflectAPI { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { // 利用泛型的Class來指明這個Class對象爲Student的Class對象 Class<Student> stCls = Student.class; // 利用Class的newInstance()方法,調用無參構造器來建立對象 Student sDefault = stCls.newInstance(); System.out.println("調用無參構造器建立Student對象: "+sDefault); // 獲取帶String和int類型參數的構造器 Constructor ctr = stCls.getConstructor(String.class, int.class); Student s = (Student)ctr.newInstance("john", 20); System.out.println(s); // 爲s這個對象增長課程 // Student中courses字段爲private,getField("courses")會觸發NoSuchFieldException異常 // Field fld = stCls.getField("courses"); Field fld = stCls.getDeclaredField("courses"); // 因爲courses字段爲private的,不設置Field.setAccessible(true)會觸發IllegalAccessException異常 fld.setAccessible(true); fld.set(s, Arrays.asList("計算機網絡","操做系統")); System.out.println(fld.get(s)); // 設置爲private的字段,通常來講是不但願你去直接訪問它的。 // 一種合法的修改這個值的方法就是調用其public的set方法去修改它 // 這裏使用getMethod()方法去獲取修飾爲public的方法,若是這個方法爲private或者不存在會觸發NoSuchFieldException異常 Method m = stCls.getMethod("setAge", int.class); m.invoke(s, 24); System.out.println(s); } }
首先了解下什麼是代理模式:其目的是爲其餘對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理。而動態代理能夠動態的建立代理並動態的處理對所代理方法的調用。 JDK實現的動態代理只能代理實現了某接口的被代理對象。java動態代理主要用到java.lang.reflect.Proxy
類和java.lang.reflect.InvocationHandler
接口。動態代理的實現主要包括如下步驟:
- 實現InvocationHandler接口建立調用處理器
- 調用Proxy的newInstance()方法建立動態代理實例
如下是一個動態代理的示例程序:
public interface WorkerInterface { void doJob(String job); }
public class Worker implements WorkerInterface{ @Override public void doJob(String job) { System.out.println("doing "+job); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyProxyHandler implements InvocationHandler { private Object proxied; public MyProxyHandler(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----------------info------------------------"); System.out.println("Proxy: "+proxy.getClass()); System.out.println("Method: "+method); System.out.println("args: "+args); System.out.println("----------------info------------------------"); return method.invoke(proxied, args); } }
public class SimpleProxy{ public static void doJob(WorkerInterface wi) { System.out.println("Get ready to start work"); wi.doJob("Whitewashing"); System.out.println("Finish the work"); } public static void main(String[] args) { Worker w = new Worker(); WorkerInterface proxy = (WorkerInterface)Proxy.newProxyInstance(WorkerInterface.class.getClassLoader(), new Class[]{WorkerInterface.class}, new MyProxyHandler(w)); doJob(proxy); } }
咱們在調用Proxy.newInstance方法時其實是經歷了一下三步:
- 建立代理類的類對象Class<?> cl = getProxyClass0(loader, intfs)
- 反射從生成的類對象cls中獲取構造器對象:Constructor<?> cons = cl.getConstructor(constructorParams)
- 用上一步獲取的構造器對象建立動態代理類實例cons.newInstance(new Object[]{h})
經過動態代理咱們能夠爲目標類預處理消息、添加修飾等。