【Java學習】Java反射機制以及實現簡單框架(暴力反射)

Java反射機制:JAVA反射機制是在運行狀態中,對於任意一個實體類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制

反射的好處:

      1.可以在程序運行過程中,操作這些對象。

      2.可以進行解耦,提高程序的擴展性。

  • Java代碼在計算機中的三個階段

1. Sources源代碼階段:*.java被編譯成*.class字節碼文件。

2.Class類對象階段:*.class字節碼文件被類加載器加載進內存,並將其封裝成Class對象(用於描述在內存中描述字節碼文件),

Class對象將原字節碼文件中的成員變量,構造函數,方法等的做了封裝。

3.Runtime運行階段:創建對象的過程new

  •  講解獲取Class對象的方式

獲取Class對象的三種方式對應着java代碼在計算機中的三個階段:

  • 源代碼階段

Class.forName("全類名"):將字節碼文件加載進內存,返回Class對象。

  • Class類對象階段

類名.class:通過類名的屬性class獲取

  • Runtime運行時階段

對象.getClass():getClass()方法是定義在Object類中的方法。

結論:同一個字節碼文件(*.class)在一次程序運行過程中,只會被加載一次,無論通過哪一種方式獲取的Class對象都是同一個。

測試代碼:

package com.company.reflect;

import com.company.reflect.domain.Person;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 10:37
 * @Description:
 */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一:Class.forName("全類名")
        Class<?> aClass = Class.forName("com.company.reflect.domain.Person");
        System.out.println(aClass);
        //方式二:類名.class
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
        //方式三:對象.getClass()
        Person person = new Person();
        Class<? extends Person> aClass1 = person.getClass();
        System.out.println(aClass1);
        //比較 == 三個對象
        System.out.println(aClass == aClass1);
        System.out.println(personClass==aClass1);
    }
}

兩個true表示Class對象是同一個。

  •  獲取Class對象功能

(1)獲取成員變量們
		Field[] getFields() :獲取所有public修飾的成員變量
		Field getField(String name)   獲取指定名稱的 public修飾的成員變量

		Field[] getDeclaredFields()  獲取所有的成員變量,不考慮修飾符
		Field getDeclaredField(String name)
(2)獲取構造方法們
		Constructor<?>[] getConstructors()  
		Constructor<T> getConstructor(類<?>... parameterTypes)  

		Constructor<?>[] getDeclaredConstructors()  
		Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)  
(3)獲取成員方法們
		Method[] getMethods()  
		Method getMethod(String name, 類<?>... parameterTypes)  

		Method[] getDeclaredMethods()  
		Method getDeclaredMethod(String name, 類<?>... parameterTypes)
  •  Field:成員變量

先寫一個測試類

public class Person {
    private String name;
    private int age;
    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {

    }

    public Person(String name, Integer age) {
        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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    //無參方法
    public void eat(){
        System.out.println("eat...");
    }

    //重載有參方法
    public void eat(String food){
        System.out.println("eat..."+food);
    }
}
  • 獲取所有的public修飾的成員變量
//0.獲取Person對象
        Class<Person> personClass = Person.class;
        //1.獲取所有public修飾的成員變量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

 

  • 獲取特定的成員變量(public)
//2.Field getField(String name)
        Field a = personClass.getField("a");
        //獲取成員變量a 的值 [也只能獲取公有的,獲取私有的或者不存在的字符會拋出異常]
        Person person = new Person();
        Object o = a.get(person);
        System.out.println("o  value: "+o);
        //設置屬性a的值
        a.set(person,"haha");
        System.out.println(person);

 

  • 獲取全部的成員變量
//Field[] getDeclaredFields():獲取所有的成員變量,不考慮修飾符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField+" ");
        }
        System.out.println("==============================");

 

  • 獲取特定的成員變量,在這裏如果需要對private進行修改,就必須進行暴力反射,將
d.setAccessible(true);設置爲true
System.out.println("==============================");
        Field d = personClass.getDeclaredField("d");
        d.setAccessible(true);//暴力反射
        d.get(person);
        d.set(person,"222");
        System.out.println(person);

  •  普通方法獲取

獲取指定名稱的方法(不帶參數的獲取)

Class<Person> personClass = Person.class;
        //獲取指定名稱的方法
        Method eat = personClass.getMethod("eat");
        Person person = new Person();
        eat.invoke(person);//執行方法

 

獲取指定名稱的方法(帶參數獲取)

//獲取具有參數的構造方法
        Method eat1 = personClass.getMethod("eat", String.class);
        eat1.invoke(person,"fans");
        System.out.println("===============================");

 

獲取方法列表

Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);//繼承的方法也會被訪問(前提是方法是public)
        }

 

 如果設置的方法中含有私有的方法,也可以設置d.setAccessible(true);設置爲true,然後就可以訪問私有方法。

  • 構造方法

  • 獲取無參數的構造器
Class<Person> personClass = Person.class;
        //Constructor<?>[] getConstructors()
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        //Constructor<T> getConstructor(類<?>... parameterTypes)
        //獲取無參
        Constructor<Person> constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
            //利用獲取的構造器創建對象
        Person person = constructor1.newInstance();
        System.out.println(person);

 

  • 獲取有參數的構造器
//獲取有參
        Constructor<Person> constructor = personClass.getConstructor(String.class,Integer.class);
        System.out.println(constructor);
        Person person1 = constructor.newInstance("PCB",100);
        System.out.println(person1);
        //理應Class類對象進行對象的構建獲取
        Person person2 = personClass.newInstance();
        System.out.println(person2);
        //對於getDeclaredConstructor方法和getDeclaredConstructors方法,此外在構造器的對象內也有setAccessible(true);方法,並設置成true就可以操作了。

  •  設計簡單的框架,理解反射的好處

準備測試類

package com.company.reflect.domain;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 13:27
 * @Description:
 */
public class Student {
    public void sleep(){
        System.out.println("sleep...");
    }
}

 準備文件properties文件

className = com.company.reflect.domain.Student
methodName = sleep
  •  需求

寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創建任意類的對象,並且執行其中任意方法。

  • 實現

(1)配置文件 (2)反射

  • 步驟

(1)將需要創建的對象的全類名和需要執行的方法定義在配置文件中 (2)在程序中加載讀取配置文件 (3)使用反射技術來加載類文件進內存 (4)創建對象 (5)執行方法

 

package com.company.reflect.反射案例;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 13:30
 * @Description:
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        /**
         * 前提:不能改變該類的任何代碼。可以創建任意類的對象,可以執行任意方法
         */
        //1.加載配置文件
        //1.1創建Properties對象
        Properties properties = new Properties();
        //1.2加載配置文件,轉換爲一個集合
        //1.2.1獲取class目錄下的配置文件  使用類加載器
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        properties.load(resourceAsStream);
        //2.獲取配置文件中定義的數據
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //加載類到內存中
        Class<?> aClass = Class.forName(className);
        //創建對象
        Object o = aClass.newInstance();
        //獲取對象方法
        Method method = aClass.getMethod(methodName);
        //執行方法
        method.invoke(o);

    }
}

改變配置文件

className = com.company.reflect.domain.Person
methodName = eat

  • 好處

我們這樣做有什麼好處呢,對於框架來說,是人家封裝好的,我們拿來直接用就可以了,而不能去修改框架內的代碼。但如果我們使用傳統的new形式來實例化,那麼當類名更改時我們就要修改Java代碼,這是很繁瑣的。修改Java代碼以後我們還要進行測試,重新編譯、發佈等等一系列的操作。而如果我們僅僅只是修改配置文件,就來的簡單的多,配置文件就是一個實實在在的物理文件。

 

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