天天看點

java學習筆記(10)類加載器和反射一、類加載器二、反射

文章目錄

  • 一、類加載器
    • 1.三種類加載器
    • 2.類加載器的内容
    • 3.類加載器的雙親委派模型
  • 二、反射
    • 1.反射的定義
    • 2.反射的基本應用
      • (1)擷取Class類的對象(即得到相應類的位元組碼)
      • (2)關于構造方法的反射
      • (3)關于成員變量的反射
      • (4)關于成員方法的反射

一、類加載器

兩個類是同一個類 = 兩個類用的類加載器相同 + 類本身的全限定名相同

1.三種類加載器

  • Bootstrap ClassLoader 根類加載器(引導類加載器)

    引導類加載器(由C++語言編寫)由作業系統的本地代碼來實作,這個類加載器沒有父類加載器。之是以會出現引導類加載器,是因為其他兩個類加載器也需要類加載器來加載。它負責Java核心類的加載,比如System,String等。

  • Extension ClassLoader 擴充類加載器

    擴充類加載器(由Java語言編寫)主要負責擴充路徑下的代碼,負責JRE的擴充目錄中jar包的加載。它的父類加載器是根類加載器。

  • System ClassLoader 系統類加載器

    系統類加載器(由Java語言編寫)也稱為應用類加載器(APPClassLoader),這兩個是等價的。負責在JVM啟動時加載來自java指令的class檔案,以及classpath環境變量所指定的jar包和類路徑。它的父類加載器是擴充類加載器。

  • 使用者自定義類加載器

三種類加載器的關系圖如下:

java學習筆記(10)類加載器和反射一、類加載器二、反射

2.類加載器的内容

  • 除了根類加載器以外,其他的類加載器都有且隻有一個父類加載器。
  • 當生成一個自定義的類加載器執行個體時,如果沒有指定它的父加載器,那麼系統類加載器将成為該類加載器的父類加載器。
  • 類加載器隻負載它的“祖先”無法加載的類。(雙親委派模型)

    比如,若系統類加載器需要加載一個類,它首先委托它的父類加載器(擴充類加載器),然後擴充類加載器再委托它的父類加載器(根類加載器),如果父類加載器不能加載此類,那麼子類加載器就會在自己的庫中查找這個類,這樣也使得安全性大大提升。

3.類加載器的雙親委派模型

(1)定義

在類加載的時候,系統會首先判斷目前類是否被加載過。已經被加載的類會直接傳回,否則才會嘗試加載。加載的時候,首先會把該請求委派該父類加載器的 loadClass() 處理,是以所有的請求最終都應該傳送到頂層的啟動類加載器 BootstrapClassLoader 中。當父類加載器無法處理時,才由自己來處理。當父類加載器為null時,會使用啟動類加載器 BootstrapClassLoader 作為父類加載器。

java學習筆記(10)類加載器和反射一、類加載器二、反射

(2)雙親委派模型的優點

  1. 雙親委派模型保證了Java程式的穩定運作,可以避免類的重複加載(JVM 區分不同類的方式不僅僅根據類名,相同的類檔案被不同的類加載器加載産生的是兩個不同的類)。
  2. 保證了 Java 的核心 API 不被篡改。

二、反射

1.反射的定義

  • 反射就是把Java類中的各種成分(成員變量,成員方法,構造器等)映射成相應的Java類。
  • JAVA反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為java語言的反射機制。
  • 反射一般會涉及如下類:Class(表示一個類的類)、Field(表示屬性的類)、Method(表示方法的類)和Constructor(表示類的構造器的類)。

2.反射的基本應用

寫在前面,對于IDEA使用反射時,要在File->Settings->Build,Execution,Deployment->Compiler->Java Compiler中的Additional command line parameters: 後面填上 -parameters,點選OK。

java學習筆記(10)類加載器和反射一、類加載器二、反射

(1)擷取Class類的對象(即得到相應類的位元組碼)

  • 方法一:getClass()方法
Person p = new Person();
Class c = p.getClass();
           
  • 方法二:通過 類名.class 擷取到位元組碼檔案對象(任意資料類型都具備一個class靜态屬性)
Class c2 = Person.class;
           
  • 方法三:通過調用Class類中的靜态方法forName()方法将類名作為字元串傳遞給Class類中的靜态方法forName即可)
Class c3 = Class.forName("Person");
           

注: 前兩種方法必須明确Person類型,方法三是指定這種類型的字元串就行。(方法三擴充更強,我不需要知道你的類.我隻提供字元串,按照配置檔案加載就可以了)

首先寫好了一個Reflect類,然後利用方法三去獲得Class類的對象。

public class Reflect {
    public String name;
    public int age;
    private String address;
    public Reflect(){
        System.out.println("空參構造方法");
    }

    public Reflect(String name){
        this.name = name;
        System.out.println("帶有String的構造方法");
    }

