快速掌握J(rèn)ava中注解與反射
注解和反射
一、注解
什么是注解?Annotation
@Override :表示一個(gè)方法聲明打算重寫超類的另一個(gè)方法聲明
@Deprecated:表示不鼓勵(lì)程序員使用這樣的元素,(此注釋可以用于修飾方法,屬性,類)
@SupperssWarnings():用來抑制編譯時(shí)的警告信息
"all", "unchecked", value={"unchecked","deprecation"})]
元注解
元注解的作用就是負(fù)責(zé)注解其他注解,Java定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類型,它們被用來對(duì)其他annotation類型作說明。
- @Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
表示我們的注解可以用在哪些地方(字段、方法等)
- @Retention:表示需要什么級(jí)別保存該注釋信息,用于描述注解的生命周期(source源碼 < class < runtime運(yùn)行)
表示我們的注解在什么地方還有效
- @Document:說明該注解將被包含在javadoc中
表示是否將我們的注解生成在javadoc中
- @Inherited:說明子類可以繼承父類中的該注解
子類可以繼承父類的注解
二、反射機(jī)制
動(dòng)態(tài)語言:
- 在運(yùn)行時(shí)可以根據(jù)某些條件改變自身結(jié)構(gòu)。如:C、C#、JavaScript、PHP、Python
靜態(tài)語言:
- 在運(yùn)行時(shí)結(jié)構(gòu)不可改變的語言就是靜態(tài)語言,如Java、C、C++
Java不是動(dòng)態(tài)語言,但是Java可以稱之為“準(zhǔn)動(dòng)態(tài)語言”。即Java有一定的動(dòng)態(tài)性,我們可以利用反射機(jī)制獲得類似動(dòng)態(tài)語言的特性。
Reflection(反射)是Java被視為動(dòng)態(tài)語言的關(guān)鍵,反射機(jī)制允許程序在執(zhí)行期間于Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對(duì)象的內(nèi)部屬性及方法。
Class c = Class.forName("java.lang.String")
加載完類之后,在堆內(nèi)存的方法區(qū)中就產(chǎn)生了一個(gè)Class類型的對(duì)象(一個(gè)類只有一個(gè)Class對(duì)象),這個(gè)對(duì)象就包含了完整的類的結(jié)構(gòu)信息(構(gòu)造器、方法、字段、包括私有字段等)。我們可以通過這個(gè)對(duì)象看到類的結(jié)構(gòu)。這個(gè)對(duì)象就像一面鏡子,透過這個(gè)鏡子看到類的結(jié)構(gòu),所以,我們形象的稱之為:反射
- 正常方式:引入需要的“包類”名稱 ---> 通過new實(shí)例化--->取得實(shí)例化對(duì)象
- 反射方式:實(shí)例化對(duì)象--->getClass()--->取得完整的“包類”名稱
Java反射機(jī)制提供的功能
- 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類、判斷任意一個(gè)類所具有的成員變量和方法。
- 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
- 在運(yùn)行時(shí)獲取泛型信息
- 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的成員變量和方法
- 在運(yùn)行時(shí)處理注解
- 生成動(dòng)態(tài)代理
- .......
Java反射優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):可以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯,體現(xiàn)出很大的靈活性。
- 缺點(diǎn):對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于直接執(zhí)行相同的操作。
java.lang.Class; 代表一個(gè)類 java.lang.reflect.Method; 代表類的方法 java.lang.reflect.Field; 代表類的成員變量 java.lang.reflect.Constructor; 代表類的構(gòu)造器
Class類
在Object類中定義了以下的方法,此方法將被所有子類繼承
public final Class getClass()
此方法返回值的類型是一個(gè)Class類,此類是Java反射的源頭,實(shí)際上所謂從程序中的運(yùn)行結(jié)果來看也是很好理解,即:可以通過對(duì)象反射類的名稱。
對(duì)象照鏡子后可以得到的信息:某個(gè)類的屬性、方法和構(gòu)造器、某個(gè)類到底實(shí)現(xiàn)了哪些接口。對(duì)于每個(gè)類而言,JRE都為其保留了Class類型的對(duì)象。一個(gè)Class對(duì)象包含了特定某個(gè)結(jié)構(gòu)(class/interface/enum/annotation/primitive type/void/[]
)的有關(guān)信息。
- 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類的常用方法
Class類中部分源碼
public class Class<T> { /** * 根據(jù)類的全限定名稱獲取 Class 對(duì)象 * * @param className 類的全限定名稱 * @return Class 對(duì)象 * @throws ClassNotFoundException 如果找不到指定名稱的類 */ public static Class<?> forName(String className) throws ClassNotFoundException { return Class.forNameImpl(className, false); } ? // 實(shí)現(xiàn)細(xì)節(jié) private static Class<?> forNameImpl(String name, boolean initialize) throws ClassNotFoundException { ClassLoader cl = null; try { if (cl == null) { cl = Thread.currentThread().getContextClassLoader(); } return cl.loadClass(name); } catch (ClassNotFoundException e) { // 嘗試使用當(dāng)前類加載器 cl = ClassLoader.getSystemClassLoader(); return cl.loadClass(name); } } // 構(gòu)造器是私有的,不允許外部直接創(chuàng)建 Class 對(duì)象 private Class() {} ? // 返回類的全限定名稱 public native String getName(); ? // 返回類的規(guī)范名稱 public String getCanonicalName() { return null; // 實(shí)際實(shí)現(xiàn)可能返回具體的規(guī)范名稱 } ? // 返回類的簡(jiǎn)單名稱 public String getSimpleName() { int lastDot = name.lastIndexOf('.'); return (lastDot < 0) ? name : name.substring(lastDot + 1); } ? // 創(chuàng)建并返回此 Class 對(duì)象所表示的類的一個(gè)新實(shí)例 @Deprecated(since = "9") public T newInstance() throws InstantiationException, IllegalAccessException { return getDeclaredConstructor().newInstance(); } ? // 獲取此 Class 對(duì)象所表示的類聲明的所有方法 public Method[] getDeclaredMethods() { Method[] result = declaredMethods; if (result == null) { // 實(shí)際實(shí)現(xiàn)會(huì)從類信息中獲取方法 // 這里簡(jiǎn)化處理 result = new Method[0]; } return result; } ? // 獲取此 Class 對(duì)象所表示的類聲明的所有字段 public Field[] getDeclaredFields() { Field[] result = declaredFields; if (result == null) { // 實(shí)際實(shí)現(xiàn)會(huì)從類信息中獲取字段 // 這里簡(jiǎn)化處理 result = new Field[0]; } return result; } ? // 測(cè)試指定對(duì)象是否分配了此 Class 對(duì)象所表示的類或接口 public boolean isInstance(Object obj) { return this.equals(obj.getClass()); } ? // 獲取此 Class 對(duì)象所表示的類的直接超類 public native Class<?> getSuperclass(); } ? ?
獲取Class類的實(shí)例
- 若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,程序性能最高。
Class c1 = Person.class;
- 已知某個(gè)類的實(shí)例,調(diào)用該實(shí)例的getClass()方法獲取Class對(duì)象
Class c2 = person.getClass();
- 已知一個(gè)類的全類名,且該類在類路徑下,可以通過Class類的靜態(tài)方法forName()獲取,可能會(huì)拋出ClassNotFoundException
Class c3 = Class.forName("Deom1.Student");
- 基本內(nèi)置數(shù)據(jù)類型的包裝類都有一個(gè)Type屬性
Class c4 = Integer.Type;
- 還可以利用ClassLoader
哪些類型可以有Class對(duì)象
class:外部類,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部?jī)?nèi)部類,匿名內(nèi)部類。
Interface:接口
[]:數(shù)組
enum:枚舉
annotation:注解@interface
primitive type:基本數(shù)據(jù)類型
void
package com.briup.chap13; ? import java.lang.annotation.ElementType; ? /** * @author 35329 */ public class Test04 { 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;// void Class c9 = Class.class;// Class ? System.out.println("c1 = " + c1); System.out.println("c2 = " + c2); System.out.println("c3 = " + c3); System.out.println("c4 = " + c4); System.out.println("c5 = " + c5); System.out.println("c6 = " + c6); System.out.println("c7 = " + c7); System.out.println("c8 = " + c8); System.out.println("c9 = " + c9); ? // 只要元素類型與維度一樣,就是同一個(gè)Class int[] a = new int[10]; int[] b = new int[100]; System.out.println("a.getClass().hashCode() = " + a.getClass().hashCode()); System.out.println("b.getClass().hashCode() = " + b.getClass().hashCode()); ? // 運(yùn)行結(jié)果: //c1 = class java.lang.Object //c2 = interface java.lang.Comparable //c3 = class [Ljava.lang.String; //c4 = class [[I //c5 = interface java.lang.Override //c6 = class java.lang.annotation.ElementType //c7 = class java.lang.Integer //c8 = void //c9 = class java.lang.Class ? //a.getClass().hashCode() = 21685669 //b.getClass().hashCode() = 21685669 } }
類加載內(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)存都將在方法去中進(jìn)行分配。
- 解析:虛擬機(jī)常量池內(nèi)的符號(hào)引用(常量名)替換為直接引用(地址)的過程。
- 初始化:
- 執(zhí)行類構(gòu)造器<clinit>()方法的過程,類構(gòu)造器<clinit>()方法是由編譯期自動(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è)類的<clinit>()方法在多線程環(huán)境中被正確加鎖和同步。
package com.briup.chap13; ? /** * @author 35329 */ public class Test05 { public static void main(String[] args) { A a = new A(); System.out.println(A.m); /** * 1、加載到內(nèi)存,會(huì)產(chǎn)生一個(gè)類對(duì)應(yīng)Class對(duì)象 * 2、鏈接,鏈接結(jié)束后 m = 0; * 3、初始化 * <clinit>(){ * System.out.println("A類靜態(tài)代碼塊初始化"); * m = 300; * m = 100; * } * m = 100; */ } } ? class A{ static { System.out.println("A類靜態(tài)代碼塊初始化"); m = 300; } static int m = 100; /* * m = 300 * m = 100 * 后面的會(huì)覆蓋前面的 * */ public A(){ System.out.println("A類的無參構(gòu)造初始化"); } } ? // 運(yùn)行結(jié)果: //A類靜態(tài)代碼塊初始化 //A類的無參構(gòu)造初始化 //100
什么時(shí)候會(huì)發(fā)生類初始化
- 類的主動(dòng)引用(一定會(huì)發(fā)生類的初始化)重點(diǎn)
- 當(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)用類的常量池中了)
package com.briup.chap13; ? /** * @author 35329 */ // 測(cè)試類什么時(shí)候會(huì)初始化 public class Test06 { static { System.out.println("mian類被加載"); } ? public static void main(String[] args) throws ClassNotFoundException { // 1、主動(dòng)被加載 // Son son = new Son(); ? // 反射也會(huì)產(chǎn)生主動(dòng)引用 // Class.forName("com.briup.chap13.Son"); ? // 不會(huì)產(chǎn)生類的引用的方法 // System.out.println("Son.b = " + Son.b); ? // Son[] arr = new Son[5]; ? System.out.println("Son.M = " + Son.M); } } ? class Father{ static int b = 2; static { System.out.println("父類被加載"); } } class Son extends Father{ static { System.out.println("子類被加載"); m = 300; } ? static int m = 100; static final int M = 1; } ?
類加載器的作用:
類加載器的作用:將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ì)象,作為方法區(qū)中類數(shù)據(jù)的訪問入口。
類緩存:標(biāo)準(zhǔn)的JavaSE類加載器可以按要求查找類,但一旦某個(gè)類被加載到類加載器中,它將維持加載(緩存)一段時(shí)間。不過JVM垃圾回收機(jī)制可以回收這些Class對(duì)象。
類加載器作用是用來把類(class)裝載進(jìn)內(nèi)存的。JVM規(guī)范定義了如下類型的類的加載器。
- 引導(dǎo)類加載器:用C++編寫,是JVM自帶的類加載器,負(fù)責(zé)Java平臺(tái)核心庫,用來裝載核心類庫。該加載器無法直接獲取。
- 擴(kuò)展類加載器:負(fù)責(zé)
jre/lib/ext
目錄下的jar包或-D java.ext.dirs
指定目錄下的jar包裝入工作庫 - 系統(tǒng)類加載器:負(fù)責(zé)
java -classpath 或 -D java.class.path
所指的目錄下的類與jar包裝入工作,是最常用的加載器。
package com.briup.chap13; ? import com.briup.chap07.test.bean.Cat; ? /** * @author 35329 */ public class Test07 { public static void main(String[] args) throws ClassNotFoundException { // 獲取系統(tǒng)類的加載器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println("systemClassLoader = " + systemClassLoader); ? // 獲取系統(tǒng)類加載器的父類加載器 --> 擴(kuò)展類加載器 ClassLoader parent = systemClassLoader.getParent(); System.out.println("parent = " + parent); ? // 獲取擴(kuò)展類加載器 --> 根加載器(C/c++) ClassLoader parent1 = parent.getParent(); System.out.println("parent1 = " + parent1); ? // 運(yùn)行結(jié)果: //systemClassLoader = sun.misc.Launcher$AppClassLoader@18b4aac2 //parent = sun.misc.Launcher$ExtClassLoader@14ae5a5 //parent1 = null ? // 測(cè)試當(dāng)前類是那個(gè)加載器加載的 ClassLoader classLoader = Class.forName("com.briup.chap13.Test07").getClassLoader(); System.out.println("classLoader = " + classLoader); ? // 測(cè)試JDK內(nèi)置的類是誰加載的 System.out.println("Class.forName(\"java.lang.Object\").getClassLoader() = " + Class.forName("java.lang.Object").getClassLoader()); ? // 運(yùn)行結(jié)果: //classLoader = sun.misc.Launcher$AppClassLoader@18b4aac2 //Class.forName("java.lang.Object").getClassLoader() = null ? // 如何獲得系統(tǒng)類加載器可以加載的路徑 System.out.println(System.getProperty("java.class.path")); ? // 運(yùn)行結(jié)果: /* * C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar; * C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar; * D:\JDProject\corejava\out\production\corejava; * C:\Users\35329\.m2\repository\junit\junit\4.12\junit-4.12.jar; * C:\Users\35329\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar; * D:\idea\ideaIU-2020\lib\idea_rt.jar * */ } } ?
獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
通過反射獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
Field、Method、Constructor、Superclass、Interface、Annotation
package com.briup.chap13; ? import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; ? /** * 通過反射獲得類的信息 * @author 35329 */ public class Test08 { public static void main(String[] args) throws Exception { Class c1 = Class.forName("com.briup.chap13.user"); ? // 獲得類的名字 System.out.println("c1.getName() = " + c1.getName()); // 獲得包名 + 類名 System.out.println("c1.getSimpleName() = " + c1.getSimpleName()); // 獲得類名 ? System.out.println("-----------------------------------"); // 獲得類的屬性 Field[] fields = c1.getFields(); // 只能找到public屬性 ? fields = c1.getDeclaredFields(); // 可以找到全部屬性 for (Field field : fields) { System.out.println(field); } ? // 獲得指定屬性的值 Field name = c1.getDeclaredField("name"); System.out.println("name = " + name); ? System.out.println("-----------------------------------------"); // 獲得類的方法 Method[] methods = c1.getMethods(); // 獲得本類及其父類的全部public方法 for (Method method : methods) { System.out.println("正常的:" + method); } ? Method[] declaredMethods = c1.getDeclaredMethods(); // 獲得本類的所有方法(包括私有的) for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } ? // 獲得指定方法 // 重載 Method getName = c1.getMethod("getName", null); Method setName = c1.getMethod("setName", String.class); System.out.println("getName = " + getName); System.out.println("setName = " + setName); ? System.out.println("--------------------------------"); // 獲得類的構(gòu)造器 Constructor[] constructors = c1.getConstructors(); // 獲得public修飾的構(gòu)造方法 for (Constructor constructor : constructors) { System.out.println(constructor); } constructors = c1.getDeclaredConstructors();// 獲得全部構(gòu)造方法 for (Constructor constructor : constructors) { System.out.println("#" + constructor); } ? //獲得指定的構(gòu)造器 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println("指定構(gòu)造器:" + declaredConstructor); } } ?
小結(jié):
- 在實(shí)際操作中,取得類的信息的操作代碼,不會(huì)經(jīng)常開發(fā)。
- 一定要熟悉java.lang.reflect包的作用,反射機(jī)制。
- 如何取得屬性、方法、構(gòu)造器的名稱,修飾符等。
有了Class對(duì)象,能做什么?
創(chuàng)建一個(gè)類的對(duì)象:調(diào)用Class對(duì)象的newInstance()方法
- 類必須有一個(gè)無參數(shù)的構(gòu)造器
- 類的構(gòu)造器的訪問權(quán)限需要足夠
思考?難道沒有無參的構(gòu)造器就不能創(chuàng)建對(duì)象了嗎?只要在操作的時(shí)候明確的調(diào)用類中的構(gòu)造器,并將參數(shù)傳遞進(jìn)去之后,才可以實(shí)例化操作。
步驟如下:
- 通過Class類的getDeclaredConstructor(Class ... parameterTypes)取得本類的指定形參類型的構(gòu)造器。
- 向構(gòu)造器的形參中傳遞一個(gè)對(duì)象數(shù)組進(jìn)去,里面包含了構(gòu)造器中所需的各個(gè)參數(shù)。
- 通過Constructor實(shí)例化對(duì)象。
通過指定的方法
通過反射,調(diào)用類中的方法,通過Method類完成
- 通過Class類的getMethod(String name, Class ... parameter Types)方法取得一個(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)用此invoke()方法前,顯示調(diào)用方法對(duì)象的setAccessible(true)方法,將可訪問private修飾的方法。
setAccessible
- Method和Field、Constructor對(duì)象都有setAccessible()方法
- setAccessible()作用是啟示和禁用訪問安全檢查的開關(guān)
- 參數(shù)值為true則指示反射的對(duì)象在使用時(shí)應(yīng)該取消Java語言訪問檢查。
- 提高反射的效率,如果代碼中必須用反射,而該句代碼需要頻繁的被調(diào)用,那么請(qǐng)?jiān)O(shè)置為true
- 使得原本無法訪問的私有成員也可以訪問
- 參數(shù)值為false則指示反射的對(duì)象應(yīng)該實(shí)施Java語言訪問檢查。
package com.briup.chap13; ? import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; ? /** * 分析性能問題 * @author 35329 */ public class Test10 { // 通過普通方法調(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 c1 = user.getClass(); Method getName = c1.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 c1 = user.getClass(); Method getName = c1.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(); } } ?
反射操作泛型(了解即可)
- 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ù)組類型
- TypeVariable:是各種類型變量的公共接口
- WildcardType:代表一種通配符類型表達(dá)式
反射操作注解
練習(xí):什么是ORM?
- Object relationship Mapping -->對(duì)象關(guān)系映射
- 類和表結(jié)構(gòu)的映射關(guān)系
- 類和表結(jié)構(gòu)對(duì)應(yīng)
- 屬性和字段對(duì)應(yīng)
- 對(duì)象和記錄對(duì)應(yīng)
package com.briup.chap13; ? import java.lang.annotation.*; import java.lang.reflect.Field; ? /** * 練習(xí)反射操作注解 * @author 35329 */ public class Test12 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class<?> c1 = Class.forName("com.briup.chap13.Student2"); ? // 通過反射獲取注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } ? // 獲得注解的Value值 TableBriup tableBriup = c1.getAnnotation(TableBriup.class); String value = tableBriup.value(); System.out.println(value); ? // 獲得類指定的注解 Field f = c1.getDeclaredField("name"); FieldBriup annotation = f.getAnnotation(FieldBriup.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); ? } } ? @TableBriup("db_student") class Student2{ ? @FieldBriup(columnName = "db_id", type = "int", length = 10) private int id; @FieldBriup(columnName = "db_age", type = "int", length = 10) private int age; @FieldBriup(columnName = "db_name", type = "varchar", length = 3) private String name; ? public Student2() { } ? public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } ? @Override public String toString() { return "Test12{" + "id=" + id + ", age=" + age + ", 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; } } ? // 類名注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableBriup{ String value(); } ? // 屬性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldBriup{ String columnName(); String type(); int length(); }
到此這篇關(guān)于Java中注解與反射的詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Java注解與反射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot日志配置SLF4J和Logback的方法實(shí)現(xiàn)
日志記錄是不可或缺的一部分,本文主要介紹了SpringBoot日志配置SLF4J和Logback的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04Postman form-data、x-www-form-urlencoded的區(qū)別及說明
這篇文章主要介紹了Postman form-data、x-www-form-urlencoded的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03Java8實(shí)現(xiàn)Stream流的合并的方法展示
本文介紹了Java8中Stream流的合并方法,包括concat()、flatMap()和reduce()三種方法。其中,concat()方法可以將兩個(gè)Stream流合并成一個(gè),flatMap()方法可以將一個(gè)Stream流中的元素映射成多個(gè)Stream流并合并成一個(gè),reduce()方法可以將Stream流中的元素逐個(gè)合并成一個(gè)結(jié)果2023-05-05Java Servlet線程中AsyncContext異步處理Http請(qǐng)求
這篇文章主要介紹了Java Servlet線程中AsyncContext異步處理Http請(qǐng)求及在業(yè)務(wù)中應(yīng)用,AsyncContext是Servlet 3.0使Servlet 線程不再需要一直阻塞,直到業(yè)務(wù)處理完畢才能再輸出響應(yīng),最后才結(jié)束該Servlet線程2023-03-03Java實(shí)現(xiàn)Html轉(zhuǎn)Pdf的方法
這篇文章主要介紹了Java實(shí)現(xiàn)Html轉(zhuǎn)Pdf的方法,實(shí)例分析了java基于ITextRenderer類操作頁面及系統(tǒng)自帶字體生成pdf文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07