平常敲碼中,若是想要在程序運行階段訪問某個類的全部信息,並支持修改類的狀態或者行爲的話,確定會用到反射,而反射靠的就是Class類。Java的動態代理也用到了這個東西,因此瞭解其基本操做在苦逼的CRUD中會添加一絲絲樂趣(有點意思)。php
首先來看看Class的操做有哪些?java
public final class Class<T> {}
上述代碼可知,Class是一個由final修飾的泛型類,因此並不能直接經過new Class()獲取其實例。那麼應該如何獲取呢?程序員
//直接經過類的靜態變量來獲取 Class<Integer> intClass = Integer.class; //經過實例變量的getClass方法 Integer integer = new Integer(0); Class<? extends Integer> aClass = integer.getClass(); //經過Class.forName("類的全限定名") Class<?> aClass1 = Class.forName("java.lang.Integer");
上述三種就是獲取某個Class的class實例的方式,須要注意的是,JVM只會加載一個Class實例,也就是說上述三種方式獲取到的class實例都是同樣的。spring
而在運用反射的時候,Class.forName是最經常使用的一種方式。而Class.forName底層會指向forName0這個本地方法typescript
(1)name:類的全限定名安全
(2)initialize:是否初始化這個類微信
(3)loader:類加載器ide
(4)caller:調用Class.forName所在類的Class,好比A類代碼塊裏有Class.forName,那麼caller就是A的class實例。函數
經過Class類能夠獲取類的實例,構造方法,字段,成員方法,接口等信息。獲取以後能夠經過API進行相應的操做。ui
接下來看一下獲取到class實例以後怎麼獲取當前類的實例以及構造方法。
上述兩種方式都是調用默認的無參構造進行實例化對象,那麼怎麼經過公共或私有的有參構造獲取實例呢?
//屬性 int a; String b ; //公共有參構造 public ClassSource(int a) { this.a = a; } //私有有參構造 private ClassSource(String b) { this.b = b; } public static void main(String[] args){ Class<?> aClass = Class.forName("com.liusy.lang.ClassSource"); //獲取public有參構造 Constructor<?> constructor = aClass.getConstructor(int.class); ClassSource o1 = (ClassSource) constructor.newInstance(2); System.out.println("屬性【a】的值爲:"+ o1.a); //獲取private有參構造 Constructor<?> constructor1 = aClass.getDeclaredConstructor(String.class); //這個有貓膩 constructor1.setAccessible(true); ClassSource o2 = (ClassSource) constructor1.newInstance("abc"); System.out.println("屬性【b】的值爲:"+ o2.b); }
上述代碼運行的結果以下:
而獲取私有構造函數最重要的是要setAccessible(true)這個方法,點進去看一下,這個方法最後是給override字段賦值的,可用於類的字段、方法以及構造函數。
public void setAccessible(boolean flag) throws SecurityException { //這個「安全管理器」,容許應用程序在進行某些不安全或敏感操做 //以前執行安全策略,若是不容許執行,則經過拋異常阻止操做。 SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(ACCESS_PERMISSION); setAccessible0(this, flag); } private static void setAccessible0(AccessibleObject obj, boolean flag){ if (obj instanceof Constructor && flag == true) { Constructor<?> c = (Constructor<?>)obj; if (c.getDeclaringClass() == Class.class) { //若是是Class.class,則拋異常 } } //最終是設置此屬性 obj.override = flag; } //訪問級別是否能夠被覆蓋,可用於字段,方法,構造函數 boolean override;
須要注意的是,在代碼中用反射操做當前類私有字段、私有方法或其餘私有屬性時,並不須要setAccessible(true),例如在A類中用反射操做A類的私有屬性。只有在A類中操做其餘類的私有屬性才須要設置setAccessible(true)。
無論是Class.newInstance仍是Constructor.newInstance,底層調用的都是ConstructorAccessor(構造函數訪問器)接口的newInstance方法。
(1)Class.newInstance
//標識全部公共屬性 public static final int PUBLIC = 0; //標識全部私有屬性 public static final int DECLARED = 1;
public T newInstance(){ //省略部分代碼 // 檢查有沒有存在已加載過的構造器 if (cachedConstructor == null) { //省略部分代碼 try { Class<?>[] empty = {}; //獲取默認私有無參構造 final Constructor<T> c = getConstructor0(empty, Member.DECLARED); //省略部分代碼 cachedConstructor = c; } catch (NoSuchMethodException e) { throw (InstantiationException) new InstantiationException(getName()).initCause(e); } } Constructor<T> tmpConstructor = cachedConstructor; //省略部分代碼 try { //調用無參構造的newInstance方法 return tmpConstructor.newInstance((Object[])null); } catch (InvocationTargetException e) { //省略部分代碼 return null; } } private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) { //獲取構造函數,重點看這個方法,參數是false Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); //省略匹配構造函數參數的代碼 }
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) { //檢查是否已被初始化,若是否,則進行安全檢查 checkInitted(); Constructor<T>[] res; //反射的數據 ReflectionData<T> rd = reflectionData(); if (rd != null) { res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; if (res != null) return res; } // 檢查是不是接口 if (isInterface()) { //代碼省略 } else { //底層調用的是本地方法 res = getDeclaredConstructors0(publicOnly); } if (rd != null) { if (publicOnly) { rd.publicConstructors = res; } else { rd.declaredConstructors = res; } } return res; }
如上,能夠看到Class.newInstance底層是調用無參構造的newInstance方法。
(2)Constructor.newInstance
public T newInstance(Object ... initargs){ //override就是是否可覆蓋級別訪問 if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } } ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; } public interface ConstructorAccessor { Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException; }
能夠看到,Class.newInstance調用的是無參構造的newInstance,而構造函數調用的是ConstructorAccessor接口類的newInstance,因此最終調用的都是ConstructorAccessor接口類的newInstance。
最終調用的是NativeConstructorAccessorImpl的newInstance方法。
【注】如下僅演示操做方法,源碼不貼,防止篇幅太長。
獲取實例以及構造方法以後,來看一下如何訪問,修改類字段信息。
//public屬性 public int a; //private屬性 private String b; public static void main(String[] args) throws Exception { Class<?> aClass = Class.forName("com.liusy.lang.ClassSource"); //獲取實例 Object instance = aClass.newInstance(); //根據字段名獲取公共屬性並進行賦值 Field a = aClass.getField("a"); a.set(instance,2); System.out.println("a的值爲:"+a.get(instance)); //根據字段名獲取私有屬性並進行賦值 Field b = aClass.getDeclaredField("b"); b.setAccessible(true); b.set(instance,"abc"); System.out.println("b的值爲"+b.get(instance)); }
上述代碼運行結果爲:
接下來瞧一下如何利用class訪問類的成員方法:
public static void main(String[] args) throws Exception { Class<?> aClass = Class.forName("com.liusy.lang.ClassSource"); //獲取實例 Object instance = aClass.newInstance(); //獲取公共方法並調用 Method sayPublicHello = aClass.getMethod("sayPublicHello"); sayPublicHello.invoke(instance); //獲取私有方法並調用 Method sayPrivateHello = aClass.getDeclaredMethod("sayPrivateHello", int.class); sayPrivateHello.setAccessible(true); sayPrivateHello.invoke(instance,1); } public void sayPublicHello(){ System.out.println("sayPublicHello"); } private void sayPrivateHello(int param){ System.out.println("sayPrivateHello,參數:"+param); }
上述代碼運行結果爲:
還能夠使用「==」判斷數據類型
Class<?> aClass = Class.forName("com.liusy.lang.ClassSource"); System.out.println(aClass == ClassSource.class);
以前遇到過用了不少反射的代碼,看起來很噁心。但其實瞭解事後反而以爲反射是個很強大的東西,包括JDK動態代理,spring AOP,事務底層都是用的反射,研究以後有利於瞭解更多底層的知識。
=======================================================
我是Liusy,一個喜歡健身的程序員。
歡迎關注微信公衆號【Liusy01】,一塊兒交流Java技術及健身,獲取更多幹貨。