    private Reflect(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("帶有String,int的私有構造方法");
    }

    public Reflect(String name ,int age,String address){
            this.name = name;
            this.age = age;
            this.address = address;
            System.out.println("帶有String,int,String的構造方法");
        }

        public void method1(){
            System.out.println("沒有傳回值沒有參數的方法");
    }

    public void method2(String name){
            System.out.println("沒有傳回值有參數的構造方法"+name);
    }

    public int method3(){
        System.out.println("有傳回值,沒有參數的構造方法");
        return 1;
    }

    public String method4(String name){
        System.out.println("有傳回值,有參數的方法");
        return "參數為:"+name;
    }

    public void method5(){
        System.out.println("私有方法");
    }

    public String toString(){
        return "Reflect [name=" + name + ", age=" + age + ", address=" + address+ "]";
    }
}
           
public class Demo{
    public static void main(String[] args){
        Class c = Class.forName("Reflect");
        System.out.println(c);
    }
}
           

運作結果:

java學習筆記(10)類加載器和反射一、類加載器二、反射

将ClassNotFoundException異常抛出後,運作結果:

java學習筆記(10)類加載器和反射一、類加載器二、反射

(2)關于構造方法的反射

(2)-1通過反射擷取構造方法

  • 傳回一個構造方法
    • public Constructor getConstructor(Class<?>… parameterTypes) 擷取由public修飾, 且指定參數類型對應的構造方法。
    • public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 擷取指定參數類型所對應的構造方法(包含私有的)。
  • 傳回多個構造方法
    • public Constructor<?>[ ] getConstructors() 擷取所有的public 修飾的構造方法。
    • public Constructor<?>[ ] getDeclaredConstructors() 擷取所有的構造方法(包含私有的)。

舉例如下(需要導包):

import java.lang.reflect.Constructor;
public class Demo{
    public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException {

        Class c = Class.forName("Reflect");
        System.out.println("----------擷取所有的構造方法--------------");
        Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con : cons){
            System.out.println(con);
        }
        
        System.out.println("-----------擷取一個構造方法(無參)-------------");
        Constructor con1 = c.getConstructor(null);
        System.out.println(con1);
        
        System.out.println("-----------擷取一個構造方法(有參)-------------");
        Constructor con2 = c.getConstructor(String.class);
        System.out.println(con2);
        
        System.out.println("-----------擷取一個全部構造方法(包括私有)(有參)-------------");
        Constructor con3 = c.getDeclaredConstructor(String.class,int.class);
        System.out.println(con3);
    }
}
           

運作結果:

java學習筆記(10)類加載器和反射一、類加載器二、反射

(2)-2通過反射方式,擷取構造方法(public權限),建立對象

  • 步驟:
  1. 擷取到Class對象(forName())
  2. 擷取指定的構造方法(利用(2)-1)
  3. 通過構造方法類Constructor中的方法,建立對象

    public T newInstance(Object… initargs)

  • 舉例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException, 
            NoSuchMethodException, IllegalAccessException, 
            InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class, int.class, String.class);
       Object obj = con.newInstance("小東",22,"西安");
        System.out.println("obj");
    }
}
           
  • 運作結果:擷取的哪個構造方法,就用哪個構造方法建立對象,建立對象時用newInstance()
java學習筆記(10)類加載器和反射一、類加載器二、反射

(2)-3通過反射方式,擷取私有構造方法,建立對象

  • 要想擷取私有構造方法,并用私有構造方法來建立對象,首先就要取消java的權限修飾符(public、private等)。AccessibleObject 類是 Field、Method 和 Constructor 對象的父類。它提供了将反射的對象标記為在使用時取消預設 Java 語言通路控制檢查的能力。
  • 步驟
  1. 擷取到Class對象
  2. 擷取指定的構造方法
  3. 暴力通路, 通過setAccessible(boolean flag)方法
  4. 通過構造方法類Constructor中的方法,建立對象

    public T newInstance(Object… initargs)

  • 舉例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getDeclaredConstructor(String.class, int.class);
        con.setAccessible(true);             //是否取消 Java 語言通路檢查:true
        Object obj = con.newInstance("小東",22);
        System.out.println("obj");
    }
}
           
  • 運作結果
    java學習筆記(10)類加載器和反射一、類加載器二、反射

(3)關于成員變量的反射

(3)-1通過反射擷取成員變量

在反射機制中,把類中的成員變量使用類Field表示。可通過Class類中提供的方法擷取成員變量:

  • 傳回一個成員變量
    • public Field getField(String name) 擷取指定的 public修飾的變量。
    • public Field getDeclaredField(String name) 擷取指定的任意變量。
  • 傳回多個成員變量
    • public Field[] getFields() 擷取所有public修飾的變量。
    • public Field[] getDeclaredFields() 擷取所有的 變量 (包含私有)。

