Java:反射機制

反射的理解

/*
Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法
框架 = 反射 + 註解 + 設計模式
*/

反射機制能提供的功能

/*
1. 在運行時判斷任意一個對象所屬的類
2. 在運行時構造任意一個類的對象
3. 在運行時判斷任意一個類所具有的成員變量和方法
4. 在運行時獲取泛型信息
5. 在運行時調用任意一個對象的成員變量和方法
6. 在運行時處理註解
7. 生成動態代理
*/

反射前對Person類的操作

Person.java

package Reflection;

/**
 * ClassName: Person
 * Date:      2020/3/11 12:25
 * author:    Oh_MyBug
 * version:   V1.0
 */
public class Person {
    private String name;
    public int age;

    public Person() {
        System.out.println("這是空參構造器:Person()");
    }

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

    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 void show(){
        System.out.println("你好,我是一個人");
    }

    private String showNation(String nation){
        System.out.println("我的國籍是:" + nation);
        return nation;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
@Test
public void test1(){
    // 1. 創建Person類的對象
    Person p1 = new Person("Tom",12);
    // 2. 通過對象,調用其內部的屬性、方法
    p1.age = 10;
    System.out.println(p1);
    p1.show();
    // 在Person類外部,不可以通過Person類的對象調用其內部私有的結構
    // 比如:name、showNation()以及私有的構造器
    /*
     輸出:
         Person{name='Tom', age=10}
         你好,我是一個人
     */
}

反射後對Person類的操作

@Test
public void test2() throws Exception {
    Class clazz = Person.class;
    // 1. 通過反射,創建Person類的對象
    Constructor cons = clazz.getConstructor(String.class, int.class);
    Object obj = cons.newInstance("Tom", 12);
    System.out.println(obj);
    Person p = (Person) obj;
    System.out.println(p);
    // 2. 通過反射,調用對象指定的屬性和指定的方法
    // 調用屬性
    Field age = clazz.getDeclaredField("age");
    age.set(p, 10);
    System.out.println(p);
    // 調用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);
    System.out.println("*****************************");
    // 通過反射可以調用Person類的私有結構。比如:私有的構造器、方法、屬性
    // 調用私有構造器
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);
    // 調用私有屬性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"HanMeiMei");
    System.out.println(p1);
    // 調用私有方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1,"中國");// 相當於String nation = p1.showNation("中國");
    System.out.println(nation);
    /*
     輸出:
         Person{name='Tom', age=12}
         Pe{name='Tom', age=12}
         Person{name='Tom', age=10}
         你好,我是一個人
         *****************************
         Person{name='Jerry', age=0}
         Person{name='HanMeiMei', age=0}
         我的國籍是:中國
         中國
      */
}

對Class類的理解

/*
關於Java.lang.Class類的理解
1. 類的加載過程
程序經過javac.exe命令後,會生成一個或多個字節碼文件(.class結尾)。
接着我們使用java.exe命令對某個字節碼文件進行解釋運行。相當於將某個
字節碼文件加載到內存中。此過程就成爲類的加載。加載到內存中的類,我
們就稱爲運行時類,此運行時類,就作爲Class的一個實例。
2. 換句話說,Class的實例就對應着一個運行時類。
3. 加載到內存中的運行時類,會緩存一定的時間。在此時間之內,我們可
    以通過不同的方式來獲取此運行時類。
*/

獲取Class實例的四種常見方式

@Test
public void test3() throws ClassNotFoundException {
    // 方式一:調用運行時類的屬性
    Class clazz1 = Person.class;
    System.out.println(clazz1); // 輸出:class Reflection.Person
    // 方式二:通過運行時類的對象,調用getClass()
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    System.out.println(clazz2); // 輸出:class Reflection.Person
    // 方式三:調用Class的靜態方法:forName(String classPath)
    Class clazz3 = Class.forName("Reflection.Person");
    System.out.println(clazz3); // 輸出:class Reflection.Person
    //        clazz3 = Class.forName("java.lang.String");
    //        System.out.println(clazz3); // 輸出:class java.lang.String
    System.out.println(clazz1 == clazz2); // 輸出:true
    System.out.println(clazz1 == clazz3); // 輸出:true
    // 方式四:使用類的加載器:ClassLoader
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("Reflection.Person");
    System.out.println(clazz4); // 輸出:class java.lang.String
    System.out.println(clazz1 == clazz4); // 輸出:true
}

Class實例可以是哪些結構的說明

@Test
public void test4(){
    Class c1 = Object.class;
    Class c2 = Comparable.class;
    Class c3 = String.class;
    Class c4 = int[][].class;
    Class c5 = ElementType.class;
    Class c6 = Override.class;
    Class c7 = int.class;
    Class c8 = void.class;
    Class c9 = Class.class;

    int[] a = new int[9];
    int[] b = new int[100];
    Class c10 = a.getClass();
    Class c11 = b.getClass();
    // 只要元素類型與維度一樣,就是同一個Class
    System.out.println(c10 == c11); // 輸出:true
}

創建Class對應運行時類的通用方法

@Test
public void test1() throws Exception {
    Class clazz = Person.class;
    /*
    newInstance():調用此方法,創建對應的運行時類的對象。內部調用了運行時類的空參的構造器
    要想此方法正常的創建運行時類的對象,要求:
    1. 運行時類必須提供空參構造器
    2. 空參的構造器的訪問權限得夠。通常設置爲public。

    在javabean中要求提供一個public的空參構造器。原因:
    1. 便於通過反射,創建運行時類的對象
    2. 便於子類繼承此運行時類時,默認調用super()時,保證服了由此構造器
    */
    Person obj = (Person) clazz.getConstructor().newInstance();
    System.out.println(obj);  // 輸出:這是空參構造器:Person() \n Person{name='null', age=0}
}

反射的動態性

@Test
public void test2(){
    int num = new Random().nextInt(3);// 0,1,2
    String classPath = ";";
    switch (num){
        case 0:
            classPath = "java.util.Date";
            break;
        case 1:
            classPath = "java.lang.Object";
            break;
        case 2:
            classPath = "Reflection.Person";
            break;
    }
    try {
        Object obj = getInstance(classPath);
        System.out.println(obj);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

通過反射獲取運行時類的完整結構

需要用到的類、接口、註解

package Reflection;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "Hello";
}
package Reflection;

public interface MyInterface {
    void info();
}
package Reflection;

/**
 * ClassName: People
 * Date:      2020/3/11 15:32
 * author:    Oh_MyBug
 * version:   V1.0
 */
@MyAnnotation(value = "OhMyBug-Class")
public class People extends Creature<String> implements Comparable<String>,MyInterface{
    private String name;
    int age;
    public int id;

    public People() {
    }
    @MyAnnotation(value = "OhMyBug-Construstor")
    private People(String name) {
        this.name = name;
    }

    People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @MyAnnotation(value = "OhMyBug-Method")
    private String show(String nation){
        System.out.println("我的國籍是:" + nation);
        return nation;
    }
    public String display(String interest, int age) throws NullPointerException, ClassNotFoundException{
        return interest + age;
    }

    @Override
    public void info() {
        System.out.println("我是一個人");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("我是一個可愛的人");
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

獲取當前運行時類的屬性結構

/**
 * ClassName: FieldTest
 * Date:      2020/3/11 15:41
 * author:    Oh_MyBug
 * version:   V1.0
 *
 * 獲取當前運行時類的屬性結構
 */
public class FieldTest {
    @Test
    public void test1(){
        Class clazz = People.class;
        // 獲取屬性結構
        // getFields():獲取當前運行時類及其所有父類中聲明爲public訪問權限的屬性
        Field[] fields = clazz.getFields();
        for (Field f: fields){
            System.out.println(f);
        }
        System.out.println();
        // getDeclaredFields():獲取當前運行時類中聲明的所有屬性。(不包含父類中聲明的屬性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f: declaredFields){
            System.out.println(f);
        }
        /*
        輸出:
            public int Reflection.People.id
            public double Reflection.Creature.weight

            private java.lang.String Reflection.People.name
            int Reflection.People.age
            public int Reflection.People.id
         */
    }

    // 權限修飾符 數據類型 變量名
    @Test
    public void test2(){
        Class clazz = People.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f: declaredFields){
            // 1. 權限修飾符
            int modifiers = f.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "\t");
            // 2. 數據類型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");
            // 3. 變量名
            String name = f.getName();
            System.out.print(name);
            System.out.println();
        }
        /*
        輸出:
            private	java.lang.String	name
                int	age
            public	int	id
         */
    }
}

獲取運行時類的方法結構

package Reflection;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * ClassName: MethodTest
 * Date:      2020/3/11 15:58
 * author:    Oh_MyBug
 * version:   V1.0
 *
 * 獲取運行時類的方法結構
 */
public class MethodTest {
    @Test
    public void test1(){
        Class clazz = People.class;
        // getMethods():獲取當前運行時類及其所有父類中聲明爲public權限的方法
        Method[] methods = clazz.getMethods();
        for (Method m: methods){
            System.out.println(m);
        }
        System.out.println();
        // getDeclaredMethods():獲取當前運行時類中聲明的所有方法。(不包含父類中聲明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m: declaredMethods){
            System.out.println(m);
        }
        /*
        輸出:
            public int Reflection.People.compareTo(java.lang.String)
            public int Reflection.People.compareTo(java.lang.Object)
            public void Reflection.People.info()
            public java.lang.String Reflection.People.display(java.lang.String)
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            public final void java.lang.Object.wait() throws java.lang.InterruptedException
            public boolean java.lang.Object.equals(java.lang.Object)
            public java.lang.String java.lang.Object.toString()
            public native int java.lang.Object.hashCode()
            public final native java.lang.Class java.lang.Object.getClass()
            public final native void java.lang.Object.notify()
            public final native void java.lang.Object.notifyAll()

            public int Reflection.People.compareTo(java.lang.String)
            public int Reflection.People.compareTo(java.lang.Object)
            public void Reflection.People.info()
            public java.lang.String Reflection.People.display(java.lang.String)
            private java.lang.String Reflection.People.show(java.lang.String)
         */
    }

