Java学习笔记7-2——注解与反射
目錄
- 理解 Class 類并獲取 Class 實(shí)例
- Class類
- 獲取 Class 類的實(shí)例
- 哪些類型可以有Class對(duì)象
- 所有類型的Class對(duì)象
- 從內(nèi)存角度分析類加載【重點(diǎn)】
- 類加載的過程
- 什么時(shí)候會(huì)發(fā)生類的初始化
- 類加載器
- 獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
- 有了Class對(duì)象能做什么
- 性能對(duì)比分析
- 通過反射操作泛型
- 通過反射操作注解
理解 Class 類并獲取 Class 實(shí)例
1.先定義一個(gè)實(shí)體類
package com.cheng.reflection;public class User {// 屬性// getter setter// ... }2.主程序
public class Test01 {public static void main(String[] args) throws Exception {// 通過反射獲取類的 Class 對(duì)象Class<?> aClass = Class.forName("com.cheng.reflection.User");System.out.println(aClass);} }輸出:
class com.cheng.reflection.User
一個(gè)類在內(nèi)存中只有一個(gè) Class 對(duì)象,一個(gè)類被加載之后,類的整個(gè)結(jié)構(gòu)都會(huì)被封裝在 Class 對(duì)象中
Class類
在 Object 類中定義了以下方法,此方法將被所有子類繼承
public final native Class<?> getClass();上面的方法返回值的類型是一個(gè) Class 類,此類是 Java 反射的源頭,實(shí)際上所謂反射從程序的運(yùn)行結(jié)果來看也很好理解,即:可以通過對(duì)象反射求出類的名稱
對(duì)于每個(gè)類而言,JRE 都為其保留了一個(gè)不變的 Class 類型的對(duì)象。一個(gè) Class 對(duì)象包含了特定某個(gè)結(jié)構(gòu)(class / interface / enum / annotation / primitive type/void / [])
- Class 本身也是一個(gè)類
- Class 對(duì)象只能由系統(tǒng)建立對(duì)象
- 一個(gè)加載的類在 JVM 中只會(huì)有一個(gè) Class 實(shí)例
- 一個(gè) Class 對(duì)象對(duì)應(yīng)的是一個(gè)加載到 JVM 中的一個(gè) .class 文件
- 每個(gè)類的實(shí)例都會(huì)記得自己是由哪個(gè) Class 實(shí)例所生成
- 通過 Class 可以完整地得到一個(gè)類中的所有被加載的結(jié)構(gòu)
- Class 類是 Reflection 的根源,針對(duì)任何你想動(dòng)態(tài)加載、運(yùn)行的類、唯有先獲得響應(yīng)的 Class 對(duì)象
Class 類的常用方法:
| static Class forName(String name) | 返回指定類名 name 的 Class 對(duì)象 |
| Object newInstance() | 調(diào)用缺省構(gòu)造函數(shù),返回 Class 對(duì)象的一個(gè)實(shí)例 |
| getName() | 返回此 Class 對(duì)象所表示的實(shí)體(類、接口、數(shù)組類、或 void)的名稱 |
| Class getSuperClass() | 返回當(dāng)前 Class 對(duì)象的父類的 Class 對(duì)象 |
| Class[] getInterfaces() | 獲取當(dāng)前 Class 對(duì)象的接口 |
| ClassLoader getClassLoader() | 返回該類的類加載器 |
| Constructor[] getConstructors() | 返回一個(gè)包含某些 Constructor 對(duì)象的數(shù)組 |
| Method getMethod(String name, Class… T) | 返回一個(gè) Method 對(duì)象,此對(duì)象的形參類型為 paramType |
| Field[] getDeclaredFields() | 返回 Field 對(duì)象的一個(gè)數(shù)組 |
獲取 Class 類的實(shí)例
public class Test01 {public static void main(String[] args) throws Exception {// 方式一:forName 獲得Class aClass = Class.forName("com.java.demo.reflect.User");System.out.println(aClass);// 方式二:通過對(duì)象獲得Class aClass1 = new User().getClass();System.out.println(aClass1);// 方式三:通過類名.class 獲得Class<User> aClass2 = User.class;System.out.println(aClass2);} }輸出:
class com.cheng.reflection.User
class com.cheng.reflection.User
class com.cheng.reflection.User
哪些類型可以有Class對(duì)象
- class:外部類,成員(成員內(nèi)部類、靜態(tài)內(nèi)部類),局部?jī)?nèi)部類,匿名內(nèi)部類
- interface:接口
- []:數(shù)組
- enum:枚舉
- annotation:注解@inerface
- primitive type:基本數(shù)據(jù)類型
- void
所有類型的Class對(duì)象
public class Test02 {public static void main(String[] args) {Class c1 = Object.class; // 類Class c2 = Comparable.class; // 接口Class c3 = String[].class; // 一維數(shù)組Class c4 = int[][].class; // 二維數(shù)組Class c5 = Override.class; // 注解Class c6 = ElementType.class; // 枚舉Class c7 = Integer.class; // 基本數(shù)據(jù)類型Class c8 = void.class; // voidClass c9 = Class.class; // ClassSystem.out.println(c1);System.out.println(c2);System.out.println(c3);System.out.println(c4);System.out.println(c5);System.out.println(c6);System.out.println(c7);System.out.println(c8);System.out.println(c9);// 只要元素類型與維度一樣,就是同一個(gè)Classint[] a = new int[10];int[] b = new int[100];System.out.println(a.getClass().hashCode());System.out.println(b.getClass().hashCode());} }輸出:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
2133927002
2133927002
從內(nèi)存角度分析類加載【重點(diǎn)】
【重點(diǎn)】可參考B站視頻:【狂神說Java】注解和反射
(方法區(qū)可以看作特殊的堆內(nèi)存)
類加載的過程
加載:
- 將 class 文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),然后生成一個(gè)代表這個(gè)類的java.lang.Class 對(duì)象
鏈接:
(將 Java 類的二進(jìn)制代碼合并到 JVM 的運(yùn)行狀態(tài)之中的過程)
- 驗(yàn)證:確保加載的類信息符合 JVM 規(guī)范,沒有安全方面的問題
- 準(zhǔn)備:正式為類變量(static)分配內(nèi)存并設(shè)置類變量默認(rèn)初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配
- 解析:虛擬機(jī)常量池內(nèi)的符號(hào)引用(常量名)替換為直接引用(地址)的過程
初始化:
- (JVM)執(zhí)行類構(gòu)造器< clint>() 方法的過程,類構(gòu)造器< clint>()方法是由編譯期自動(dòng)收集類中所有類變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句合并產(chǎn)生的(類構(gòu)造器是構(gòu)造類信息的,不是構(gòu)造該類對(duì)象的構(gòu)造器)
- 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行初始化,則需要先觸發(fā)其父類的初始化
- 虛擬機(jī)會(huì)保證一個(gè)類的< clint>()方法在多線程環(huán)境中被正確加鎖和同步
輸出:
A 類靜態(tài)代碼塊初始化
A 類的無參構(gòu)造函數(shù)初始化
100
什么時(shí)候會(huì)發(fā)生類的初始化
類的主動(dòng)引用(一定會(huì)發(fā)生類的初始化):
- 當(dāng)虛擬機(jī)啟動(dòng),先初始化main方法所在的類
- new一個(gè)類的對(duì)象
- 調(diào)用類的靜態(tài)成員(除了final常量)和靜態(tài)方法
- 使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用
- 當(dāng)初始化一個(gè)類,如果父類沒有被初始化,則先會(huì)初始化它的父類
類的被動(dòng)引用(不會(huì)發(fā)生類的初始化):
- 當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正聲明這個(gè)域的類才會(huì)被初始化。如:當(dāng)通過子類引用父類的靜態(tài)變量,不會(huì)導(dǎo)致子類初始化
- 通過數(shù)組定義類引用,不會(huì)觸發(fā)此類的初始化
- 引用常量不會(huì)觸發(fā)此類的初始化(常量在鏈接階段就存入調(diào)用類的常量池中了)
類加載器
類加載器作用:
輸出:
sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@7f31245a null =============================== sun.misc.Launcher$AppClassLoader@18b4aac2 null =============================== C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar; C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar; C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar; ...(內(nèi)容過多,不予展示)獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
通過反射獲取運(yùn)行時(shí)類的完整結(jié)構(gòu):
- 實(shí)現(xiàn)的全部接口
- 所繼承的父類
- 全部的構(gòu)造器
- 全部的方法
- 全部的 Field
- 注解
- …
有了Class對(duì)象能做什么
**創(chuàng)建類的對(duì)象:**調(diào)用Class對(duì)象的newInstance()方法
沒有無參構(gòu)造器也能創(chuàng)建對(duì)象。只要在操作的時(shí)候明確調(diào)用類中的構(gòu)造器,并將參數(shù)傳遞進(jìn)去之后,才可以實(shí)例化操作。
步驟如下:
調(diào)用指定的方法:
通過反射,調(diào)用類中的方法,通過Method類完成
- 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個(gè)Method對(duì)象,并設(shè)置此方法操作時(shí)所需要的參數(shù)類型
- 之后使用Object invoke(Object obj, Object[] args)進(jìn)行調(diào)用,并向方法中傳遞要設(shè)置的obj對(duì)象的參數(shù)信息
Object invoke(Object obj, Object… args) - Object對(duì)應(yīng)原方法的返回值,若原方法無返回值,此時(shí)返回null
- 若原方法為靜態(tài)方法,此時(shí)形參Object obj可為null
- 若原方法形參列表為空,則Object[] args可為null
- 若原方法聲明為private,則需要在調(diào)用此invo()方法前,顯式調(diào)用對(duì)象的setAccessible(true)方法,即可訪問private方法
setAccessible
- Method、Field、Constructor對(duì)象都有setAccessible()方法
- setAccessible作用是啟動(dòng)和禁用訪問安全檢查的開關(guān)
- 參數(shù)值為true則指示反射的對(duì)象在使用時(shí)應(yīng)該取消Java語言訪問檢查,使得原本無法訪問的私有成員也可以訪問。可以提高反射的效率。如果代碼中必須使用反射,而該句代碼需要頻繁地被調(diào)用,請(qǐng)?jiān)O(shè)置為true
性能對(duì)比分析
上面說到了setAccessible(true)可以提高反射效率,下面開始對(duì)比普通方式調(diào)用和反射方式調(diào)用(包括反射方式的setAccessible()開啟和禁用)的效率
//分析性能問題 public class Test08 {// 普通方式調(diào)用public static void test01() {User user = new User();long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {user.getName();}long endTime = System.currentTimeMillis();System.out.println("普通方式執(zhí)行 10 億次:" + (endTime - startTime) + "ms");}// 反射方式調(diào)用public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c = user.getClass();Method getName = c.getDeclaredMethod("getName", null);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}long endTime = System.currentTimeMillis();System.out.println("反射方式執(zhí)行 10 億次:" + (endTime - startTime) + "ms");}// 反射方式調(diào)用,關(guān)閉檢測(cè)public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c = user.getClass();Method getName = c.getDeclaredMethod("getName", null);getName.setAccessible(true);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}long endTime = System.currentTimeMillis();System.out.println("關(guān)閉檢測(cè)方式執(zhí)行 10 億次:" + (endTime - startTime) + "ms");}public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {test01();test02();test03();} }輸出:
普通方式執(zhí)行 10 億次:5ms
反射方式執(zhí)行 10 億次:1692ms
關(guān)閉檢測(cè)方式執(zhí)行 10 億次:1331ms
通過反射操作泛型
- Java 采用泛型擦除的機(jī)制來引入泛型,Java 中的泛型僅僅是給編譯器 javac使用的,確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換問題,但是,一旦編譯完成,所有和泛型有關(guān)的類型全部擦除
- 為了通過反射操作這些類型,Java 新增了 ParameterizedType,GenericArrayType,TypeVariable和 WildcardType 幾種類型來代表不能被歸一到 Class 類中的類型但是又和原始類型齊名的類型。
ParameterizedType:表示一種參數(shù)化類型,比如 Collection< String>
GenericArrayType:表示一種元素類型時(shí)參數(shù)化類型或者類型變量的數(shù)組類型
TypeVariable :是各種類型變量的公共父接口
WildcardType :代表一種通配符類型表達(dá)式
通過反射操作注解
package com.cheng.reflection;import java.lang.annotation.*; import java.lang.reflect.Field;public class Test10 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class c1 = Class.forName("com.cheng.reflection.Stu");// 通過反射獲得注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println("---->"+annotation);}// 獲得注解的value的值TableStu tableStu = (TableStu)c1.getAnnotation(TableStu.class);String value = tableStu.value();System.out.println(value);// 獲得類指定注解Field f = c1.getDeclaredField("id");FieldStu annotation = f.getAnnotation(FieldStu.class);System.out.println(annotation.columnName());System.out.println(annotation.type());System.out.println(annotation.length());} }@TableStu("db_stu") class Stu{@FieldStu(columnName = "db_id", type = "int", length = 10)private int id;@FieldStu(columnName = "db_age", type = "int", length = 10)private int age;@FieldStu(columnName = "db_name", type = "varchar", length = 3)private String name;public Stu() {}public Stu(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Stu{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';} }// 類名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableStu{String value(); }// 屬性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldStu{String columnName();String type();int length(); }總結(jié)
以上是生活随笔為你收集整理的Java学习笔记7-2——注解与反射的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年焊工(初级)考试及焊工(初级)
- 下一篇: Java Web应用的生命周期