反射

認識發射

反射指的是對象的反向處理操作,根據對象倒推類的組成。既然是反向處理,我們先來觀察一下"正"的操作。在默認情況下,必須要先導入一個包,而後才能產生類的實例化對象

範例:觀察正常處理

import java.util.Date;

public class Test {
    public static void main(String[] args) throws Exception{
        Date date = new Date();
    }
}

以上是我們正常的關於對象的處理流程:根據**包名.類名找到類** 。所謂的"反"指的是根據對象來取得對象的來源信息,而這個"反"的操作核心的處理就在於Object類的一個方法: 取得Class對象:

* @return The {@code Class} object that represents the runtime
*         class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();

在反射的世界裏面,看重的不再是一個對象,而是對象身後的組成(類、構造、普通、成員等)

class對象的三種實例化方式

Class類是描述整個類的概念,也是整個反射的操作源頭,在使用Class類的時候需要關注的依然是這個類的對象。 任何一個類的class對象由JVM加載類後產生(該對象在JVM中全局唯一),用戶只能調用指定方法來取得該對象。

而這個類的對象的產生模式一共有三種:

  1. 任何類的實例化對象可以通過調用Object類提供的getClass()取得該類的Class對象
  2. 類名稱.Class”可以直接根據某個具體類來取得其Class對象
  3. 調用Class類的靜態方法:public static Class<?> Class.forName(String className) throws ClassNotFoundException 傳入類的全名稱來取得其Class對象
import java.util.Date;

public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        // 通過類對象.getClass()
        System.out.println(date.getClass());
        // 通過類名稱.class
        System.out.println(Date.class);
        // 通過調用Class類提供的靜態方法forName(String className)
        try {
            System.out.println(Class.forName("java.util.Date"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在以上給出的三個方法中我們可以發現,除了第一種方法會產生Date類的實例化對象之外,其他的兩種都不會產生 Date類的實例化對象。

於是取得了Class類對象有一個最直接的好處:可以通過反射實例化對象,在Class類中定義有如下方法:

public T newInstance()
        throws InstantiationException, IllegalAccessException

範例:反射實例化對象

import java.util.Date;

public class Test {
    public static void main(String[] args) throws Exception{
        // 取得類的實例化對象
        Class<Date> cls = (Class<Date>)Class.forName("java.util.Date");
        // 通過反射取得Date類實例化對象
        Date date = cls.newInstance();  // 實例化對象,等價於 new java.util.Date() 
        System.out.println(date);
    }
}

現在發現除了關鍵字new之外,對於對象的實例化模式有了第二種做法,通過反射進行

反射與工廠設計模式

工廠設計模式曾經給過原則:如果是自己編寫的接口,要想取得本接口的實例化對象,最好使用工廠類來設計。但是也需要知道傳統工廠設計所帶來的問題。

傳統工廠類:https://blog.csdn.net/sifanchao/article/details/83420681
傳統工廠類在實際開發之中根本用不到。(問題就在於new)。每增加一個接口的子類就需要修改工廠類
如果要想解決關鍵字new帶來的問題,最好的做法就是通過反射來完成處理,因爲Class類可以使用newInstance() 實例化對象,同時Class.forName()能夠接收類名稱。

interface IFruit{
    void eat();
}
class Apple implements IFruit {
    @Override
    public void eat() {
        System.out.println("吃蘋果");
    }
}
class Orange implements IFruit{
    @Override
    public void eat() {
        System.out.println("吃橘子");
    }
}
class FruitFactory {
    public FruitFactory() {}
    public static IFruit getInstance(String className) {
        IFruit fruit = null;
        try {
            fruit = (IFruit) Class.forName(className).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return fruit;
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        IFruit fruit = FruitFactory.getInstance("www.bit.MyTest.Apple");
        fruit.eat();
    }
}

引入反射後,每當新增接口子類,無需去修改工廠類代碼就可以很方便的進行接口子類擴容

反射與類操作

利用反射可以做出一個對象具備的所有操作行爲,最爲關鍵的是這一切的操作都可以基於Object進行

取得父類信息

在java中任何的程序類都一定會有父類,在Class類中就可以通過如下方法來取得父類或者實現的父接口:

  • 取得類的包名稱
    public Package getPackage() {
        return Package.getPackage(this);
    }
  • 取得父類的Class對象
    public native Class<? super T> getSuperclass();
  • 取得實現的父接口
    public Class<?>[] getInterfaces()

範例:取得包名稱、父類、父接口

interface IFruit{}
interface IMessage{}
class Person{}
class Student extends Person implements IFruit,IMessage{}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Student.class;  // 取得Class類對象
        // 取得Package名稱
        System.out.println(cls.getPackage().getName());
        // 取得父類名稱
        System.out.println(cls.getSuperclass().getName());
        // 取得實現的父接口對象 
        Class<?>[] iclass = cls.getInterfaces();
        for (Class<?> classes: iclass){
            System.out.println(classes.getName());
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
www.bit.MyTest
www.bit.MyTest.Person
www.bit.MyTest.IFruit
www.bit.MyTest.IMessage

反射調用構造—Constructor(描述類中構造方法)

  • 取得類中指定public權限參數類型的構造
    @CallerSensitive
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }
  • 取得類中指定參數(包含private權限)類型的構造
    @CallerSensitive
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.DECLARED);
    }
  • 取得類中public權限的所有參數類型構造
    @CallerSensitive
    public Constructor<?>[] getConstructors() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
  • 取得類中所有參數(包含private權限)類型構造
    @CallerSensitive
    public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(false));
    }

