基礎——Class類與反射

一、java.lang.Class類

在這裏插入圖片描述

  1. java程序編譯生成.class文件,.class文件中的內容其實就是.java中的內容,只是.class文件是給JVM看的,一般.class文件中存儲的是字節碼信息,我們能看懂是因爲Idea幫我們反編譯了
  2. java是面向對象的編程語言,任何事物都可以定義創建對象。.class文件也是一種事物,也可以定義類,也可以創建對象。
  3. java.lang.Class類,就是用來專門描述.class字節碼文件的類。Class也是有對象的,只是Class類型的對象,程序員無法創建和銷燬,是由JVM幫助我們創建和銷燬,但是我們可以獲取Class類型的文件,從而完成相關操作。

Class類型的定義
在這裏插入圖片描述
注意區別:

  1. class:定義類的關鍵字
  2. Class:類名稱,就是一個普通的類名稱

二、類的加載

前言,我們已經知道了java編譯以後會生成.class文件供JVM運行,.class文件信息會加載到內存方法區存儲.class文件的常量池(靜態區)。

  1. 第一次使用類的信息時,.class字節碼文件會被加載到內存,存儲在方法區中。
  2. JVM會爲加載到方法區的.class文件創建一個Class類型的對象,該對象被保存在堆內存中。相當於堆內存中的Class類型的對象指向了方法區中的.class文件。
  3. 一個類只會被加載一次,所以Class類型的對象只會有一個。
  4. 任意類型(基本類型/引用類型)都有Class類型的對象。

在這裏插入圖片描述

tip:Class可以被比喻爲手術刀,反射好比是對類的解刨。

三、反射

反射就是通過獲取到Class類型的對象,從而操作.class文件。

java中萬物皆對象!

對象 類型 獲取方式 執行方式
.class文件 Class 三種方式
成員變量 Field getField(…) / getFields() set(…) / get(…)
成員方法 Method getMethod(…) / getMethods() invoke()
構造方法 Constructor getConstructor(…) / getConstructors() newInstance()

1、獲取.class字節碼文件(Class類的對象)

