Java之反射Reflection

Paste_Image.png

什么是Java的反射

Java反射是可以讓我們在運行時獲取類的函數,屬性,父類,接口等Class內部信息的機制。通過反射還可以讓我們在運行期實例化對象,調用方法,通過調用get/set方法獲取變量的值,即使方法或屬性是私有的也可以通過反射的形勢調用,這種看透class的能力被稱為內省,這種能力在框架開發中尤為重要。有些情況下,我們要使用的類在運行時才會確定,這個時候我們不能在編譯器就使用它,因此只能通過反射的形勢來使用在運行才存在的類(該類符合某種特定的規范,例如JDBC),這是反射用得比較多的場景。
還有一個比較常見的場景就是編譯時我們對于類的內部信息不可知,必須得到運行時才能獲取類的具體信息。比如ORM框架,在運行時才能夠獲取類中的各個屬性,然后通過反射的形式獲取其屬性名和值,存入數據庫。這也是反射比較經典的應用場景之一。

下面我會為大家演示反射的一些常用的api,從代碼的角度理解反射。
反射的相關api列舉出來:

接口說明

<pre>// 加載指定的 Class 對象,參數 1 為要加載的類的完整路徑,例如"com.simple.Student". ( 常用方式 )
public static Class<?> forName (String className)

// 加載指定的 Class 對象,參數 1 為要加載的類的完整路徑,例如"com.simple.Student";
// 參數 2 為是否要初始化該 Class 對象,參數 3 為指定加載該類的 ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)</pre>

獲取構造函數接口

<pre>// 獲取一個公有的構造函數,參數為可變參數,如果構造函數有參數,那么需要將參數的類型傳遞給 getConstructor 方法
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// 獲取目標類所有的公有構造函數
public Constructor[]<?> getConstructors ()</pre>

獲取類中的方法接口

<pre>// 獲取 Class 對象中指定函數名和參數的函數,參數一為函數名,參數 2 為參數類型列表
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)

// 獲取該 Class 對象中的所有函數( 不包含從父類繼承的函數 )
public Method[] getDeclaredMethods ()

// 獲取指定的 Class 對象中的公有函數,參數一為函數名,參數 2 為參數類型列表
public Method getMethod (String name, Class...<?> parameterTypes)

// 獲取該 Class 對象中的所有公有函數 ( 包含從父類和接口類集成下來的函數 )
public Method[] getMethods ()</pre>

獲取類中的成員屬性接口

<pre>// 獲取 Class 對象中指定屬性名的屬性,參數一為屬性名
public Method getDeclaredField (String name)

// 獲取該 Class 對象中的所有屬性( 不包含從父類繼承的屬性 )
public Method[] getDeclaredFields ()

// 獲取指定的 Class 對象中的公有屬性,參數一為屬性名
public Method getField (String name)

// 獲取該 Class 對象中的所有公有屬性 ( 包含從父類和接口類集成下來的公有屬性 )
public Method[] getFields ()</pre>

獲取Class對象

在你想檢查一個類的信息之前,你首先需要獲取類的 Class 對象。Java 中的所有類型包括基本類型,即使是數組都有與之關聯的 Class 類的對象。如果你在編譯期知道一個類的名字的話,那么你可以使用如下的方式獲取一個類的 Class 對象。
<pre>Class<?> myObjectClass = MyObject.class;</pre>
如果你已經得到了某個對象,但是你想獲取這個對象的 Class 對象,那么你可以通過下面的方法得到:
<pre>Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();</pre>
如果你在編譯期獲取不到目標類型,但是你知道它的完整類路徑,那么你可以通過如下的形式來獲取 Class 對象:
<pre>Class<?> myObjectClass = Class.forName("com.simple.User");</pre>