範例:取得類中構造信息

import java.lang.reflect.Constructor;

class Person {
    public Person(){}
    protected Person(String name){}
    private Person(String name,int age){}
    public Person(String name,int age,String id){}
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        // 取得類中指定`public權限`參數類型的構造
        Constructor<?> constructor1 = cls.getConstructor(String.class.getClasses());
        System.out.println(constructor1);
        System.out.println("------------------------------------");

        // 取得類中`public權限`的所有參數類型構造
        Constructor<?>[] constructor2 = cls.getConstructors();
        for (Constructor<?> constructor : constructor2){
            System.out.println(constructor);
        }
        System.out.println("------------------------------------");

        // 取得類中指定參數(`包含private權限`)類型的構造
        Constructor<?> constructor3 = cls.getDeclaredConstructor(String.class,int.class);
        System.out.println(constructor3);
        System.out.println("------------------------------------");
        
        // 取得類中所有參數(`包含private權限`)類型構造
        Constructor<?>[] constructors = cls.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors){
            System.out.println(constructor);
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public www.bit.MyTest.Person()
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
public www.bit.MyTest.Person()
------------------------------------
private www.bit.MyTest.Person(java.lang.String,int)
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
private www.bit.MyTest.Person(java.lang.String,int)
protected www.bit.MyTest.Person(java.lang.String)
public www.bit.MyTest.Person()

Process finished with exit code 0
  • Constructor類提供了實例化對象的方法
    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException

在定義簡單java類的時候一定要保留有一個無參構造
範例:觀察Class實例化對象的問題

class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        System.out.println(cls.newInstance());
    }
}

Exception in thread "main" java.lang.InstantiationException: www.bit.MyTest.Person
	at java.lang.Class.newInstance(Class.java:427)
	at www.bit.MyTest.Test1.main(Test1.java:150)
Caused by: java.lang.NoSuchMethodException: www.bit.MyTest.Person.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 1 more

Process finished with exit code 1

Class類通過反射實例化類對象的時候,只能夠調用類中的無參構造。如果現在類中沒有無參構造則無法使用Class 類調用,只能夠通過明確的構造調用實例化處理

範例:通過Constructor類實例化對象

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        // 取得指定參數類型的構造方法對象
        System.out.println(cls.getConstructor(String.class,int.class).newInstance("改革開放",40));
    }
}

反射調用普通方法

  • 取得類中指定public權限參數類型的普通方法
    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
  • 取得類中指定參數(包含private權限)類型的普通方法
    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
  • 取得類中public權限的所有參數類型普通方法
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }
  • 取得類中所有參數(包含private權限)類型普通方法
    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

範例:取得一個類中的全部普通方法

import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;
    public Person(){}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}