三種方式:

  1. java.lang.Object類的成員方法

    public Class<?> getClass():獲取Class類型的對象(獲取.class字節碼文件)

  2. 使用對象的class屬性

    任意類型都有一個隱藏的class屬性:獲取Class類型的對象

  3. java.lang.Class類的靜態方法(建議使用

    public static Class<?> forName(String className)

    Class類獲取類名的方式
    (1) public String getSimpleName():只獲取類名
    (2)public String getName():獲取包名和類名

2、獲取構造方法

2.1 步驟:

  1. 獲取Class類型的對象(建議使用Class類型靜態forName方法的方式)

  2. 通過Class類型的對象獲取構造方法對象

    java.lang.Class類 成員方法
    (1)public Constructor[] getConstructores()

    a. 只能獲取public修飾的所有構造方法。
    b. 每個構造方法被封裝成一個Constuctor類型的對象,被存儲在數組中

     注意:Constructor類,專門用來描述構造方法
    

    (2)Constructor getConstructor(Class... parameterTypes)

    a. 根據參數類型獲取構造方法對象,只能獲得public修飾的構造方法。
    b. 如果不存在對應的構造方法,則會拋出 java.lang.NoSuchMethodException異常。

     注意:
     1. 參數是可變參數,調用此方法時,可以不寫參數,獲取的空參構造
     2. 可以寫參數,給定的參數必須是Class對象
    
     比如:
     參數 String name,int age
     調用此方法: String.class,int.class
    

2.2 執行構造方法

java.lang.reflect.Constructor類的成員方法

public T newInstance(Object ... params)

參數:

  1. Object...params:可變參數,可以傳遞數組,參數列表,不傳遞(執行的就是空參構造)
  2. 參數作用:對象中的成員變量,所需要的的具體數據,不傳遞使用默認值

返回值:

  1. Object類型:被創建出來的對象,提升爲Object類型
  2. 對比:假設已經獲取了滿參構造方法con,
    使用反射之前:Person p = new Person(“張三”,87);
    使用反射之後: Person p = (Person)con.newInstance(“張三”,87)

3、獲取成員方法

2.1 步驟:

  1. 獲取Class類型的對象(建議使用Class類型靜態forName方法的方式)

  2. 通過Class類型的對象獲取構造方法對象

    java.lang.Class類 成員方法
    (1)public Method[] getMethods()

    a. 只能獲取public修飾的所有構造方法。
    b. 每個構造方法被封裝成一個Method類型的對象,被存儲在數組中

     注意:Method類,專門用來描述成員方法
    

    (2)Method getMethod​(String name, 方法的參數類型... 類型.class)

    a. 根據參數類型獲取成員方法對象,只能獲得public修飾的構造方法。
    b. 如果不存在對應的成員方法(參數類型傳遞不對),則會拋出 java.lang.NoSuchMethodException異常。

     注意:
     1. 參數是可變參數,調用此方法時,可以不寫參數,獲取的空參構造
     2. 可以寫參數,給定的參數必須是Class對象
    
     比如:
     參數 String name,int age
     調用此方法: String.class,int.class
    

2.2 執行成員方法

java.lang.reflect.Method類的成員方法

public Object invoke​(Object obj, Object... args)

參數:

  1. Object:傳遞指定的對象(通過構造方法創建的對象)。如果obj=null,則表示該方法是靜態方法
  2. Object... args:可變參數,傳遞方法的參數。如果參數傳遞不對會出現java.lang.IllegalArgumentException: wrong number of arguments異常。

返回值:
基本類型會被包裝爲對應的包裝類型,如果是數組類型需要轉型,基本類型不會被包裝爲Object[]類型。

public class Animal {
    public void eat(){
        System.out.println("喫東西");
    }
    public String[] num(int i){
        return new String[]{"1","2"};
    }
}
/*
反射案例
需求:寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創建任意類的對象,並且執行其中任意方法

實現:
1. 創建配置文件
2. 反射

步驟:
1.創建一個配置文件(config.properties),存儲鍵值對
2.讀取配置文件,獲取String類型的類名和方法名稱
3.獲取Class類型的對象
4.獲取構造方法
5.獲取成員方法
 */
public class Demo {
    public static void main(String[] args) throws Exception {
        //1.創建一個配置文件(config.properties),存儲鍵值對
        Properties prop = new Properties();
        //2.讀取配置文件,獲取String類型的類名和方法名稱
        Reader reader = new FileReader("config.properties");
        prop.load(reader);
        String className = prop.getProperty("ClassName");
        //3.獲取Class類型的對象
        Class<?> c = Class.forName(className);
        //4.獲取構造方法
        Constructor<?> con = c.getConstructor();
        Animal animal = (Animal) con.newInstance();
        //5.獲取成員方法
        String methodName = prop.getProperty("MethodName");
        Method method = c.getMethod(methodName,int.class);
        Object invoke = method.invoke(animal,0);
        System.out.println(Arrays.toString((Object[]) invoke));
    }
}

config.properties配置文件

# Animal的完整類名(包名 + 類名)
ClassName=com.domain.Animal
# 調用的成員函數名稱
MethodName=num

4、總結——反射示意圖

一個類可以好比是一個人體,有心肝肺。
在這裏插入圖片描述

類加載器

類加載器是負責加載類的對象。將.class文件(硬盤)加載到內存生成Class對象。

1、類加載器的組成

  • BootstrapClassLoader 根類加載器也被稱爲引導類加載器,負責Java核心類的加載。比如System,String等。

  • ExtClassLoader 擴展類加載器負責JRE的擴展目錄中jar包的加載。在JDK中JRE的lib目錄下ext目錄

  • AppClassLoader 系統類加載器負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑。

2、繼承關係

在這裏插入圖片描述

3、類加載器的獲取和調用機制

使用 類.class.getClassLoader() 獲得加載自己的類加載器

  • 類加載器加載機制:全盤負責委託機制
  1. 全盤負責:A類如果要使用B類(不存在),A類加載器必須負責加載B類。
  2. 委託機制:A類加載器如果要加載資源B,必須詢問父類加載是否加載。 如果加載,將直接使用。 如果沒有
    機制,自己再加載。

採用全盤負責委託機制保證一個class文件只會被加載一次,形成一個Class對象

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