不瞭解java反射機制,怎麼看懂框架源碼

一、理解反射的基礎(重要指數五顆星爆表)

1.Class文件

爲什麼有

  • 實現跨平臺,一次編譯,到處運行
  • 一次編譯後的字節碼文件(class文件),可以在安裝了java虛擬機的各個操作系統上運行

是什麼

  • java源代碼編譯後的字節碼文件(.class)
  • 任何一個Class文件都對應唯一的一個類或接口的定義信息

注意:

  • Class文件應當看作是一串二進制字節流(畫外音:記住這句話,切記)
  • 包括但不限於磁盤文件、網絡、數據庫、內存或動態產生

2.Java虛擬機的類加載機制

類加載就是程序執行時,加載Class文件的

類加載機制包括5個階段:

  • 加載—>驗證—>準備—>解析—>初始化(畫外音:知道即可,暫不需要)
  • 重要的是類加載機制的加載階段

圖解類加載的加載階段(太重要了,老鐵,好好看)

如:實例化一個A類時
先將A類編譯成A.class文件,放在主機硬盤上
當執行 A a = new A(),步驟在下面
在這裏插入圖片描述
步驟如下:對應上圖序號

  • 1.類加載器(classLoad)通過一個類的全限定類名在主機硬盤找到A.class文件
  • 2.轉換爲A字節碼:將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構(包含字段方法等)
  • 3.生成A的class對象:在堆中生成一個代表這個類的java.lang.Class對象,可以通過這個對象訪問方法區的A字節碼(字段方法等)

假如不通過new對象的方式,直接拿到A的class對象是不是就可以操作A對象的字段方法呢,答案是肯定的,這個就是反射

二、反射——框架設計的靈魂

2.1是什麼

  • java的反射機制就是在程序運行過程中,動態調用對象的屬性和方法。

用上面的圖翻譯:

  • java的反射機制就是在程序運行過程中,拿到類的字節碼文件對應的class對象,從而訪問屬性方法的過程

2.2與反射相關的類

相關類 含義
java.lang.Class 對象代表一個字節碼文件,一個類
java.lang.reflect.Constructor 對象代表字節碼中構造方法,類中的構造方法
java.lang.reflect.Field 對象代表字節碼中的屬性字節碼,類中屬性
java.lang.reflect.Method 對象代表字節碼中的方法,類中的方法

總結:JAVA反射將類的各個部分映射成各個部分對應的對象

三、反射的使用

3.1獲得Class對象的三種方式

再寫一次:Class對象對應某個類

1.Class.forName()靜態方法
使用格式

  • 參數是一個字符串
  • 字符串是一個類的全限定類名,包括包名
Class c1 = Class.forName("java.lang.String");

說明:

  • c1(Class對象)對應String類
  • java.lang.String就是String類的全限定類名
  • 此方式在反射中最常用

2.對象的getClass方法

String s = "abc";
Class x = s.getClass();

說明

  • x(Class對象)對應String類
  • s的getClass方法繼承於Object
  • 此方法不常用,因爲對象已經出來了,還要反射去調用對象方法嘛,直接用對象調用就可以

3.class屬性

Class a = String.class

說明

  • a(Class對象)對應String類
  • 此方法不常用,因爲使用時需要導入相關的類,依賴性強

3.2反射調用構造方法並使用

Vip類

public class Vip {
    int no;
    String name;
    //無參構造方法
    public Vip(){}
    
