圖文詳解Java的反射機(jī)制
1.什么是反射
反射就是Reflection,Java的反射是指程序在運(yùn)行期可以拿到一個(gè)對(duì)象的所有信息。
加載類后,在堆中就產(chǎn)生了一個(gè)class類型的對(duì)象,這個(gè)對(duì)象包含了類的完整結(jié)構(gòu)的信息,通過這個(gè)對(duì)象得到類的結(jié)構(gòu)。這個(gè)class對(duì)象就像一面鏡子,透過這個(gè)鏡子可以看到類的結(jié)構(gòu),所以形象的稱之為“反射”
反射機(jī)制是框架的靈魂,一個(gè)java程序員不能不會(huì)使用反射,沒有反射機(jī)制,java將一無是處
2.Hello,java反射
使用實(shí)例:
Cat類:
public class Cat { private String name; private String age; public void hi() { System.out.println("喵喵喵~"); } }
測(cè)試類:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射入門 * 通過字符串形式的類的路徑和方法信息調(diào)用類的方法 */ public class reflectionTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { // 類的路徑 String classPath = "reflection.Cat"; // 要執(zhí)行類的方法名稱 String classMethod = "hi"; // 加載類 Class cls = Class.forName(classPath); // 通過cls得到你加載的類Cat的對(duì)象實(shí)例 Object o = cls.newInstance(); // 查看o的運(yùn)行類型,為Cat類型 System.out.println(o.getClass()); // 得到加載的類的方法對(duì)象 // 在反射中,可以把方法視為對(duì)象 Method method = cls.getMethod(classMethod); // 通過method實(shí)例調(diào)用方法:即通過方法對(duì)象來實(shí)現(xiàn)調(diào)用方法 method.invoke(o); } }
輸出:
class reflection.Cat
喵喵喵~
3.java程序運(yùn)行的三個(gè)階段
反射的基本原理:
4.反射相關(guān)類
現(xiàn)在我們來完善一下上面的測(cè)試類:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射入門 * 通過字符串形式的類的路徑和方法信息調(diào)用類的方法 */ public class reflectionTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { // 類的路徑 String classPath = "reflection.Cat"; // 要執(zhí)行類的方法名稱 String classMethod = "hi"; // 加載類 Class cls = Class.forName(classPath); // 通過cls得到你加載的類Cat的對(duì)象實(shí)例 Object o = cls.newInstance(); // 查看o的運(yùn)行類型,為Cat類型 System.out.println(o.getClass()); // 得到加載的類的方法對(duì)象 // 在反射中,可以把方法視為對(duì)象 Method method = cls.getMethod(classMethod); // 通過method實(shí)例調(diào)用方法:即通過方法對(duì)象來實(shí)現(xiàn)調(diào)用方法 method.invoke(o); // 得到name字段的信息 // 注意:getField不能得到私有的屬性信息 Field nameField = cls.getField("name"); System.out.println(nameField.get(o)); // 得到構(gòu)造器 Constructor constructor = cls.getConstructor(); System.out.println(constructor); } }
輸出:
class reflection.Cat
喵喵喵~
豹貓
public reflection.Cat()
5.反射的優(yōu)化
反射調(diào)用方法的效率比普通方法調(diào)用的效率低很多
在使用反射調(diào)用方法時(shí),可以關(guān)閉安全訪問檢測(cè),這樣會(huì)提高反射調(diào)用的效率
例如:下面是一個(gè)實(shí)例:
// 在反射中,可以把方法視為對(duì)象 Method method = cls.getMethod(classMethod); // 在反射調(diào)用方法時(shí),取消訪問檢測(cè) method.setAccessible(true); // 通過method實(shí)例調(diào)用方法:即通過方法對(duì)象來實(shí)現(xiàn)調(diào)用方法 method.invoke(o);
6.Class類分析
Class也是類,因此也繼承Object類
Class對(duì)象不是new出來的,而是系統(tǒng)創(chuàng)建的(通過loadClass方法)
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
對(duì)于某個(gè)類的Class對(duì)象,在內(nèi)存中只有一份,因?yàn)轭愔患虞d一次
每個(gè)對(duì)象實(shí)例都會(huì)記錄自己是由哪一個(gè)Class實(shí)例生成的
通過Class對(duì)象可以完整的得到一個(gè)類的結(jié)構(gòu)
Class對(duì)象存在于堆中
類的字節(jié)碼二進(jìn)制數(shù)據(jù)是放在方法區(qū)里面的,有的地方稱為類的元數(shù)據(jù)
Class對(duì)象方法演示:
實(shí)例
Cat類:
public class Cat { public String name = "豹貓"; public String age; public void hi() { System.out.println("喵喵喵~"); } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } }
ClassMethod類:
import java.lang.reflect.Field; /** * 演示Class類的常用方法 */ public class ClassMethod { public static void main(String[] args) throws Exception { String classAllPath = "reflection.Cat"; // 獲取Cat類對(duì)應(yīng)的Class對(duì)象 Class<?> aClass = Class.forName(classAllPath); // 顯示aClass對(duì)象是哪一個(gè)類的Class對(duì)象 System.out.println(aClass); // 輸出aClass對(duì)象的運(yùn)行類型 System.out.println(aClass.getClass()); // 得到包名 System.out.println(aClass.getPackage().getName()); // 得到全類名 System.out.println(aClass.getName()); // 創(chuàng)建對(duì)象實(shí)例 Cat cat = (Cat) aClass.newInstance(); System.out.println(cat); // 通過反射獲取屬性 Field name = aClass.getField("name"); System.out.println(name.get(cat)); // 通過反射給屬性設(shè)置值 name.set(cat,"湯圓"); System.out.println(name.get(cat)); // 得到所有的屬性 Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field.getName()); } } }
輸出:
class reflection.Cat
class java.lang.Class
reflection
reflection.Cat
Cat{name='豹貓', age='null'}
豹貓
湯圓
name
age
7.獲取Class對(duì)象的六種方式
獲取Class對(duì)象細(xì)分可以分為六種方式:
現(xiàn)在我們使用代碼進(jìn)行演示:
/** * 獲取Class對(duì)象的六種方式 */ public class GetClass { public static void main(String[] args) throws ClassNotFoundException { String classAllPath = "reflection.Cat"; // 1.forName獲取。多用于配置文件讀取類的全路徑,加載類 // 2.類名.class。多用于參數(shù)傳遞 System.out.println(String.class); System.out.println(Cat.class); // 3.對(duì)象.getClass。適用于已經(jīng)存在對(duì)象實(shí)例的情況 Cat cat = new Cat(); System.out.println(cat.getClass()); // 4.通過類加載器獲取Class對(duì)象 // (1)得到類加載器 ClassLoader classLoader = cat.getClass().getClassLoader(); // (2)通過類加載器得到Class對(duì)象 Class<?> cls = classLoader.loadClass(classAllPath); System.out.println(cls); // 5.基本數(shù)據(jù)類型通過.class獲取Class對(duì)象 Class<Integer> integerClass = int.class; Class<Character> characterClass = char.class; Class<Boolean> booleanClass = boolean.class; System.out.println(integerClass); // 6.基本數(shù)據(jù)類型的包裝類可以通過.TYPE得到Class對(duì)象 Class<Integer> type = Integer.TYPE; System.out.println(type); } }
輸出:
class java.lang.String
class reflection.Cat
class reflection.Cat
class reflection.Cat
int
int
那么,那些類型擁有Class對(duì)象呢?
- 外部類
- 接口
- 數(shù)組(含二維數(shù)組)
- 注解
- 枚舉
- 基本數(shù)據(jù)類型
- 包裝類
- 成員內(nèi)部類
8.類加載機(jī)制
動(dòng)態(tài)加載和靜態(tài)加載
靜態(tài)加載:
在編譯時(shí)加載的類,如果沒有則直接報(bào)錯(cuò),依賴性太強(qiáng)
動(dòng)態(tài)加載:
運(yùn)行時(shí)加載相應(yīng)的類,如果運(yùn)行時(shí)沒有用到相應(yīng)的類,則不會(huì)進(jìn)行加載,改善了依賴性的問題
通過new創(chuàng)建實(shí)例的方式就是典型的靜態(tài)加載,通過反射創(chuàng)建對(duì)象是典型的動(dòng)態(tài)加載
類加載時(shí)機(jī):
- 創(chuàng)建對(duì)象時(shí)
- 子類被加載
- 調(diào)用類中的靜態(tài)成員時(shí)
- 通過反射(動(dòng)態(tài)加載)
類加載流程概述
類加載的三個(gè)階段:
類加載后的內(nèi)存布局:
整體布局:
加載階段
JVM在該階段的主要目的是將字節(jié)碼從不同的數(shù)據(jù)源轉(zhuǎn)化為二進(jìn)制字節(jié)流加載到內(nèi)存中,并生成一個(gè)代表該類的Class對(duì)象
連接階段
驗(yàn)證:
目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,不會(huì)危害虛擬機(jī)自身的安全
包括:文件格式的驗(yàn)證,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證
準(zhǔn)備:
JVM會(huì)在該階段對(duì)靜態(tài)變量分配內(nèi)存并默認(rèn)初始化(分配默認(rèn)值)。這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配
解析:
虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接應(yīng)用的過程
初始化
<clinit>()方法依次自動(dòng)收集類中的所有靜態(tài)變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句,并進(jìn)行合并
虛擬機(jī)會(huì)保證一個(gè)類的<clinit>()方法在多線程環(huán)境中被正確的加鎖同步,如果多個(gè)線程同時(shí)去初始化一個(gè)類,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類的<clinit>()方法。其他線程都要阻塞等待
9.通過反射獲取類的結(jié)構(gòu)信息
我們使用一段程序來演示所有的常用方法:
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) throws ClassNotFoundException { // 得到Class對(duì)象 Class<?> personCls = Class.forName("reflection.Person"); // 得到全類名 System.out.println(personCls.getName()); // 得到簡(jiǎn)單類名 System.out.println(personCls.getSimpleName()); // 獲取所有的public修飾的屬性,包含父類的 Field[] fields = personCls.getFields(); for (Field field : fields) { System.out.println(field.getName()); } // 獲取本類的所有屬性,不包含父類 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { // 打印屬性名字和等級(jí)和類型 // 默認(rèn):0,public:1,private:2,protected:4,static:8,final:16 System.out.println(declaredField.getName() + "\t" + declaredField.getModifiers() + "\t" + declaredField.getType()); } // 獲取所有public方法,包含父類的 Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println(method.getName()); } // 獲取本類的所有方法和等級(jí)和返回類型和參數(shù)類型數(shù)組 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName() + "\t" + declaredMethod.getModifiers() + "\t" + declaredMethod.getReturnType()); // 獲取方法的參數(shù)類型數(shù)組 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType); } } // 獲取所有的public構(gòu)造器,不包含父類的 Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor.getName()); } // 獲取所有的構(gòu)造器,包含私有的,還有構(gòu)造器的參數(shù)類型 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor.getName()); Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType); } } // 返回包信息 System.out.println(personCls.getPackage()); // 以Class形式返回父類的信息 Class<?> superclass = personCls.getSuperclass(); System.out.println(superclass); // 以Class形式返回接口信息 Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface.getName()); } // 返回注解信息 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } } interface IA {} interface IB {} @Deprecated class Person extends Something implements IA, IB { public String name; protected int level; private int age; String job; public Person() { } public Person(String name) { this.name = name; } private Person(String name, int level, int age, String job) { this.name = name; this.level = level; this.age = age; this.job = job; } public void show(String content, int code) {} protected void hi() {} void say() {} private void hei() {} } class Something { public String hobby; }
以上就是圖文詳解Java的反射機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Java反射機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java設(shè)計(jì)模式之抽象工廠模式實(shí)例詳解
這篇文章主要介紹了Java設(shè)計(jì)模式之抽象工廠模式,結(jié)合實(shí)例形式分析了抽象工廠模式的概念、功能、定義與使用方法,需要的朋友可以參考下2017-09-09java+socket實(shí)現(xiàn)簡(jiǎn)易局域網(wǎng)聊天室
這篇文章主要為大家詳細(xì)介紹了java+socket實(shí)現(xiàn)簡(jiǎn)易局域網(wǎng)聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

淺談在springboot中使用定時(shí)任務(wù)的方式

Java web的讀取Excel簡(jiǎn)單實(shí)例代碼

MyBatis接口綁定的實(shí)現(xiàn)方式和工作原理

Java 實(shí)戰(zhàn)項(xiàng)目之疫情人員流動(dòng)管理系統(tǒng)詳解