java反射--框架設計的靈魂

反射初識

平時寫代碼的時候,我們都會用到框架,框架的使用可能並不需要使用到反射,但是如果我們需要去寫框架的時候,就需要我們對反射又一個深刻的理解。
使用的前提條件:必須先得到代表的字節碼的Class,Class類用於表示.class文件(字節碼)

框架:半成品軟件,可以在框架的基礎上進行軟件開發,簡化代碼
反射:將類的各個組成部分封裝爲其他對象,這就是反射機制

反射的好處:
1:可以在程序運行過程中,操作這些對象
2:可以解耦,提高程序的可擴展性

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法.所以先要獲取到每一個字節碼文件對應的Class類型的對象.

或者可以這樣理解,反射就是把java類中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量、方法、構造方法等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)
如圖是類的正常加載過程。
熟悉一下加載的時候:Class對象的由來是將class文件讀入內存,併爲之創建一個Class對象。
在這裏插入圖片描述
第一個階段:
我們程序員寫的.java文件經過javac編譯,變成.class文件(也就是字節碼文件)。第一階段的代碼都在硬盤上。

第二個階段:
通過類加載起器,將字節碼文件加載到內存中,爲了描述這個字節碼文件,(java萬物皆對象)用Class類對象來描述我們的字節碼。因此將我們的成員變量、方法、構造方法等等信息變成對於的不同對象,來組成Class類對象。

第三個階段:
當我們在new對象的時候,實際上就是通過Class裏面的對象來創建的。創建的對象是在內存裏。是在運行階段完成的。
獲取class對象的方式:
1:Class.forName(“全類名(包名.class)”):將字節碼文件加載進內存,返回Class對象
多用於配置文件,將類名定義在配置文件,讀取文件加載類

2:類名.class:通過類名的屬性class獲取
多用於參數的傳遞

3:對象.getClass():getClass()在Object類中定義
多用於對象的獲取字節碼的方式

驗證:
三個對象是否是一個對象,即對象地址相同

Class cls1 = Class.forName("com.company.domain.Person");
        System.out.println(cls1);
        Class cls2 = Person.class;
        System.out.println(cls2);
        Person person = new Person();
        Class cls3 = person.getClass();
        System.out.println(cls3);

        //驗證三個對象是否是一個對象,即對象地址相同
        System.out.println(cls1 ==cls3);
        System.out.println(cls1 ==cls2);

        Student student = new Student();
        Class cls4 = student.getClass();
        System.out.println(cls1 == cls4);

在這裏插入圖片描述
結論:
同一個字節碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論是通過哪一種方式獲取的class對象,都是同一個。

接下來我們來了解,怎麼使用Class對象,那麼就需要首先了解Class的功能:
一:獲取功能

  1. 獲取成員變量( field )們
    Field[ ] getFields() :獲取public 修飾的成員變量。
    Field getField(String name)

    Field[ ] getDeclaredFields() 獲取private 修飾的成員變量。
    Field getDeclaredField(String name)

  2. 獲取構造方法們
    Constructor<?>[] getConstructors()
    Constructor getConstructor(類<?> . . . parameterTypes)

    Constructor<?>[] getDeclaredConstructors()
    Constructor getDeclaredConstructor(類<?> . . . parameterTypes)

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

    Methods[ ] getDeclaredMethods()
    Method getDeclaredMethod(String name,類<?> . . . parameterTypes)

  4. 獲取類名
    String getName()

Field常見操作:

1:設置值
field.set(Object obj ,Object value);
2:獲取值
field.get(Object obj);
3:忽略訪問權限修飾符的安全檢查
field.setAccessible(true); // 暴力反射
⚠️
field.get(Object obj); //獲取的是field的具體值
Field getDeclaredField(String name) //獲取field的包名+名字
對於private修飾的field,包括後面的Constructor,Method,都可用對應的getDeclaredXX(),同時設置Xx.setAccessible(true)就可以暴力反射

例:

        Field name = personClass.getDeclaredField("name");
        name.setAccessible(true);
        Object nameval = name.get(person);
        System.out.println(name);
        System.out.println(nameval);
        

輸出:

private java.lang.String com.company.domain.Person.name
null

Constructor:構造方法

創建對象:
T newInstance(Object … initargs)
1:獲取constructor

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //1:獲取Person的class對象
        Class personClass = Person.class;
        Constructor constructor = personClass.getConstructor(String.class,int.class);
        System.out.println(constructor);

    }
}

輸出:

public com.company.domain.Person(java.lang.String,int)

Process finished with exit code 0

2:用構造器創建對象

//創建對象
        Object person1 = constructor.newInstance("張三",23);
        System.out.println(person1);

打印結果:

Person{name='張三', age=23, address='null', id=0}

成員方法功能

1:獲取方法
2:執行方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //1:獲取指定方法
        Class personClass = Person.class;
        Method eat = personClass.getMethod("eat");
        //帶參數的方法
        Method eatfood = personClass.getMethod("eat",String.class);
        System.out.println(eat);
        //執行方法
        Person person = new Person();
        eat.invoke(person);
        eatfood.invoke(person,"麪包");

    }
}

打印結果

public void com.company.domain.Person.eat()
eat....
eat....麪包

獲取所有public方法:

//獲取所有public方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods){
            System.out.println(method);
        }

打印結果(包含父類所有的public方法~ ):

public java.lang.String com.company.domain.Person.getName()
public java.lang.String com.company.domain.Person.toString()
public void com.company.domain.Person.setName(java.lang.String)
public int com.company.domain.Person.getId()
public java.lang.String com.company.domain.Person.getAddress()
public void com.company.domain.Person.eat()
public void com.company.domain.Person.eat(java.lang.String)
public void com.company.domain.Person.setAddress(java.lang.String)
public void com.company.domain.Person.setId(int)
public int com.company.domain.Person.getAge()
public void com.company.domain.Person.setAge(int)
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 final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
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()

Process finished with exit code 0

獲取類名

String classname = personClass.getName();
System.out.println("======"+classname);

打印結果

======com.company.domain.Person

反射案例

需求:寫一個框架,可以幫助我們創建任意類的對象,並且執行其中任意方法

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

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以創建任意類的對象,並且執行其中任意方法
        //在程序中加載讀取配置文件,轉換爲一個集合
        Properties properties = new Properties();
        //獲取class目錄下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        properties.load(is);

        //獲取配置文件中定義的數據
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //使用反射技術來加載該類進內存
        Class cls = Class.forName(className);
        //創建對象
        Constructor constructor = cls.getConstructor();
        Object obj = constructor.newInstance();
        //獲取方法
        Method method =cls.getMethod(methodName);
        //執行方法
        method.invoke(obj);

    }
}

在這裏插入圖片描述

框架修改的是配置文件,有什麼好處呢,有什麼區別,爲什麼要這麼做?
因爲如果未來代碼量龐大的時候,你修改類文件的話,就需要你重新測試,重新編譯,上線。
但是如果僅僅修改量配置文件的話,它只是個物理文件,改完就完事了,不會那麼麻煩,而且這種方式可以使得程序有了很好的可擴展性。

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