    //有參構造方法
    public Vip(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
	//重寫toString()方法
    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

測試類

    public static void main(String[] args) throws Exception {
        //不使用反射機制創建對象
        Vip v1 = new Vip();
        Vip V2 = new Vip(110,"zhangsan");

        //使用反射機制創建對象
        Class c = Class.forName("com.thinkcoder.java.bean.Vip");
        
        //調用無參構造
        Object obj = c.newInstance();
        System.out.println(obj);//Vip{no=0, name='null'}
        
        //調用有參構造方法
        //第一步:先獲取構造方法
        Constructor con = c.getDeclaredConstructor(int.class,String.class);
        //第二步: 調用構造方法new對象
        Object newObj = con.newInstance(111,"lisi");
        System.out.println(newObj);//Vip{no=111, name='lisi'}
    }

說明

  • 1.java.lang.reflect.Constructor類的對象代表一個類的構造函數
  • 2.Constructor類的newInstance方法可以調用這個類的構造函數創建此類對象

3.3反射獲取屬性並賦值

Student類

public class Student {
    //屬性採用不同的訪問修飾符
    public int no;  
    private String name;
}

測試類

    public static void main(String[] args) throws Exception {
        //反射機制獲取一個類
        Class  studentClass = Class.forName("com.thinkcoder.java.bean.Student");
        Object obj = studentClass.newInstance();//obj代表Student對象

        //獲取公開屬性no
        Field noField = studentClass.getDeclaredField("no");

        //給obj對象的name屬性賦值
        /*
        * 給屬性賦值三要素
        *   1.obj對象
        *   2.no屬性
        *   3.值爲1
        * */
        noField.set(obj,1);
        System.out.println(noField.get(obj));//1

        //獲取私有屬性name
        Field nameField = studentClass.getDeclaredField("name");

        //打破封裝,使私有屬性可以訪問
        nameField.setAccessible(true);
        nameField.set(obj,"zhangsan");
        System.out.println(nameField.get(obj));//zhangsan
    }

說明

  • 1.java.lang.reflect.Field類對象代表一個類的屬性
  • 2.Field中的get(),set()方法可以獲取一個類的屬性並賦值
  • 3.set和get方法只能訪問公開屬性,訪問私有屬性需要setAccessible方法打破封裝訪問

3.4反射調用方法

com.thinkcoder.java.service.UserService類

public class UserService {
	//實現簡單登錄驗證
    public boolean login(String name,String pwd){
        if("admin".equals(name) && "123".equals(pwd)){
            return true;
        }
        return false;
    }
}

測試類

    public static void main(String[] args) throws Exception {
        //不使用反射機制調方法
        //UserService userService = new UserService();
        //userService.login("admin","123");

        //使用反射機制調用方法
        Class userService = Class.forName("com.thinkcoder.java.service.UserService");
        //創建對象
        Object obj = userService.newInstance();
        //獲取method
        Method method =  userService.getDeclaredMethod("login", String.class, String.class);
        //調用方法用invoke
        /*
        * 要素1:對象obj
        * 要素2:方法名method
        * 要素3:實參列表"admin""123"
        * 要素4:返回值retValue
        * */
        Object retValue= method.invoke(obj,"admin","123");
        System.out.println(retValue);//true
    }

說明

  • 1.java.lang.reflect.Method類對象代表一個類中的方法
  • 2.Method類中的invoke方法調用這個類的方法

3.5總結

正如之上總結,java反射將類和其各個組成部分映射成各個對應的對象,通過對應的對象調用相關的屬性方法

四、反射+配置文件

com.thinkcoder.java.service.UserService類

public class UserService {
	//實現簡單登錄驗證
    public boolean login(String name,String pwd){
        if("admin".equals(name) && "123".equals(pwd)){
            return true;
        }
        return false;
    }
}

classinfo1配置文件內容

className=com.thinkcoder.java.service.UserService

測試類

public class ResouceBundleTest {
    public static void main(String[] args) {
        //資源綁定器,只能綁定xxx.propertities文件,寫路徑時,擴展名不能寫
        ResourceBundle bundle = ResourceBundle.getBundle("classinfo1");

        String className = bundle.getString("className");
        System.out.println(className);//com.thinkcoder.java.service.UserService
    }
}

老鐵當你獲取了一個類的全限定類名之後,按照上邊的操作,可以對該類各個組成部分進行操作
注意:

  • java.util包下提供了一個資源綁定器,便於獲取屬性配置文件中的內容
  • 使用以下這種方式時,屬性配置文件xxx.properties必須放到類路徑下,即src路徑下

五、總結

反射爲什麼叫反射呢?
就是將一個類和其組成部分映射(反射)到java.reflect包下對應的類

完結,反射的內容大致如此,以後看到源碼部分時,或者學習反射更多相關東西,將補充

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