舉例如下:

import java.lang.reflect.Field;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException {
        Class c = Class.forName("Reflect");
        System.out.println("---------擷取多個變量(包括私有)--------");
        Field[] f = c.getDeclaredFields();
        for(Field field : f){
            System.out.println(field);
        }

        System.out.println("---------擷取一個變量--------");
        Field ageField = c.getField("age");
        System.out.println(ageField);
        
        System.out.println("---------擷取一個變量(私有)--------");
        Field addressField = c.getDeclaredField("address");
        System.out.println(addressField);

    }
}

           

運作結果:

java學習筆記(10)類加載器和反射一、類加載器二、反射

(3)-2通過反射擷取成員變量,并進行指派與擷取值操作

  • 擷取成員變量,步驟如下:
  1. 擷取Class對象
  2. 擷取構造方法
  3. 通過構造方法,建立對象
  4. 擷取指定的成員變量(私有成員變量,通過setAccessible(boolean flag)方法暴力通路)
  5. 通過方法,給指定對象的指定成員變量指派或者擷取值

    5.1public void set(Object obj, Object value)

    在指定對象obj中,将此 Field 對象表示的成員變量設定為指定的新值

    5.2public Object get(Object obj)

    傳回指定對象obj中,此 Field 對象表示的成員變量的值

  • 舉例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class);
        Object obj = con.newInstance("小明");
        Field nameField = c.getField("name");
        Field ageField = c.getField("age");
        Field addressField = c.getDeclaredField("address");
        addressField.setAccessible(true);        //取消私有成員變量addressField的Java 語言通路檢查
        //通過方法,擷取指定對象的指定成員變量的值
        System.out.println("------------取值--------------");
        System.out.println("name = "+ nameField.get(obj));
        System.out.println("age = "+ ageField.get(obj));
        System.out.println("address = "+ addressField.get(obj));
        //指派
        System.out.println("------------指派--------------");
        ageField.set(obj, 23);
        addressField.set(obj, "大雁塔");
        System.out.println("name = "+ nameField.get(obj));
        System.out.println("age = "+ ageField.get(obj));
        System.out.println("address = "+ addressField.get(obj));
    }
}

           
  • 運作結果:
    java學習筆記(10)類加載器和反射一、類加載器二、反射

(4)關于成員方法的反射

(4)-1通過反射擷取成員方法

在反射機制中,把類中的成員方法使用類Method表示。可通過Class類中提供的方法擷取成員方法:

  • 傳回擷取一個方法:
    • public Method getMethod(String name, Class<?>… parameterTypes)

      擷取public 修飾的方法

    • public Method getDeclaredMethod(String name, Class<?>… parameterTypes)

      擷取任意的方法,包含私有的

      (參數1: name 要查找的方法名稱; 參數2: parameterTypes 該方法的參數類型)

  • 傳回擷取多個方法:
    • public Method[] getMethods() 擷取本類與父類中所有public 修飾的方法
    • public Method[] getDeclaredMethods() 擷取本類中所有的方法(包含私有的)

舉例如下:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        System.out.println("---------擷取多個方法--------------");
       Method [] methods = c.getDeclaredMethods();
       for(Method m : methods){
           System.out.println(m);
       }

        System.out.println("---------擷取一個方法--------------");
        Method method = c.getMethod("method1", null);
        System.out.println(method);
        method = c.getMethod("method4", String.class);
        System.out.println(method);

        System.out.println("---------擷取一個方法(私有)--------------");
        method = c.getDeclaredMethod("method5", null);
        System.out.println(method);
    }
}

           

結果如下:

java學習筆記(10)類加載器和反射一、類加載器二、反射

(4)-2通過反射擷取成員方法,使用成員方法

  • 擷取成員方法,步驟如下:
  1. 擷取Class對象
  2. 擷取構造方法
  3. 通過構造方法,建立對象
  4. 擷取指定的方法
  5. 執行找到的方法

    5.1public Object invoke(Object obj, Object… args)

    執行指定對象obj中,目前Method對象所代表的方法,方法要傳入的參數通過args指定。

  • 舉例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo{
    public static void main (String[] args) throws ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("Reflect");
        Constructor con = c.getConstructor(String.class, int.class, String.class);
        Object obj = con.newInstance("小東", 23, "西安");
        
        System.out.println("-----------調用方法------------");
        Method m4 = c.getMethod("method4",String.class);
        Object result = m4.invoke(obj,"你好呀");
        System.out.println("result = " + result);
        
        System.out.println("-----------調用私有方法------------");
        Method m5 = c.getDeclaredMethod("method5",null);
        m5.setAccessible(true);
        m5.invoke(obj,null);
    }
}


           
  • 運作結果:
java學習筆記(10)類加載器和反射一、類加載器二、反射