天天看點

JavaSE-類加載過程及反射一、類加載過程二、反射

目錄

一、類加載過程

1.裝載階段

1.1執行過程

    1.2.類加載器

1.3.雙親委派模型

1.4 類加載時機

2. 連結階段

2.1驗證階段

2.2準備階段

2.3解析階段

3. 初始化階段

二、反射

1.定義

2.用途

3.步驟

4.代碼實作

一、類加載過程

1.裝載階段

1.1執行過程

以Main.java為例

package com.company;

public class Main {

    public static void main(String[] args) {
	    String str1 = "123";
	    int a = 10;
    }
}
           

其中定義了兩個變量,首先利用javac Main.java進行編譯生成Main.class檔案

JavaSE-類加載過程及反射一、類加載過程二、反射

然後利用javap -verbose指令檢視反彙編

JavaSE-類加載過程及反射一、類加載過程二、反射

 其中首先由一個4位元組大小的魔數來辨別檔案類型。

上圖中的minor version為次版本号,major version表示主版本号,分别占用2位元組。

Constant pool為常量池,其大小不固定,由定義的變量來決定。

    1.2.類加載器

   Main類經過javac編譯後,生成.class檔案儲存下來,然後經過類加載器加載類至記憶體,生成java.lang.Class類的執行個體,這個執行個體就是程式通路這個類的入口,通過這個class執行個體的newInstance方法即可得到這個類的執行個體對象。

      JVM中的類的加載器主要有三種:啟動類加載器,拓展類加載器,應用類加載器。

JavaSE-類加載過程及反射一、類加載過程二、反射
  •      啟動類加載器(Bootstrap classLoader):又稱為引導類(啟動類)加載器,由C++編寫,無法通過程式得到。主要負責加載JAVA中的一些核心類庫,主要是位于<JAVA_HOME>/lib/rt.jar中。
  •      拓展類加載器(Extension classLoader):主要加載JAVA中的一些拓展類,位于<JAVA_HOME>/lib/ext中,是啟動類加載器的子類。
  •      應用類加載器(System classLoader):    又稱為系統類(應用類)加載器,主要用于加載CLASSPATH路徑下我們自己寫的類,是拓展類加載器的子類。

如果生成的class檔案不在這三個類加載器路徑下,它首先會由子加載器依次往父加載器去找,最後找不到就會報錯ClassNotFoundException,加載失敗。

如果在這些路徑下,通過Class.forName("com.company.Main"),通過全路徑加載進來。然後用Student.class.getClassLoader()得到它的類加載器,得到的是AppClassLoader(即系統類加載器),如果用Student.class.getClassLoader().getParent()得到的是它的父加載器ExtClassLoader(即拓展類加載器),然後用Student.class.getClassLoader().getParent().getParent()得到将會是Null,因為啟動類加載器是用C++寫的,無法通過程式直接得到。

例如:

class TestClass{

}

public class Main {

    public static void main(String[] args) {
        TestClass testClass = new TestClass();
        System.out.println(testClass.getClass().getClassLoader());
        System.out.println(testClass.getClass().getClassLoader().getParent());
        String s = new String();
        System.out.println(s.getClass().getClassLoader());
    }
}
           

輸出:

JavaSE-類加載過程及反射一、類加載過程二、反射

TestClass為自己定義的類,使用應用加載器加載,它的父類就是擴充加載器,String類使用的Bootstrap加載器,它由c++實作,是以得到的隻能是null。

從子類到父類流程是:從子類到父類查找之前是否加載過這個類,如果加載過,就傳回目前類的Class對象,若沒有加載過,委托給父類加載器,查找是否加載過。

若都沒有加載過目前類,從啟動類開始嘗試加載(有沒有在加載路徑下),若加載失敗就委托給子類加載器進行加載。

1.3.雙親委派模型

這幾種類加載器的層次關系,稱為類加載器的雙親委派模型。該模型要求除了頂層的啟動類加載器外,其它的類加載器都要有自己的父類加載器。簡單描述其過程:

如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此。是以所有的加載請求最終都應該傳送到頂層的啟動類加載器中,隻有當父加載器回報自己無法完成這個加載請求(它的搜尋範圍中沒有找到所需的類)時,子加載器才會嘗試自己去加載。

其優點:

(1).避免類的重複加載

如需要定義一個 System 的 Class 對象,并且隻需要一份,如果不使用委托機制,而是自己加載自己的,那麼類 A 列印的時候就會加載一份 System 位元組碼,類 B 列印的時候又會加載一份 System 位元組碼。而使用委托機制就可以有效的避免這個問題。

(2).安全性

如果在String類中嵌套了病毒,若沒有用該模型直接加載的話,病毒就會攻擊jvm,造成破壞,是以像String這種系統類都是由Bootstrap加載器去加載的,保證了其安全性。