在使用 Class.forName()方法時,你必須提供一個類的全名,這個全名包括類所在的包的名字。例如 User 類位于 com.simple 包,那么他的完整類路徑就是 com.simple.User。如果在調用 Class.forName()方法時,沒有在編譯路徑下(classpath)找到對應的類,那么將會拋出 ClassNotFoundException。

通過Class對象構造目標類型的對象

一旦你拿到 Class 對象之后,你就可以為所欲為了!當你善用它的時候它就是神兵利器,當你心懷鬼胎之時它就會變成惡魔。但獲取 Class 對象只是第一步,我們需要在執行那些強大的行為之前通過 Class 對象構造出該類型的對象,然后才能通過該對象釋放它的能量。 我們知道,在 java 中要構造對象,必須通過該類的構造函數,那么其實反射也是一樣一樣的。但是它們確實有區別的,通過反射構造對象,我們首先要獲取類的 Constructor(構造器)對象,然后通過 Constructor 來創建目標類的對象。還是直接上代碼的。
<pre> private static void classForName() {
try {
// 獲取 Class 對象
Class<?> clz = Class.forName("org.java.advance.reflect.Student");
// 通過 Class 對象獲取 Constructor,Student 的構造函數有一個字符串參數
// 因此這里需要傳遞參數的類型 ( Student 類見后面的代碼 )
Constructor<?> constructor = clz.getConstructor(String.class);
// 通過 Constructor 來創建 Student 對象
Object obj = constructor.newInstance("mr.simple");
System.out.println(" obj : " + obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
}</pre>

Person.java

<pre>public class Person {
String mName;

public Person(String aName) {
    mName = aName;
}

private void sayHello(String friendName) {
    System.out.println(mName + " say hello to " + friendName);
}

protected void showMyName() {
    System.out.println("My name is " + mName);
}

public void breathe() {
    System.out.println(" take breathe ");
}

}</pre>

Student.java

<pre>public class Student extends Person implements Examination {
// 年級
int mGrade;

public Student(String aName) {
    super(aName);
}

public Student(int grade, String aName) {
    super(aName);
    mGrade = grade;
}

private void learn(String course) {
    System.out.println(mName + " learn " + course);
}

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

public String toString() {
    return " Student :  " + mName;
}</pre>

Examination.java

<pre>// 考試接口
public interface Examination {
public void takeAnExamination();
}</pre>

反射獲取類中定義的方法

要獲取當前類中定義的所有方法可以通過 Class 中的 getDeclaredMethods 函數,它會獲取到當前類中的 public、default、protected、private 的所有方法。而 getDeclaredMethod(String name, Class...<?> parameterTypes)則是獲取某個指定的方法。代碼示例如下 :
<pre> private static void showDeclaredMethods() {
Student student = new Student("mr.simple");
Method[] methods = student.getClass().getDeclaredMethods();
for (Method method : methods) {
System.out.println("declared method name : " + method.getName());
}

    try {
        Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
        // 獲取方法的參數類型列表
        Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
        for (Class<?> class1 : paramClasses) {
            System.out.println("learn 方法的參數類型 : " + class1.getName());
        }
        // 是否是 private 函數,屬性是否是 private 也可以使用這種方式判斷
        System.out.println(learnMethod.getName() + " is private "
                + Modifier.isPrivate(learnMethod.getModifiers()));
        learnMethod.invoke(student, "java ---> ");
    } catch (Exception e) {
        e.printStackTrace();
    }
}</pre>

獲取當前類,父類中定義的公有方法

要獲取當前類以及父類中的所有 public 方法可以通過 Class 中的 getMethods 函數,而 getMethod 則是獲取某個指定的方法。代碼示例如下 :
<pre>private static void showMethods() {
Student student = new Student("mr.simple");
// 獲取所有方法
Method[] methods = student.getClass().getMethods();
for (Method method : methods) {
System.out.println("method name : " + method.getName());
}

    try {
        // 通過 getMethod 只能獲取公有方法,如果獲取私有方法則會拋出異常,比如這里就會拋異常
        Method learnMethod = student.getClass().getMethod("learn", String.class);
        // 是否是 private 函數,屬性是否是 private 也可以使用這種方式判斷
        System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
        // 調用 learn 函數
        learnMethod.invoke(student, "java");
    } catch (Exception e) {
        e.printStackTrace();
    }
}</pre>

這里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函數,并且通過這兩個函數獲取到的只是在自身中定義的函數,從父類中集成的函數不能夠獲取到。而 getMethod 和 getMethods 只包含 public 函數,父類中的公有函數也能夠獲取到。

反射當前類中定義的屬性

要獲取當前類中定義的所有屬性可以通過 Class 中的 getDeclaredFields 函數,它會獲取到當前類中的 public、default、protected、private 的所有屬性。而 getDeclaredField 則是獲取某個指定的屬性。代碼示例如下 :
<pre>private static void showDeclaredFields() {
Student student = new Student("mr.simple");
// 獲取當前類和父類的所有公有屬性
Field[] publicFields = student.getClass().getDeclaredFields();
for (Field field : publicFields) {
System.out.println("declared field name : " + field.getName());
}

    try {
        // 獲取當前類和父類的某個公有屬性
        Field gradeField = student.getClass().getDeclaredField("mGrade");
        // 獲取屬性值
        System.out.println(" my grade is : " + gradeField.getInt(student));
        // 設置屬性值
        gradeField.set(student, 10);
        System.out.println(" my grade is : " + gradeField.getInt(student));
    } catch (Exception e) {
        e.printStackTrace();
    }
}</pre>

獲取當前類、父類中定義的公有屬性

要獲取當前類以及父類中的所有 public 屬性可以通過 Class 中的 getFields 函數,而 getField 則是獲取某個指定的屬性。代碼示例如下 :
<pre> private static void showFields() {
Student student = new Student("mr.simple");
// 獲取當前類和父類的所有公有屬性
Field[] publicFields = student.getClass().getFields();
for (Field field : publicFields) {
System.out.println("field name : " + field.getName());
}

    try {
        // 獲取當前類和父類的某個公有屬性
        Field ageField = student.getClass().getField("mAge");
        System.out.println(" age is : " + ageField.getInt(student));
    } catch (Exception e) {
        e.printStackTrace();
    }
}</pre>

這里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的屬性,并且通過這兩個函數獲取到的只是在自身中定義的屬性,從父類中集成的屬性不能夠獲取到。而 getField 和 getFields 只包含 public 屬性,父類中的公有屬性也能夠獲取到。

獲取Class對象中實現的接口

<pre>private static void showInterfaces() {
Student student = new Student("mr.simple");
Class<?>[] interfaceses = student.getClass().getInterfaces();
for (Class<?> class1 : interfaceses) {
System.out.println("Student's interface is : " + class1.getName());
}
}</pre>

獲取Class對象的父類

<pre>Student student = new Student("mr.simple");
Class<?> superClass = student.getClass().getSuperclass();
while (superClass != null) {
System.out.println("Student's super class is : " + superClass.getName());
// 再獲取父類的上一層父類,直到最后的 Object 類,Object 的父類為 null
superClass = superClass.getSuperclass();
}</pre>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,973評論 19 139
  • 1. 了解 Java 中的反射 1.1 什么是 Java 的反射 Java 反射是可以讓我們在運行時獲取類的函數、...
    Ten_Minutes閱讀 555評論 0 4
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評論 18 399
  • 一:java概述:1,JDK:Java Development Kit,java的開發和運行環境,java的開發工...
    ZaneInTheSun閱讀 2,688評論 0 11
  • 【墨蘭】 墨蝶旋翼舞蓮蓮, 盈姿裊裊騰云天 。 絳唇微啟鍍春色, 幽吐蘭香攝玄月 。
    茵夢湖蘭閱讀 799評論 0 2