反射

1. 類的加載、連接和初始化

         系統可能在第一次使用某個類時加載該類,也可能採用預加載機制來加載某個類。

1.1 JVM和類

        當調用java命令運行某個java程序時,該命令將會啓動一個java虛擬進程,不管該java程序有多麼複雜,該程序啓動了多少個線程,他們都處於該java虛擬機的進程裏。系統出現以下情況時,JVM進程將被終止:

(1)程序運行到最後正常結束

(2)程序運行到使用System.exit()或Runtime.getRuntime().exit()代碼處介紹程序

(3)程序執行過程中遇到未捕獲的異常或錯誤而結束

(4)程序所在平臺強制結束了JVM進程

1.2 類的加載

        類加載指的是將類的class文件讀入內存,併爲之創建一個java.lang.Class對象,也就是說,當程序中使用任何類時,系統都會爲之創建一個java.lang.Class對象。

1.3 類的連接

        當類被加載之後,系統爲之生成一個對應的Class對象,接着將會進入連接階段,連接階段負責把類的二進制數據合併到JRE中。類的連接分爲如下三個階段:

(1)驗證:驗證階段用於檢驗被加載的類是否有正確的內部結構,並和其他類協調一致

(2)準備:類準備階段則負責爲類的變量分配內存,並設置默認初始值

(3)解析:將類的二進制數據中的符號引用替換成直接引用

1.4 類的初始化

        在類的初始化階段,虛擬機負責對類進行初始化,主要就是對類變量進行初始化。在java類中對類變量指定初始值有兩種方式:聲明類變量時指定初始值;使用靜態初始化塊爲類變量指定初始值。JVM初始化一個類包含如下幾個步驟:

(1)假如這個類還沒有被加載和連接,則程序先加載並連接該類

(2)假如該類的直接父類還沒有被初始化,則先初始化其直接父類

(3)假如類中有初始化語句,則系統依次執行這些初始化語句

1.5 類初始化時機

    當java程序首次通過下面6種方式來使用某個類或接口時,系統就會初始化該類或接口:

(1)創建類的實例。爲某個類創建實例的方式包括:使用new操作符來創建實例,通過反射來創建實例,通過反序列化的方式來創建實例

(2)調用某個類的類方法(靜態方法)

(3)訪問某個類或接口的類變量,或爲該類變量賦值

(4)使用反射的方式強制創建某個類或接口對應的java.lang.Class對象

(5)初始化某個類的子類。當初始化某個類的子類時,該子類的所有父類都會被初始化

(6)直接使用java.exe命令來運行某個主類。當運行某個主類時,程序會先初始化該主類

2. 類加載器

         類加載器負責將.class文件加載到內存中,併爲之生成對應的java.lang.Class對象。

2.1類加載器的組成

(1)Bootstrap ClassLoader 根類加載器

    也被稱爲引導類加載器,負責Java核心類的加載

    比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中

(2)Extension ClassLoader 擴展類加載器

    負責JRE的擴展目錄中jar包的加載。

    在JDK中JRE的lib目錄下ext目錄

(3)System ClassLoader 系統類加載器

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

3. 反射

        JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

3.1 獲得Class對象

獲取Class對象的三種方式:

(1)使用Class類的forName(String className)靜態方法。該方法需要傳入字符串參數,該字符串參數的值是某個類的全限定類名

(2)調用某個類的class屬性來獲取該類對應的Class對象

(3)調用某個對象的getClass()方法。該方法是java.lang.Object類中的一個方法,所以所有的java對象都可以調用該方法,該方法將會返回該對象所屬類對應的Class對象

package com.xupt.reflect01;

public class Reflect001 {

	public static void main(String[] args) throws Exception {
		//使用Class類的forName獲取字節碼對象
		Class c1=Class.forName("com.xupt.reflect01.Student");
		System.out.println(c1);
		//調用Student類的class屬性來獲取字節碼對象
		Class c2=Student.class;
		System.out.println(c2);
		//調用Student對象的getClass()方法獲取字節碼對象
		Student s=new Student();
		Class c3=s.getClass();
		System.out.println(c3);

	}

}
3.1 通過反射獲取構造方法並使用

        在反射機制中,把類中的成員(構造方法、成員方法、成員變量)都封裝成了對應的類進行表示。其中,構造方法使用類Constructor表示。可通過Class類中提供的方法獲取構造方法:

返回一個構造方法

    public Constructor<T>getConstructor(Class<?>... parameterTypes) 獲取public修飾, 指定參數類型所對應的構造方法

    public Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes) 獲取指定參數類型所對應的構造方法(包含私有的)

返回多個構造方法

    public Constructor<?>[]getConstructors() 獲取所有的public 修飾的構造方法

    public Constructor<?>[]getDeclaredConstructors() 獲取所有的構造方法(包含私有的)

package com.xupt.reflect02;

import java.lang.reflect.Constructor;

public class Reflect02 {

	public static void main(String[] args) throws Exception {
		//通過反射獲取字節碼對象
		Class c=Student.class;
		//public Constructor<T> getConstructor(Class<?>... parameterTypes) 
		//獲取public修飾, 指定參數類型所對應的構造方法
		Constructor ct1=c.getConstructor();  
		System.out.println(ct1);
		
		Constructor ct2=c.getConstructor(String.class,int.class);
		System.out.println(ct2);
		
		//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
		//獲取指定參數類型所對應的構造方法(包含私有的)
		Constructor ct3=c.getDeclaredConstructor(String.class,int.class,int.class);
		System.out.println(ct3);
		
		//public Constructor<?>[] getConstructors() 獲取所有的public 修飾的構造方法
		Constructor[] ct4=c.getConstructors();
		for(Constructor cr:ct4){
			System.out.println(cr);
		}
		
		//public Constructor<?>[] getDeclaredConstructors() 獲取所有的構造方法(包含私有的)
		Constructor[] con=c.getDeclaredConstructors();
		for(Constructor cc:con){
			System.out.println(cc);
		}
	}

}
3.2 通過反射獲取構造方法創建對象

獲取構造方法,步驟如下:

    1. 獲取到Class對象

    2. 獲取指定的構造方法

    3. 通過構造方法類Constructor中的方法,創建對象

        public T newInstance(Object... initargs)

package com.xupt.reflect02;

import java.lang.reflect.Constructor;

/*
 * 通過反射獲取公共的構造方法 創建對象
 */
public class Reflect03 {

	public static void main(String[] args) throws Exception {
		//獲取Student字節碼文件對象
		Class c=Student.class;
		//通過字節碼文件對象獲取 構造方法
		Constructor con=c.getDeclaredConstructor(String.class,int.class);
		//通過構造方法創建對象
		Object obj=con.newInstance("岳雲鵬",25);
		System.out.println(obj);

	}

}
3.3 通過反射的方式獲取私有構造方法創建對象

        AccessibleObject類是 Field、Method 和 Constructor 對象的父類。它提供了將反射的對象標記爲在使用時取消默認 Java 語言訪問控制檢查的能力。

        對於公共成員、默認(打包)訪問成員、受保護成員和私有成員,在分別使用 Field、Method 或 Constructor 對象來設置或獲取字段、調用方法,或者創建和初始化類的新實例的時候,會執行訪問檢查。常用方法如下:

    public void setAccessible(boolean flag) throws SecurityException

    參數值爲true則指示反射的對象在使用時應該取消 Java 語言訪問檢查。參數值爲false 則指示反射的對象應該實施 Java 語言訪問檢查。

獲取私有構造方法,步驟如下:

    1. 獲取到Class對象

    2. 獲取指定的構造方法

    3. 暴力訪問, 通過setAccessible(boolean flag)方法

    4. 通過構造方法類Constructor中的方法,創建對象

        publicT newInstance(Object... initargs)

package com.xupt.reflect02;

import java.lang.reflect.Constructor;

/*
 * 通過反射獲取私有構造方法創建對象
 */
public class Reflect04 {

	public static void main(String[] args) throws Exception {
		
		Class c=Student.class;
		//獲取私有的構造方法
		Constructor con=c.getDeclaredConstructor(String.class,int.class,int.class);
		//暴力反射取消語法檢查
		con.setAccessible(true);
		//創建對象
		Object obj=con.newInstance("孫越",45,12138);
		System.out.println(obj);

	}

}
3.4 通過反射獲取成員變量並使用

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

返回一個成員變量

    public Field getField(Stringname) 獲取指定的 public修飾的變量

    public FieldgetDeclaredField(String name) 獲取指定的任意變量

返回多個成員變量

    public Field[] getFields() 獲取所有public 修飾的變量

    public Field[]getDeclaredFields() 獲取所有的 變量 (包含私有)

package com.xupt.reflect02;

import java.lang.reflect.Field;

/*
 * 通過反射獲取成員變量並使用
 */
public class Reflect05 {