1.4 類加載時機

  • new對象時
  • 調用靜态變量/方法(在反彙編中,invokestatic指令專門用于調用靜态方法變量的,invokespecial指令用于調用構造方法的,invokevirtual指令用于調用執行個體方法變量的)
  • 擷取目前類的Class對象

2. 連結階段

2.1驗證階段

該階段的任務是為了保證Class檔案的位元組流中包含的資訊符合目前虛拟機要求,如魔數,jdk版本号,各種資料驗證等,并且不會危害虛拟機自身的安全。

2.2準備階段

為類變量配置設定記憶體并設定類變量(靜态變量)初始值(靜态變量的預設值)的階段,這些變量所使用的記憶體都将在方法區中進行配置設定。

private static int a = 10;

2.3解析階段

将符号引用解析為直接引用的過程。

例如:

class test{
    String str = "a";
}
           

str内容對應的位址被儲存在磁盤上,通過磁盤上的位址去通路該内容就是符号引用,而通過堆去通路就是直接引用,解析的作用就是進行二者的轉換。

3. 初始化階段

給靜态變量進行指派操作。

二、反射

1.定義

Java的反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性;這種動态擷取資訊以及動态調用對象方法的功能稱為java語言的反射機制。

2.用途

在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是隻對系統應用開放,這時候就可以利用Java的反射機制通過反射來擷取所需的私有成員或是方法。當然,也不是所有的都适合反射,有的案例通過反射得到的結果與預期不符。閱讀源碼發現,經過層層調用後在最終傳回結果的地方對應用的權限進行了校驗,對于沒有權限的應用傳回值是沒有意義的預設值,否則傳回實際值起到保護使用者的隐私目的,是以如果源碼中明确進行了權限驗證,而你的應用又無法獲得這個權限的話,建議就不要浪費時間反射了。

3.步驟

①擷取Class對象

  • Class c = People.class;
  • Class c = Class.forName("People");
  • People p = new People();  Class c = p.getClass();

②擷取目前類的構造函數

Constructor con = c.getConstructor();

③通過new對象進行構造函數生成

4.代碼實作

①無參方法反射:

package com.company;


import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class people{
    private people(){
        System.out.println("構造函數");
    }

    public void eat(){
        System.out.println("eating");
    }
}

public class Main {

    public static void main(String[] args) {
        //擷取目前類的Class對象
        Class<people> peopleClass = people.class;
        try {
            //擷取無參構造函數,getDeclaredConstructor在private,public,protected中查找,getConstructor在public方法中查找
            Constructor constructor = peopleClass.getDeclaredConstructor();
            //設定為可通路權限
            constructor.setAccessible(true);
            //根據無參構造函數new對象
            Object object = constructor.newInstance();
            //根據對象用用函數
            //1.擷取哪個方法
            Method method = peopleClass.getMethod("eat");
            //2.根據對象object調用方法
            method.invoke(object);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
           

其中:getDeclaredConstructor在private,public,protected中查找,getConstructor在public方法中查找,私有的構造方法用getConstructor去調用會報錯,并且要給constuctor對象設定通路權限。

②屬性修改反射

package com.company;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class people{
    private String name;

    private people(){
        System.out.println("構造函數");
    }

    public void eat(){
        System.out.println("eating");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Main {

    public static void main(String[] args) {
        //擷取目前類的Class對象
        Class<people> peopleClass = people.class;
        try {
            //擷取無參構造函數,getDeclaredConstructor在private,public,protected中查找,getConstructor在public方法中查找
            Constructor constructor = peopleClass.getDeclaredConstructor();
            //設定為可通路權限
            constructor.setAccessible(true);
            //根據無參構造函數new對象
            Object object = constructor.newInstance();
            //根據對象用用函數
            //1.擷取哪個屬性
            Field field = peopleClass.getDeclaredField("name");
            //2.進行修改
            field.setAccessible(true);
            field.set(object,"tom");
            System.out.println(field.get(object));

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
           

③有參方法

package com.company;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class people{
    private String name;

    private people(String name){
        this.name = name;
    }

    private void eat(Integer a){
        System.out.println(a+"個人在eating");
    }

}

public class Main {

    public static void main(String[] args) {
        //擷取目前類的Class對象
        Class<people> peopleClass = people.class;
        try {
            //擷取無參構造函數,getDeclaredConstructor在private,public,protected中查找,getConstructor在public方法中查找
            Constructor constructor = peopleClass.getDeclaredConstructor(String.class);
            //設定為可通路權限
            constructor.setAccessible(true);
            //根據無參構造函數new對象
            Object object = constructor.newInstance("jack");
            //根據對象用用函數
            Method method = peopleClass.getDeclaredMethod("eat",Integer.class);
            method.setAccessible(true);
            method.invoke(object,10);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
           
JavaSE-類加載過程及反射一、類加載過程二、反射