    /*
    @Xxxx
    權限修飾符 返回值類型 方法名(參數類型1 形參名1,....) throws XxxException{}
     */
    @Test
    public void test2(){
        Class clazz = People.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m: declaredMethods){
            // 1. 獲取方法聲明的註解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a: annotations){
                System.out.println(a);
            }
            // 2. 權限修飾符
            int modifiers = m.getModifiers();
            System.out.print(Modifier.toString(modifiers) + '\t');
            // 3. 返回值類型
            Class returnType = m.getReturnType();
            System.out.print(returnType.getName() + "\t");
            // 4. 方法名
            System.out.print(m.getName() + "(");
            // 5. 形參列表
            Class[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null || parameterTypes.length == 0)){
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1){
                        System.out.print(parameterTypes[i] + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i] + " args_" + i + ",");
                }
            }
            System.out.print(")");
            // 6. 拋出的異常
            Class[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0){
                System.out.print("throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i == exceptionTypes.length - 1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }
            System.out.println();
        }
        /*
        輸出:
            public	int	compareTo(class java.lang.String args_0)
            public volatile	int	compareTo(class java.lang.Object args_0)
            public	void	info()
            public	java.lang.String	display(class java.lang.String args_0,int args_1)throws java.lang.NullPointerException,java.lang.ClassNotFoundException
            @Reflection.MyAnnotation(value="OhMyBug-Method")
            private	java.lang.String	show(class java.lang.String args_0)
         */
    }
}