public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods){
            System.out.println(method);
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.toString()
public java.lang.String www.bit.MyTest.Person.getName()
public void www.bit.MyTest.Person.setName(java.lang.String)
public void www.bit.MyTest.Person.setAge(int)
public int www.bit.MyTest.Person.getAge()

Process finished with exit code 0

上面程序編寫的簡單java類中的getter、setter方法採用的都是明確的對象調用。
而現在有了反射機制處理之後,即使你沒有明確的Person類型對象(依然需要實例化對象,Object對象描述,所有 的普通方法必須在有實例化對象之後纔可以進行調用),就可以通過反射調用。

  • Method類中有提供調用類中普通方法的API
import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;
    public Person() {}
    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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        // 1.拿到Person的Class對象
        Class<?> cls = Person.class;
        // 2.創建Person實例化對象
        Person person = (Person) cls.newInstance();
        // 3.拿到setName的Method對象
        Method setNameMethod = cls.getMethod("setName", String.class);
        // 4.通過invoke進行調用,相當於Person對象.setName("改革開發")
        setNameMethod.invoke(person,"改革開發");

        Method getNameMethod = cls.getMethod("getName");
        // 相當於Person對象.getName()
        System.out.println(getNameMethod.invoke(person));
    }
}

此類操作的好處是:不再侷限於某一具體類型的對象,而是可以通過Object類型進行所有類的方法調用

反射調用類中屬性

在之前已經成功的實現了類的構造調用、方法調用,除了這兩種模式之外還有類中屬性調用。

前提:類中的所有屬性一定在類對象實例化之後纔會進行空間分配,所以此時如果要想調用類的屬性,必須保證有實例化對象。通過反射的newInstance()可以直接取得實例化對象(Object類型)

  • 取得類中指定public權限參數類型的屬性
    @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Field field = getField0(name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
  • 取得類中指定參數(包含private權限)類型的屬性
    @CallerSensitive
    public Field getDeclaredField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Field field = searchFields(privateGetDeclaredFields(false), name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
  • 取得類中public權限的所有參數類型屬性
    @CallerSensitive
    public Field[] getFields() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }
  • 取得類中所有參數(包含private權限)類型屬性
    @CallerSensitive
    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }

範例:取得類中全部屬性

import java.lang.reflect.Field;

class Person {
    public String name;
    public int age;
    protected String id;
}
class Student extends Person{
    private String school;
}

public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("www.bit.MyTest.Student");
        {
            Field[] fields = cls.getFields();
            for (Field field : fields){
                System.out.println(field);
            }
        }
        System.out.println("-----------------------");
        {
            Field[] fields = cls.getDeclaredFields();
            for (Field field : fields){
                System.out.println(field);
            }
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.name
public int www.bit.MyTest.Person.age
-----------------------
private java.lang.String www.bit.MyTest.Student.school

Process finished with exit code 0

因爲在實際開發之中,屬性基本上都會進行封裝處理,所以沒有必要去關注父類中的屬性。也就是說以後所取得的屬性都以本類屬性爲主。

而後就需要關注屬性的核心描述類:java.lang.reflect.Field,在這個類之中有兩個重要方法:

  • 設置屬性內容 :
 public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException 
  • 取得屬性內容 :
 public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
  • 取得屬性的類型
public Class<?> getType()

在這裏插入圖片描述
在這裏插入圖片描述

  • 動態設置封裝

Constructor、Method、Field類都是AccessibleObject子類。
AccessibleObject提供動態設置封裝方法(在本次JVM進行中有效且只能通過反射調用)

public void setAccessible(boolean flag) throws SecurityException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    setAccessible0(this, flag);
}

setAccessible(true)取消了Java的權限控制檢查(注意不是改變方法或字段的訪問權限),調用了類的private屬性,並且能夠修改private成員變量的值

import java.lang.reflect.Field;

class Person {
    public Person(){}
    public String name;
    private int age;
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        Person person = (Person) cls.getConstructor().newInstance();

        Field nameField = cls.getField("name");
        nameField.set(cls,"劉德華");
        System.out.println(nameField.get(cls));
        System.out.println(nameField.getType().getName());

        Field ageField = cls.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(cls,45);
        System.out.println(ageField.get(cls));
        System.out.println(ageField.getType().getName());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章