JAVA反射機制總結

JAVA反射機制總結

反射的概述

什麼是反射?

  • 反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改他狀態或行爲的一種能力。
  • JAVA反射機制是在運行狀態中,對於任何一個類,都能知道這個類的所有屬性和方法;對於任何一個對象,都能夠調用他的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能成爲Java語言的反射機制。
  • 簡單的來說反射機制指的的是程序在運行是能夠獲取自身的信息。在java中,只要給定類的名字,那麼就可以通過反射機制來獲得類的所有信息。包括其訪問修飾符、父類、實現的接口、屬性和方法的所有信息,並可在運行時創建對象、修改屬性(包括私有的)、調用方法(包括私有的)。

爲什麼用反射機制?

  • 初次接觸反射的概念,不經會想,直接創建對象不就可以了麼,這裏涉及到動態和靜態的概念。
  • 靜態編譯:在編譯時確定類型,綁定對象,即通過。
Student stu = new Student("zhangsan",30);
  • 動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多臺的應用,用以降低類之間的耦合性。
Class.forName("com.Lct.Person.Student").newInstance();
  • 一句話,反射機制的優點是可以實現動態創建對象和編譯,體現出很大的靈活性,特別是在J2EE的開發中。
  • 他的缺點是對性能的影響。使用反射基本上是一種解釋操作,這類操作總是慢於直接執行的相同操作,所以什麼時候使用反射,就要靠業務的需求、大小,以及經驗的積累來決定。

反射機制的關鍵

Java的反射機制的實現要藉助於class類和java.lang.reflect包下4個類: Constructor, Field, Method,Array

Class類 - 代表類的Class對象
Constructor類-代表類的構造方法
Field類-代表類的屬性
Method類-代表類的方法
Array類 - 提供動態創建數組,以及訪問數組元素的靜態方法
通過前四個對象我們可以粗略的看到一個類的各個組成部分
Array類會在最後有所簡述。

Java 反射機制主要提供了以下功能

在運行時判斷任意一個對象所屬的類。
在運行時構造任意一個類的對象。
在運行時判斷任意一個類所具有的成員變量和方法。
在運行時調用任意一個對象的方法

獲取Class對象

獲取Class對象的多種方式

代碼示例(獲取Class對象)

package com.Lct.classtype;

public class ClassDemo {

    public static void main(String[] args) {

        //使用實例對象.getClass()
        /*Employee employee = new Employee("zhangsan", 18);
        Class<?> class1 = employee.getClass();
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());*/

        //使用類名.Class
        /*Class<?> class1 = Employee.class;
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());*/

        //使用Class.forName("")
        /*try {
            Class<?> class1 = Class.forName("com.Lct.classtype.Employee");
            System.out.println(class1.getName());
            System.out.println(class1.getSuperclass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        //基本數據類型的Class對象
/*      Class <?> class1 = int.class;
        System.out.println(class1.getName());
        //基本數據類型沒有父類,下面代碼會報NullPointerException異常
        //System.out.println(class1.getSuperclass().getName());
*/       

        //包裝類獲取基本數據類型的Class對象
/*      Class <?> class1 = Integer.TYPE;
        System.out.println(class1.getName());
        //基本數據類型沒有父類,下面代碼同樣會報NullPointerException異常
        //System.out.println(class1.getSuperclass().getName());
*/  

        //獲取包裝類的Class對象
        Class <?> class1 = Integer.class;
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());
    }
}

class Employee {

    private String name;
    private int age;

    public Employee(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    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;
    }

}

獲得構造器的方法

Constructor getConstructor(Class[] params) — 根據構造函數的參數,返回一個具體的具有public屬性的構造函數
Constructor getConstructors() — 返回所有具有public屬性的構造函數數組
Constructor getDeclaredConstructor(Class[] params) — 根據構造函數的參數,返回一個具體的構造函數(不分public和非public屬性)
Constructor getDeclaredConstructors() — 返回該類中所有的構造函數數組(不分public和非public屬性)

獲得屬性的方法

