進擊的Android工程師之Java基礎: 反射

反射機制呢就是在程序運行時,動態的獲取類(class),類的方法(method)屬性(field)等。主要的注意點就是程序運行時動態的獲取。
這裏主要是從代碼的角度來說解Java反射。在使用中咱們用的較多的幾個類有ClassMethodFieldConstructor,Annotation等。
下面咱們分別介紹下。java

獲取Class

獲取class對象有以下三種方式:數組

  • cat.getClass
  • Cat.class
  • Class.forName("xyz.magicer.Cat")
//方式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()
不過改方法會拋出兩個異常:InstantiationExceptionIllegalAccessExceptioncode

  • InstantiationException :當該Class不能被實例化的時候拋出該異常,例如,爲抽象類、接口、數組類、基本類、Void等時。
  • IllegalAccessException:當無參構造爲私有時

還有一種建立實體的方式是使用Constructor,在下邊在介紹。對象

Method

獲取Method

類或接口的方法對應這Method這個類。咱們經過Class對象來獲取。主要方法有接口

  • getMethods(): 返回Method[] 返回全部public的方法。包含父類或父接口的方法。
  • getDeclaredMethods(): 返回Method[] 返回全部的方法。包括 private public defaultprotected的。不包含父類或父接口的方法
  • 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[],不帶返回的是Methodget

調用Method

既然咱們獲取到了方法(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

Method經常使用方法

  • getModifiers(): 返回方法修飾符
  • getAnnotations(): 能夠獲取父類的註解
  • getDeclaredAnnotations(): 不能夠返回父類的註解
  • getAnnotation(Class<T> annotationClass)
  • getDeclaredAnnotation(Class<T> annotationClass)
  • getParameters(): 返回Parameter[] (java1.8加入)

Field

獲取Field對象的方式跟Method同樣,用法和規律都同樣。無非是如今方法改成getFields()、getDeclaredFields()、getField(String)、getDeclaredField(String)。class

設置Field

能夠經過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

獲取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());
相關文章
相關標籤/搜索