文章目錄
- 一、類加載器
-
- 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包和類路徑。它的父類加載器是擴充類加載器。
- 使用者自定義類加載器
三種類加載器的關系圖如下:
2.類加載器的内容
- 除了根類加載器以外,其他的類加載器都有且隻有一個父類加載器。
- 當生成一個自定義的類加載器執行個體時,如果沒有指定它的父加載器,那麼系統類加載器将成為該類加載器的父類加載器。
-
類加載器隻負載它的“祖先”無法加載的類。(雙親委派模型)
比如,若系統類加載器需要加載一個類,它首先委托它的父類加載器(擴充類加載器),然後擴充類加載器再委托它的父類加載器(根類加載器),如果父類加載器不能加載此類,那麼子類加載器就會在自己的庫中查找這個類,這樣也使得安全性大大提升。
3.類加載器的雙親委派模型
(1)定義
在類加載的時候,系統會首先判斷目前類是否被加載過。已經被加載的類會直接傳回,否則才會嘗試加載。加載的時候,首先會把該請求委派該父類加載器的 loadClass() 處理,是以所有的請求最終都應該傳送到頂層的啟動類加載器 BootstrapClassLoader 中。當父類加載器無法處理時,才由自己來處理。當父類加載器為null時,會使用啟動類加載器 BootstrapClassLoader 作為父類加載器。
(2)雙親委派模型的優點
- 雙親委派模型保證了Java程式的穩定運作,可以避免類的重複加載(JVM 區分不同類的方式不僅僅根據類名,相同的類檔案被不同的類加載器加載産生的是兩個不同的類)。
- 保證了 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。
(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);
}
}
運作結果:
将ClassNotFoundException異常抛出後,運作結果:
(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);
}
}
運作結果:
(2)-2通過反射方式,擷取構造方法(public權限),建立對象
- 步驟:
- 擷取到Class對象(forName())
- 擷取指定的構造方法(利用(2)-1)
-
通過構造方法類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()
(2)-3通過反射方式,擷取私有構造方法,建立對象
- 要想擷取私有構造方法,并用私有構造方法來建立對象,首先就要取消java的權限修飾符(public、private等)。AccessibleObject 類是 Field、Method 和 Constructor 對象的父類。它提供了将反射的對象标記為在使用時取消預設 Java 語言通路控制檢查的能力。
- 步驟
- 擷取到Class對象
- 擷取指定的構造方法
- 暴力通路, 通過setAccessible(boolean flag)方法
-
通過構造方法類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");
}
}
- 運作結果
(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);
}
}
運作結果:
(3)-2通過反射擷取成員變量,并進行指派與擷取值操作
- 擷取成員變量,步驟如下:
- 擷取Class對象
- 擷取構造方法
- 通過構造方法,建立對象
- 擷取指定的成員變量(私有成員變量,通過setAccessible(boolean flag)方法暴力通路)
-
通過方法,給指定對象的指定成員變量指派或者擷取值
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));
}
}
- 運作結果:
(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);
}
}
結果如下:
(4)-2通過反射擷取成員方法,使用成員方法
- 擷取成員方法,步驟如下:
- 擷取Class對象
- 擷取構造方法
- 通過構造方法,建立對象
- 擷取指定的方法
-
執行找到的方法
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);
}
}
- 運作結果: