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);
}
}