反射機制呢就是在程序運行時,動態的獲取類(class),類的方法(method)屬性(field)等。主要的注意點就是程序運行時動態的獲取。
這裏主要是從代碼的角度來說解Java反射。在使用中咱們用的較多的幾個類有Class
,Method
,Field
,Constructor
,Annotation
等。
下面咱們分別介紹下。java
獲取class對象有以下三種方式:數組
//方式1 調用對象的getClass()方式,該方法屬於Object類 Class<? extends Cat> aClass = cat.getClass(); //方式2 直接類.class獲取。 Class<Cat> catClass = Cat.class; //方式3 經過Class類的靜態方法forName獲取,參數爲想要獲取的類的全名(含包名,否則會報ClassNotFoundException) try { Class<?> cat2 = Class.forName("xyz.magicer.Cat"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
在使用中根據實際狀況選擇,獲取Class
對象是咱們使用反射的第一步。不過仍是很簡單的。獲取了Class
對象後,怎麼建立一個實體呢?catClass.newInstance()
不過改方法會拋出兩個異常:InstantiationException
和IllegalAccessException
code
還有一種建立實體的方式是使用Constructor
,在下邊在介紹。對象
類或接口的方法對應這Method
這個類。咱們經過Class對象來獲取。主要方法有接口
getMethods()
: 返回Method[]
返回全部public
的方法。包含
父類或父接口的方法。getDeclaredMethods()
: 返回Method[]
返回全部的方法。包括 private
public
default
和 protected
的。不包含
父類或父接口的方法getMethod(String name, Class<?>... parameterTypes)
: 返回Method
, 根據方法名和類型獲取Method。類或接口的及父類父接口公共成員方法。getDeclaredMethod(String name, Class<?>... parameterTypes)
: 返回Method
。根據方法名和類型返回Method。類和接口的全部方法。//sleep是private的 Method[] methods = catClass.getMethods(); for (Method method : methods) { //method.getName() 返回方法名,不包括修飾符,參數和返回值。 System.out.printf(method.getName()+" "); } // 打印toString getName setName setColor eat eat getAge setAge getColor wait wait wait equals hashCode getClass notify notifyAll System.out.println(); Method[] declaredMethods = catClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.printf(declaredMethod.getName()+" "); } //打印 toString getName setName sleep setColor eat eat getAge setAge getColor //拋出NoSuchMethodException 由於sleep的訪問權限爲private //Method sleep1 = catClass.getMethod("sleep", null); Method hashCode = catClass.getMethod("hashCode", null); //拋出NoSuchMethodException,由於hashCode是父類的方法。 //Method hashCode1 = catClass.getDeclaredMethod("hashCode", null); Method eat2 = catClass.getMethod("eat",null); Method sleep1 = catClass.getDeclaredMethod("sleep", null);
經過上面的代碼咱們能看到,getMethods()能夠獲取父類的方法,可是不能獲取私有方法,而getDeclaredMethod()方法不能夠獲取父類的方法,可是能夠獲取私有的方法。getMethod也是同樣,能夠獲取父類父接口方法,可是沒法獲取私有方法,而getDeclaredMethod能夠獲取私有方法不能獲取父類父接口方法。
而帶s和不帶s的區別是帶s返回值爲Method[]
,不帶返回的是Method
。get
既然咱們獲取到了方法(Method)固然是想調用它,那麼怎麼調用呢?Method有個方法invoke(Object obj, Object... args)
.
invoke接收兩個參數,第一個是調用該方法的實體,第二個是方法的參數。參數類型多個時順序要跟方法參數相同。hash
Method eat1 = catClass.getMethod("eat"); eat1.invoke(catInstance,null); //打印:我只吃小魚乾。 Method eat = catClass.getDeclaredMethod("eat"); eat.invoke(catInstance,null); //打印: 我只吃小魚乾。 Method sleep = catClass.getDeclaredMethod("sleep"); //IllegalAccessException sleep.invoke(catInstance,null);
咱們會發現當私有方法invoke調用時會拋出IllegalAccessException,不是能夠獲取麼爲何不能調用?由於方法有權限修飾符,咱們須要設置成咱們能夠調用的。以下:it
sleep.setAccessible(true); sleep.invoke(catInstance,null);
在調用前設置爲能夠調用就解決了。
在前面咱們接觸到了兩個Method類的方法了(getName,和invoke)。io
獲取Field對象的方式跟Method同樣,用法和規律都同樣。無非是如今方法改成getFields()、getDeclaredFields()、getField(String)、getDeclaredField(String)。class
能夠經過set方法來設置值 public void set(Object obj, Object value)
每種基本數據類型都有的setxxx()方法。
//name 爲私有 Field name = catClass.getDeclaredField("name"); name.setAccessible(true); name.set(cat,"啦啦啦"); System.out.println("\n"+cat.toString());
在使用反射設置屬性的時候必定要注意,可能在代碼中用到該屬性的地方較多,改變了值以後引發一些意想不到的效果。
獲取Constructor對象的方式跟Field和Method同樣。有四個方法:
getConstructors(),getDeclaredConstructors,getConstructor(Class<?>... parameterTypes), getDeclaredConstructor(Class<?>... parameterTypes)。
獲得了Constructor對象後咱們就能夠調用newInstance(Object ... initargs)
方法進行初始化了,注意參數的順序。
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class); Cat cat1 = constructor1.newInstance("喵喵2", 3, "white"); System.out.println(cat1.toString());
到這裏咱們看到,經過反射建立一個對象有兩種方式:(1)class.newInstance()和(2)consturctor.newInstance(Object ... initargs)。那麼他們有什麼區別呢?
方式(1)只能調用無參構造建立對象,而且無參構造不能爲私有,而方式(2)能夠調用全部
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class); Cat cat1 = constructor1.newInstance("喵喵2", 3, "white"); System.out.println(cat1.toString()); //調用私有構造 Constructor<Cat> constructor2 = catClass.getDeclaredConstructor(String.class); constructor2.setAccessible(true); Cat miaomiao = constructor2.newInstance("miaomiao"); System.out.println(miaomiao.toString());