Java的反射機制之類加載詳解
類加載
反射機制是java實現(xiàn)動態(tài)語言的關(guān)鍵,也就是通過反射實現(xiàn)類動態(tài)加載
動態(tài)加載和靜態(tài)加載
1.靜態(tài)加載:編譯時加載相關(guān)的類,如果沒有則報錯,依賴性太強
2.動態(tài)加載:運行時加載需要的類,如果運行時不用該類,即使不存在該類,則不報錯,降低了依賴性
動態(tài)加載和靜態(tài)加載的區(qū)別
靜態(tài)加載是指在編譯時期確定要加載的類的類型,即通過 class 關(guān)鍵字和類名來獲取對應(yīng)類的類型,
動態(tài)加載則是在運行時期根據(jù)需要動態(tài)獲取需要加載的類的類型,例如使用 Class.forName() 方法
類加載時機
1.當創(chuàng)建對象時 (new) //靜態(tài)加載
2.當子類被加載時,父類也加載 //靜態(tài)加載
3調(diào)用類中的靜態(tài)成員時 //靜態(tài)加載
4.通過反射 //動態(tài)加載
類加載的五個階段
類加載器是負責把類加載到Java虛擬機中的系統(tǒng)模塊,Java中的類加載過程可以分為三個階段:
那么為什么我們有說是三個階段呢,因為在第二個階段中鏈接階段,又分為了三個部分,分別是驗證準備和解析
1.加載階段:通過類的全限定名獲取其二進制字節(jié)流,將字節(jié)流轉(zhuǎn)換成方法區(qū)內(nèi)部數(shù)據(jù)結(jié)構(gòu),在內(nèi)存中生成一個代表類的 Class 對象并返回。
2.鏈接階段:包括驗證、準備和解析的過程。
- 驗證:檢查類的二進制字節(jié)流是否符合Java虛擬機規(guī)范,并保證不會危害虛擬機自身的安全。
- 準備:為類的靜態(tài)變量分配內(nèi)存,并設(shè)置默認初始值。
代碼演示:
package com.reflection.classload_; /** * 我們說明一個類加載的鏈接階段-準備 */ public class ClassLoad02 { public static void main(String[] args) { } } class A { //屬性-成員變量-字段 //分析類加載的鏈接階段-準備 屬性是如何處理 //1. n1 是實例屬性, 不是靜態(tài)變量,因此在準備階段,是不會分配內(nèi)存 //2. n2 是靜態(tài)變量,分配內(nèi)存 n2 是默認初始化 0 ,而不是20 //3. n3 是static final 是常量, 他和靜態(tài)變量不一樣, 因為一旦賦值就不變 n3 = 30 public int n1 = 10; public static int n2 = 20; public static final int n3 = 30; }
- 解析:將類中的符號引用轉(zhuǎn)換成直接引用,這個過程包括方法和字段的解析。
3.初始化階段:
到初始化階段,才真正開始執(zhí)行類中定義的 Java 程序代碼,此階段是執(zhí)行<clinit>() 方法的過程。
<clinit>() 方法是由編譯器按語句在源文件中出現(xiàn)的順序,依次自動收集類中的所有靜態(tài)變量的賦值動作和靜態(tài)代碼塊中的語句,并進行合并。
虛擬機會保證一個類的 <clinit>()方法在多線程環(huán)境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執(zhí)行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執(zhí)行 <clinit>() 方法完畢 [debug源碼]
代碼演示:
package com.reflection.classload_; /** * 演示類加載-初始化階段 */ public class ClassLoad03 { public static void main(String[] args) throws ClassNotFoundException { //分析 //1. 加載B類,并生成 B的class對象 //2. 鏈接 num = 0 //3. 初始化階段 // 依次自動收集類中的所有靜態(tài)變量的賦值動作和靜態(tài)代碼塊中的語句,并合并 /* clinit() { System.out.println("B 靜態(tài)代碼塊被執(zhí)行"); //num = 300; num = 100; } 合并: num = 100 */ //new B();//類加載 如果是使用的是new B()一個對象,那么還會導致構(gòu)造器被執(zhí)行,如果使用的是類的靜態(tài)屬性,那么也會導致類的加載,但是不會導致構(gòu)造器被執(zhí)行 //System.out.println(B.num);//100, 如果直接使用類的靜態(tài)屬性,也會導致類的加載 //看看加載類的時候,是有同步機制控制 /* protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //正因為有這個機制,才能保證某個類在內(nèi)存中, 只有一份Class對象 synchronized (getClassLoadingLock(name)) { //.... } } */ B b = new B(); } } class B { static { System.out.println("B 靜態(tài)代碼塊被執(zhí)行"); num = 300; } static int num = 100; public B() {//構(gòu)造器 System.out.println("B() 構(gòu)造器被執(zhí)行"); } }
獲取類的結(jié)構(gòu)信息
Java反射中可以使用以下方法來獲取類的結(jié)構(gòu)信息:
- getDeclaredFields() :獲取類的所有屬性(Field),包括私有屬性。
- getFields() :獲取類的所有公有屬性。
- getDeclaredMethods() :獲取類的所有方法(Method),包括私有方法。
- getMethods() :獲取類的所有公有方法。
- getDeclaredConstructors() :獲取類的所有構(gòu)造器(Constructor),包括私有構(gòu)造器。
- getConstructors() :獲取類的所有公有構(gòu)造器。
- getDeclaredClasses() :獲取類的所有內(nèi)部類。
- getClasses() :獲取類的所有公有內(nèi)部類。
- getDeclaredAnnotations() :獲取類的所有注解。
- getSuperclass() :獲取類的父類。
- getInterfaces() :獲取類實現(xiàn)的接口。
除了上述方法之外,還有一些其他方法可以用于獲取類的結(jié)構(gòu)信息,如 getDeclaredField() 、 getMethod() 、 getConstructor() 等,這些方法可以用于獲取指定名稱的屬性、方法或者構(gòu)造器的信息。
代碼演示;
package com.reflection; import org.testng.annotations.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 演示如何通過反射獲取類的結(jié)構(gòu)信息 */ public class ReflectionUtils { public static void main(String[] args) { } @Test public void api_02() throws ClassNotFoundException, NoSuchMethodException { //得到Class對象 Class<?> personCls = Class.forName("com.reflection.Person"); //getDeclaredFields:獲取本類中所有屬性 //規(guī)定 說明: 默認修飾符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本類中所有屬性=" + declaredField.getName() + " 該屬性的修飾符值=" + declaredField.getModifiers() + " 該屬性的類型=" + declaredField.getType()); } //getDeclaredMethods:獲取本類中所有方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本類中所有方法=" + declaredMethod.getName() + " 該方法的訪問修飾符值=" + declaredMethod.getModifiers() + " 該方法返回類型" + declaredMethod.getReturnType()); //輸出當前這個方法的形參數(shù)組情況 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("該方法的形參類型=" + parameterType); } } //getDeclaredConstructors:獲取本類中所有構(gòu)造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("===================="); System.out.println("本類中所有構(gòu)造器=" + declaredConstructor.getName());//這里老師只是輸出名 Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("該構(gòu)造器的形參類型=" + parameterType); } } } //第一組方法API @Test public void api_01() throws ClassNotFoundException, NoSuchMethodException { //得到Class對象 Class<?> personCls = Class.forName("com.reflection.Person"); //getName:獲取全類名 System.out.println(personCls.getName());//com.reflection.Person //getSimpleName:獲取簡單類名 System.out.println(personCls.getSimpleName());//Person //getFields:獲取所有public修飾的屬性,包含本類以及父類的 Field[] fields = personCls.getFields(); for (Field field : fields) {//增強for System.out.println("本類以及父類的屬性=" + field.getName()); } //getDeclaredFields:獲取本類中所有屬性 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本類中所有屬性=" + declaredField.getName()); } //getMethods:獲取所有public修飾的方法,包含本類以及父類的 Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println("本類以及父類的方法=" + method.getName()); } //getDeclaredMethods:獲取本類中所有方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本類中所有方法=" + declaredMethod.getName()); } //getConstructors: 獲取所有public修飾的構(gòu)造器,包含本類 Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("本類的構(gòu)造器=" + constructor.getName()); } //getDeclaredConstructors:獲取本類中所有構(gòu)造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("本類中所有構(gòu)造器=" + declaredConstructor.getName());//這里老師只是輸出名 } //getPackage:以Package形式返回 包信息 System.out.println(personCls.getPackage());//com.reflection //getSuperClass:以Class形式返回父類信息 Class<?> superclass = personCls.getSuperclass(); System.out.println("父類的class對象=" + superclass);// //getInterfaces:以Class[]形式返回接口信息 Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println("接口信息=" + anInterface); } //getAnnotations:以Annotation[] 形式返回注解信息 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("注解信息=" + annotation);//注解 } } } class A { public String hobby; public void hi() { } public A() { } public A(String name) { } } interface IA { } interface IB { } @Deprecated class Person extends A implements IA, IB { //屬性 public String name; protected static int age; // 4 + 8 = 12 String job; private double sal; //構(gòu)造器 public Person() { } public Person(String name) { } //私有的 private Person(String name, int age) { } //方法 public void m1(String name, int age, double sal) { } protected String m2() { return null; } void m3() { } private void m4() { } }
通過反射創(chuàng)建對象
1.方式一:調(diào)用類中的public修飾的無參構(gòu)造器
2.方式二:調(diào)用類中的指定構(gòu)造器
3.Class類相關(guān)方法
- newlnstance : 調(diào)用類中的無參構(gòu)造器,獲取對應(yīng)類的對象
- getConstructor(Class...clazz):根據(jù)參數(shù)列表,獲取對應(yīng)的public構(gòu)造器對象
- getDecalaredConstructor(Class...clazz):根據(jù)參數(shù)列表,獲取對應(yīng)的所有構(gòu)造器對象
4.Constructor類相關(guān)方法
- setAccessible:暴破
- newlnstance(Object...obj):調(diào)用構(gòu)造器
應(yīng)用案例1
代碼演示:
package com.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 演示通過反射機制創(chuàng)建實例 */ public class ReflecCreateInstance { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //1. 先獲取到User類的Class對象 Class<?> userClass = Class.forName("com.reflection.User"); //2. 通過public的無參構(gòu)造器創(chuàng)建實例 Object o = userClass.newInstance(); System.out.println(o); //3. 通過public的有參構(gòu)造器創(chuàng)建實例 /* constructor 對象就是 public User(String name) {//public的有參構(gòu)造器 this.name = name; } */ //3.1 先得到對應(yīng)構(gòu)造器 Constructor<?> constructor = userClass.getConstructor(String.class); //3.2 創(chuàng)建實例,并傳入實參 Object hsp = constructor.newInstance("hsp"); System.out.println("hsp=" + hsp); //4. 通過非public的有參構(gòu)造器創(chuàng)建實例 //4.1 得到private的構(gòu)造器對象 Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class); //4.2 創(chuàng)建實例 //暴破【暴力破解】 , 使用反射可以訪問private構(gòu)造器/方法/屬性, 反射面前,都是紙老虎 constructor1.setAccessible(true); Object user2 = constructor1.newInstance(100, "張三豐"); System.out.println("user2=" + user2); } } class User { //User類 private int age = 10; private String name = "明天吃火鍋"; public User() {//無參 public } public User(String name) {//public的有參構(gòu)造器 this.name = name; } private User(int age, String name) {//private 有參構(gòu)造器 this.age = age; this.name = name; } public String toString() { return "User [age=" + age + ", name=" + name + "]"; } }
訪問屬性
1.根據(jù)屬性名獲取Field對象
Field f = clazz對象.getDeclaredField(屬性名);
2.暴破 : fsetAccessible(true); //f 是Field
3.訪問 f.set(o,值); // o 表示對象
syso(f.get(o));//o 表示對象
4.注意: 如果是靜態(tài)屬性,則set和get中的參數(shù)o,可以寫成null
代碼演示:
注意訪問私有的屬性需要進行爆破設(shè)置這個屬性 name.setAccessible(true);
package com.reflection; import java.lang.reflect.Field; /** * 演示反射操作屬性 */ public class ReflecAccessProperty { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { //1. 得到Student類對應(yīng)的 Class對象 Class<?> stuClass = Class.forName("com.reflection.Student"); //2. 創(chuàng)建對象 Object o = stuClass.newInstance();//o 的運行類型就是Student System.out.println(o.getClass());//Student //3. 使用反射得到age 屬性對象 Field age = stuClass.getField("age"); age.set(o, 88);//通過反射來操作屬性 System.out.println(o);// System.out.println(age.get(o));//返回age屬性的值 //4. 使用反射操作name 屬性 Field name = stuClass.getDeclaredField("name"); //對name 進行暴破, 可以操作private 屬性 name.setAccessible(true); //name.set(o, "Jack"); name.set(null, "jack~");//因為name是static屬性,因此 o 也可以寫出null System.out.println(o); System.out.println(name.get(o)); //獲取屬性值 System.out.println(name.get(null));//獲取屬性值, 要求name是static } } class Student {//類 public int age; private static String name; public Student() {//構(gòu)造器 } public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } }
訪問方法
1.根據(jù)方法名和參數(shù)列表獲取Method方法對象:
- Method m = clazz.getDeclaredMethod(方法名,XX.class);//得到本類的所有方法
2.獲取對象 : Object o=clazz.newlnstance():
3暴破: m.setAccessible(true);
4訪問 : Object returnValue = m.invoke(o,實參列表)://o 就是對象
5注意:如果是靜態(tài)方法,則invoke的參數(shù)o,可以寫成null!
本章習題
第一題
代碼演示:
要求:
1.定義PrivateTest類,有私有name屬性,并且屬性值為hellokitty 2.提供getName的公有方法 3創(chuàng)建PrivateTest的類,利用Class類得到私有的name屬性,修改私有的name屬性值并調(diào)用getName()的方法打印name屬性值
package com.homework; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* 1.定義PrivateTest類,有私有name屬性,并且屬性值為hellokitty 2.提供getName的公有方法 3創(chuàng)建PrivateTest的類,利用Class類得到私有的name屬性,修改私有的name屬性值并調(diào)用getName()的方法打印name屬性值 */ public class Homework01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { //得到class類對象 Class<?> aClass = Class.forName("com.homework.PrivateTest"); //創(chuàng)建對象實例 Object o = aClass.newInstance(); //得到所有的字段 Field name = aClass.getDeclaredField("name"); //因為name是private私有的 因此需要進行爆破 name.setAccessible(true); //修改name的值 name.set(o, "你好"); //得到所有方法,因為這里方法是public的因此不需要使用爆破 Method getName = aClass.getMethod("getName"); Object invoke = getName.invoke(o); System.out.println("name的屬性值是=" + invoke); } } class PrivateTest { private String name = "helloKitty"; public String getName() { return name; } }
第二題
代碼演示:
要求:
利用Class類的forName方法得到File類的class 對象 在控制臺打印File類的所有構(gòu)造器 通過newlnstance的方法創(chuàng)建File對象,并創(chuàng)建E:\ mynew.txt文件 提示:創(chuàng)建文件的正常寫法如下:File file = new File("d: aa.txt");file.createNewFile();
package com.homework; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* 利用Class類的forName方法得到File類的class 對象 在控制臺打印File類的所有構(gòu)造器 通過newlnstance的方法創(chuàng)建File對象,并創(chuàng)建E:\ mynew.txt文件 提示:創(chuàng)建文件的正常寫法如下:File file = new File("d: aa.txt");file.createNewFile(); */ public class Homework02 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //得到File類 的class對象 Class<?> aClass = Class.forName("java.io.File"); //得到所有的構(gòu)造器,并輸出 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } //得到一個指定的構(gòu)造器 Constructor<?> constructor = aClass.getConstructor(String.class); //指定文件創(chuàng)建的路徑 String filePath = "d:\\mynew.txt"; //創(chuàng)建文件對象 /* 此時o相當于執(zhí)行了這么一個步驟 File file = new File(filePath); 此時o就是File對象的實例 */ Object o = constructor.newInstance(filePath); //得到createNewFile方法的對象 /* 通過反射,得到file類中的一個方法createNewFIle()方法的對象 */ Method createNewFile = aClass.getMethod("createNewFile"); //最后使用反射調(diào)用方法,相當于執(zhí)行了file.createNewFIle(); //只不過一個是使用反射的方式創(chuàng)建,一個是使用new 出來的File對象的實例進行創(chuàng)建,在反射中,萬物皆對象,因此可以使用這種方法創(chuàng)建 createNewFile.invoke(o); //此時o的運行類型是Java.io.File 因為在反射中所有的編譯類型都是Object System.out.println(o.getClass()); System.out.println("文件創(chuàng)建成功"); } }
到此這篇關(guān)于Java的反射機制之類加載詳解的文章就介紹到這了,更多相關(guān)Java的反射機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從Myeclipse 導入到eclipse中無法識別為 web項目 問題的解決步驟
這篇文章主要介紹了從Myeclipse 導入到eclipse中無法識別為 web項目 問題的解決步驟,需要的朋友可以參考下2018-05-05spring AOP定義AfterThrowing增加處理實例分析
這篇文章主要介紹了spring AOP定義AfterThrowing增加處理,結(jié)合實例形式分析了spring面向切面AOP定義AfterThrowing相關(guān)實現(xiàn)步驟與操作技巧,需要的朋友可以參考下2020-01-01Spring Boot應(yīng)用監(jiān)控的實戰(zhàn)教程
Spring Boot 提供運行時的應(yīng)用監(jiān)控和管理功能,下面這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用監(jiān)控的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧2018-05-05PostgreSQL Docker部署+SpringBoot集成方式
本文介紹了如何在Docker中部署PostgreSQL和pgadmin,并通過SpringBoot集成PostgreSQL,主要步驟包括安裝PostgreSQL和pgadmin,配置防火墻,創(chuàng)建數(shù)據(jù)庫和表,以及在SpringBoot中配置數(shù)據(jù)源和實體類2024-12-12Java利用poi實現(xiàn)word表格轉(zhuǎn)excel
這篇文章主要為大家詳細介紹了Java如何利用poi實現(xiàn)word表格轉(zhuǎn)excel,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2025-03-03