Method getMethod(String name, Class[] params) — 根據方法名和參數,返回一個具體的具有public屬性的方法
Method[] getMethods() — 返回所有具有public屬性的方法數組
Method getDeclaredMethod(String name, Class[] params) — 根據方法名和參數,返回一個具體的方法(不分public和非public屬性)
Method[] getDeclaredMethods() — 返回該類中的所有的方法數組(不分public和非public屬性)

獲得方法信息的方法

Method getMethod(String name, Class[] params) — 根據方法名和參數,返回一個具體的具有public屬性的方法
Method[] getMethods() — 返回所有具有public屬性的方法數組
Method getDeclaredMethod(String name, Class[] params) — 根據方法名和參數,返回一個具體的方法(不分public和非public屬性)
Method[] getDeclaredMethods() — 返回該類中的所有的方法數組(不分public和非public屬性)

關於getMethods()和getDeclaredMethods()的區別

Java的API文檔中的註解
Method[] getDeclaredMethods()
返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。
Method[] getMethods()
返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。

抽象的文字描述總是沒有代碼表現的直白,示例代碼中有這兩個方法的調用,運行後的結果如下:

getDeclaredMethods方法獲取的所有方法 getMethods方法獲取的所有方法
修飾符–方法名–返回值類型 修飾符–方法名–返回值類型
public – toString – class java.lang.String public – toString – class java.lang.String
public – getName – class java.lang.String public – getName – class java.lang.String
public – write – void public – write – void
public – setName – void public – setName – void
privatework – void 無法獲得work方法
public – setAge – void public – setAge – void
public – getAge – int public – getAge – int
public – sleep – void (父類公共方法)
public final – wait – void (超類公共方法)
public final native – wait – void (超類公共方法)
public final – wait – void (超類公共方法)
public – equals – boolean (超類公共方法)
public native – hashCode – int (超類公共方法)
public final native – getClass – class java.lang.Class (超類公共方法)
public final native – notify – void (超類公共方法)
public final native – notifyAll – void (超類公共方法)

配合表格再去看API中的註解就事半功倍了。

Note:

  • 子類繼承父類,既可以重寫父類的方法,一旦重寫,通過getDeclaredMethods獲得就是子類中重寫的方法,不要誤以爲獲得的是父類中的方法。
  • getDeclaredMethod()和getMethod()是通過方法名和參數列表獲取指定的方法,所能獲取的方法的範圍各不相同,上述討論的情況一致。

代碼示例

package com.Lct.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 *  測試類
 */
public class ReflectionAPIDemo {

    public static void main(String[] args) throws Exception{
        //獲取Employee類所關聯的Class對象
        Class<?> classType =  Class.forName("com.Lct.reflection.Employee");

        //方法1
        //通過反射機制調用無參構造方法來實例化對象
        Employee employee = (Employee) classType.newInstance();
        System.out.println(employee);
        System.out.println("----------------------------------");

        //方法2
        //通過反射機制獲取構造方法對象,再通過該對象來實例化Employee類的實例
        Constructor<?> ct = classType.getConstructor(new Class[]{});
        //直接強制類型轉換也是可以的
        Object object = ct.newInstance(new Object[]{});
        if (object instanceof Employee) {
            Employee em1 = (Employee) object;
            System.out.println(em1);
        }
        System.out.println("----------------------------------");

        //通過反射機制獲取帶參數的構造方法對象,再通過反對向實例化Employee類的實例
        Constructor<?> ct2 = classType.getConstructor(new Class[]{String.class,int.class});
        Employee em2 = (Employee) ct2.newInstance(new Object[]{"張三",18});
        System.out.println(em2);

        //<<關於getMethods()和getDeclaredMethods()的區別>>
        //通過反射機制獲取Employee類中的所有方法,包括私有的
        Method[] methods = classType.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(Modifier.toString(method.getModifiers())+"--"+method.getName()+"--"+method.getReturnType());
        }
        System.out.println("----------------------------------");
        //通過反射機制獲取Employee類和超類中的所有共有方法
        Method[] m2 = classType.getMethods();
        for (Method m : m2) {
            System.out.println(Modifier.toString(m.getModifiers())+"--"+m.getName()+"--"+m.getReturnType());
        }
        System.out.println("----------------------------------");

        //getMethod方法調用父類的方法
        //getDeclaredMethod方法無法調用父類的public方法
        Method method2 = classType.getMethod("eat", new Class[]{});
        method2.invoke(em2, new Object[]{});
        System.out.println("----------------------------------");

        //通過反射機制獲取指定的方法,包括私有的,並可以調用它
        Method method = classType.getDeclaredMethod("work", new Class[]{int.class});
        System.out.println(method);
        //使私有的可以訪問
        method.setAccessible(true);
        //方法的調用
        method.invoke(em2, new Object[]{3});
        System.out.println("----------------------------------");

        //通過反射機制獲取所有屬性,包括私有的
        Field[] fields = classType.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------------------------");

        //通過反射機制獲取指定的屬性,包括私有的,並可以改變它
        Field field = classType.getDeclaredField("name");
        //使私有的可以訪問
        field.setAccessible(true);
        String object2 = (String) field.get(em2);
        System.out.println(object2);
        field.set(em2, "李四");
        System.out.println(em2);
        System.out.println("----------------------------------");
    }
}

/**
 *  父類
 */
class Person{

    public void sleep () {
        System.out.println("sleeping....");
    }

    @SuppressWarnings("unused")
    private void eat () {
        System.out.println("eating....");
    }
}

/**
 *  接口
 */
interface Study {
    public void write();
}

/**
 *  子類
 */
class Employee extends Person implements Study{

    private String name;
    private int age;
    private Object object;

    public Employee() {
        super();
        System.out.println("無參構造方法");
    }

    public Employee(String name, int age) {
        super();
        System.out.println("帶參數的構造方法");
        this.name = name;
        this.age = age;
    }

    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;
    }

    @Override
    public String toString() {
        return "Employee: "+"[name: "+this.name+" age: "+this.age+"]";
    }

    @SuppressWarnings("unused")
    private void work (int num) {
        System.out.println(num+" Employee -- working...");
    }

    @Override
    public void write() {
        System.out.println(" Employee -- writing...");
    }

}

Array

目前很少這麼用,個人感覺也是沒必要,但是本着存在即合理的原則還是寫下來,留個印象。

代碼示例

package com.Lct.reflection;

import java.lang.reflect.Array;

public class reflectionArrayDemo {

    public static void main(String[] args) throws Exception{
        Class<?> class1 = Class.forName("java.lang.String");
        //創建一維數組
        Object array = Array.newInstance(class1, 9);
        Array.set(array, 0, "zhangdan");
        System.out.println(Array.get(array, 0));

        //創建二維數組
        Object array2 = Array.newInstance(class1, new int[]{2,3}/*創建2行3列的二維數組*/);
        Object arrayObj = Array.get(array2, 1);
        Array.set(arrayObj, 1, "lisi");
        System.out.println(Array.get(arrayObj, 1));
    }

}

反射總結

  • 只要用到反射,現貨的Class對象
  • 沒有方法可以獲取當前類的超類的private的方法和屬性,你必須通過getSupperclass()方法找到超類之後再去嘗試獲得
  • 通常情況即使是當前類,private屬性或方法是不能訪問的,需要設置壓制權限setAccessible(true)來取得private的訪問權。但是需要注意,這已經破壞了java面向對象的封裝性規則,所以要謹慎使用。
  • 關於Java反射機制,這裏入門而已,還有很多地方值得深入,未完待續~~~

巨人的肩膀

Android反射機制實現與原理
作者寫的鉅細靡遺,其中LoadMethod類和升級版的LoadMethodEx類非常值得學習。

Android系統原理與源碼分析(1):利用Java反射技術阻止通過按鈕關閉對話框
這篇博客是Java反射機制在android中的使用實例,知道可以這樣用就好,但不推薦,雖然反射很強大,但是也需謹慎使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章