調用運行時類的指定結構:屬性

/*
	不理想,對屬性的要求太高
*/
@Test
public void test1() throws Exception {
    Class clazz = People.class;
    // 創建運行時類的對象
    People p = (People) clazz.getConstructor().newInstance();
    // 獲取指定的屬性:要求運行時類中屬性聲明爲public
    // 通常不採用此方式
    Field id = clazz.getField("id");
    /*
	設置當前屬性的值
	set():參數1:指明設置哪個對象的屬性  參數2:將此屬性值設置爲多少
	*/
    id.set(p, 10086);

    /*
	獲取當前屬性的值
    get():參數1:獲取哪個對象的當前屬性值
	*/
    int pid = (int) id.get(p);
    System.out.println(pid);    // 輸出:10086
}

/*
如何操作運行時類中指定的屬性
*/
@Test
public void test2() throws Exception {
    Class clazz = People.class;
    // 創建運行時類的對象
    People p = (People) clazz.getConstructor().newInstance();
    // 1. getDeclaredField(String fieldName):獲取運行時類中指定變量名的屬性
    Field name = clazz.getDeclaredField("name");
    // 2. setAccessible(boolean flag):保證當前屬性是可訪問的
    name.setAccessible(true);
    // 3. 獲取、設置指定對象的此屬性值
    name.set(p, "Tom");
    System.out.println(name.get(p)); // 輸出:Tom
}

調用運行時類的指定結構:方法

/*
如何操作運行時類中的指定的方法
*/
@Test
public void test3() throws Exception {
    Class clazz = People.class;
    // 創建運行時類的對象
    People p = (People) clazz.getConstructor().newInstance();
    /*
	1. 獲取指定的方法
    getDeclaredMethod():參數1:指明獲取方法的名稱   參數2:指明獲取的方法的形參列表
	*/
    Method show = clazz.getDeclaredMethod("show", String.class);
    show.setAccessible(true);
    /*
    invoke():參數1:方法的調用者   參數2:給方法形參賦值的實參
    invoke()的返回值即爲對應類中調用的方法的返回值
	*/
    Object returnValue = show.invoke(p, "CHN");
    System.out.println(returnValue);

    System.out.println("*************如何調用靜態方法*************");
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    // 如果調用的運行時類中的方法沒有返回值,則此invoke()返回null
    Object returnVal = showDesc.invoke(People.class);
    System.out.println(returnVal);
    /*
	輸出:
    	我的國籍是:CHN
        CHN
        *************如何調用靜態方法*************
        我是一個可愛的人
        null
	*/
}

調用運行時類的指定結構:構造

/*
如何調用運行時類中的指定的構造器
*/
@Test
public void test4() throws Exception{
    Class clazz = People.class;
    // private Person(String name)
    /*
    1. 獲取指定的構造器
    getDeclaredConstructor():參數:指明構造器的參數列表
    */
    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    // 2. 保證此構造器是可訪問的
    constructor.setAccessible(true);
    // 3. 調用此構造器創建運行時類的對象
    People per = (People) constructor.newInstance("Tom");
    System.out.println(per); // 輸出:People{name='Tom', age=0, id=0}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章