	public static void main(String[] args) throws Exception{
		
		Class c=Student.class;
		//獲取指定變量
		Field f=c.getDeclaredField("age");
		System.out.println(f);
		
		//獲取所有變量
		Field[] fs=c.getDeclaredFields();
		for(Field ff:fs){
			System.out.println(f);
		}

	}

}
3.5 通過反射,創建對象,獲取指定的成員變量,進行賦值與獲取值操作

獲取成員變量,步驟如下:

    1. 獲取Class對象

    2. 獲取構造方法

    3. 通過構造方法,創建對象

    4. 獲取指定的成員變量(私有成員變量,通過setAccessible(boolean flag)方法暴力訪問)

    5. 通過方法,給指定對象的指定成員變量賦值或者獲取值

     public void set(Object obj,Object value)

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

     public Object get(Object obj)

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

3.6 通過反射獲取成員方法並使用

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

返回獲取一個方法:

    public Method getMethod(Stringname, Class<?>... parameterTypes)

                         獲取public 修飾的方法

    public MethodgetDeclaredMethod(String name, Class<?>... parameterTypes)

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

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

返回獲取多個方法:

    public Method[] getMethods() 獲取本類與父類中所有public 修飾的方法

    public Method[] getDeclaredMethods() 獲取本類中所有的方法(包含私有的)

3.7 通過反射,創建對象,調用指定的方法

獲取成員方法,步驟如下:

1. 獲取Class對象

2. 獲取構造方法

3. 通過構造方法,創建對象

4. 獲取指定的方法

5. 執行找到的方法

    public Object invoke(Objectobj,  Object... args)

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

 

3.8 通過反射,創建對象,調用指定的private 方法

獲取私有成員方法,步驟如下:

1. 獲取Class對象

2. 獲取構造方法

3. 通過構造方法,創建對象

4. 獲取指定的方法

5. 開啓暴力訪問

6. 執行找到的方法

u public Object invoke(Objectobj,  Object... args)

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

 

3.9泛型擦除

程序編譯後產生的.class文件中是沒有泛型約束的,這種現象稱爲泛型的擦除。通過反射技術,可以向有泛型約束的集合中,添加任意類型的元素

public class ReflectTest {

    public staticvoid main(String[] args) throws Exception {

        ArrayList<Integer>list = newArrayList<Integer>();

        //添加元素到集合

        list.add(newInteger(30));

        list.add(newInteger("12345"));

        list.add(123);

        //list.add("哈哈");//因爲有泛型類型的約束

        System.out.println(list);

       

        //通過反射技術,實現添加任意類型的元素

        //1, 獲取字節碼文件對象

        //Class c = list.getClass();

        //Class c = ArrayList.class;

        Classc = Class.forName("java.util.ArrayList");

       

        //2, 找到add()方法

        // public boolean add(E e)

        MethodaddMethod = c.getMethod("add", Object.class);

       

        //3  執行add()方法

        addMethod.invoke(list,"哈哈");// list.add("哈哈");

        System.out.println(list);

    }

}

1.1    反射配置文件

通過反射配置文件,運行配置文件中指定類的對應方法

讀取Peoperties.txt文件中的數據,通過反射技術,來完成Person對象的創建

Peoperties.txt文件內容如下:

className=cn.itcast_01_Reflect.Person

methodName=method5

 

讀取配置文件,調用指定類中的對應方法

public class ReflectTest2 {

    public staticvoid main(String[] args)

            throws Exception {

        // 通過Properties集合從文件中讀取數據

        Propertiesprop = newProperties();

        // 讀取文件中的數據到集合中

        prop.load(newFileInputStream("properties.txt"));

        // 獲取鍵所對應的值

        StringclassName = prop.getProperty("className");

        System.out.println(className);

 

        // 1,獲取Person.class 字節碼文件對象

        Classc = Class.forName(className);

        // 2,獲取構造方法

        // public Person(String name, int age, String address)

        Constructorcon = c.getConstructor(String.class, int.class, String.class);

 

        // 3,創建對象

        Objectobj = con.newInstance("小明", 20, "中國");

        System.out.println(obj);

 

        // 4,獲取指定的方法

        // private void method5(){}

        StringmethodName = prop.getProperty("methodName");

        Methodm5 = c.getDeclaredMethod(methodName, null);

        // 5,開啓暴力訪問

        m5.setAccessible(true);

        // 6,執行找到的方法

        m5.invoke(obj, null);

    }

}



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