JAVA注解和反射(笔记)
注解簡介
Annotation是從JDK5.0開始引入的新技術).
Annotation的作用 :
- 不是程序本身(可以對程序作出解釋.(這一點和注釋(comment)沒什么區別)
- 可以被其他程序(比如:編譯器等)讀取.
Annotation的格式:
注解是以"@注釋名"在代碼中存在的, 還可以添加一些參數值,例如:@SuppressWarnings(value=“unchecked”).
Annotation在哪里使用?
可以附加在package , class , method , field等上面,相當于給他們添加了額外的輔助信息,我們可以通過反射機制編程實現對這些元數據的訪問。
內置注解
@Override : 定義在java.lang.Override中,此注釋只適用于修辭方法, 表示一個方法聲明打算重寫超類中的另一個方法聲明.
@Deprecated :定義在java.lang.Deprecated中,此注釋可以用于修辭方法,屬性,類,表示不鼓勵程序員使用這樣的元素,通常是因為它很危險或者存在更好的選擇.
@SuppressWarnings : 定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告信息,與前兩個注釋有所不同,你需要添加一-個參數才能正確使用,這些參數都是已經定義好了的,我們選擇性的使用就好了.
@SuppressWarnings(“l”)
@SuppressWarnings(“unchecked”)
@SuppressWarnings(value={“unchecked”,“deprecation”})
元注解
- @Target :用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
- @Retention :表示需要在什么級別保存該注釋信息,用于描述注解的生命周期
- (SOURCE 源碼< CLASS < RUNTIME)
- @Document:說明該注解將被包含在javadoc中
- @Inherited: 說明子類可以繼承父類中的該注解
自定義注解
使用@interface自定義注解時,自動繼承了java.lang .annotation.Annotation接口
分析:
- @ interface用來聲明一個注解,格式: public @ interface注解名{定義內容}
- 其中的每一個方法實際 上是聲明了一個配置參數.
- 方法的名稱就是參數的名稱.
- 返回值類型就是參數的類型(返回值只能是基本類型,Class , String , enum ).
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-q65W1rPq-1608789860775)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224003234223.png)]
- 可以通過default來聲明參數的默認值
- 如果只有一個參數成員, 一般參數名為value //只有value才能省略參數名
- 注解元素必須要有值,我們定義注解元素時,經常使用空字符串,0作為默認值.
靜態VS動態語言
動態語言
靜態語言
Java Reflection
Reflection (反射)是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助于Reflection API取得任何類的內部信息,并能***直接操作任意對象的內部屬性及方法***。
Class C= Class forName("java.lang String")加載完類之后,在堆內存的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一-面鏡子, 透過這個鏡子看到類的結構,所以我們形象的稱之為:反射。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1PXhtR2x-1608789860779)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224095555221.png)]
Java反射機制研究及應用
Java反射機制提供的功能
Java反射優點和缺點
反射相關的主要API
獲得反射對象
package com.kuang.demo01; public class Test02 {public static void main(String[] args) throws ClassNotFoundException {/*通過反射獲取類的Class對象,*一個類只有一個Class對象,所以c1,c2,c3的hashcode相同*一個類被加載后,整個類的結構都會被封裝在Class對象中*/Class c1 = Class.forName("com.kuang.demo01.User");System.out.println(c1.getName());//com.reflection.UserClass c2 = Class.forName("com.kuang.demo01.User");System.out.println(c2.hashCode());Class c3 = Class.forName("com.kuang.demo01.User");System.out.println(c3.hashCode());}}class User{private String name;private int id;private int age;public User(){}public User(String name, int id, int age) {this.name = name;this.id = id;this.age = age;}public String getName() {return name;}public void setName(String name) {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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", id=" + id +", age=" + age +'}';} }Class類
- 在Object類中定義了以下的方法,此方法將被所有子類繼承
- 以上的方法返回值的類型是一 個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。
- 對象照鏡子后可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些接口。
- 對于每個類而言,JRE都為其保留一個不變的Class類型的對象。-一個Class對象包含了特定某個結構(class/interface/enum/annotation/primitive type/void/[)的有關信息。
- Class本身也是一個類
- Class 對象只能由系統建立對象
- 一個加載的類在JVM中只會有一 個Class實例
- 一個Class對象對應的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個Class實例所生成
- 通過Class可以完整地得到一個類中的所有被加載的結構
- Class類 是Reflection的根源,針對任何你想動態加載、運行的類) 唯有先獲得相應的Class對象
Class類的常用方法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-97hc1j9Y-1608789860781)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224101518888.png)]
獲取Class類的實例
a)若已知具體的類, 通過類的class屬性獲取, 該訪法最為安全可靠,程序性能最高。
Class class = Person.class;b)已知某個類的實例, 調用該實例的getClass()方法獲取Class對象
Class class = person.getClass();c)已知一 個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取可能拋出ClassNotFoundException
Class class = Class.forName("demo01.Student");d)內置基本數據類型可以直接用類名.Type
e)還可以利用ClassLoader我們之 后講解
package com.kuang.demo01; public class Test03{public static void main(String[] args) throws ClassNotFoundException {Person s1 = new Student();System.out.println("這個人是"+s1.name);//方式一:通過對象獲取Class c1 = s1.getClass();System.out.println(c1.hashCode());//方式二:通過forname獲取Class c2 = Class.forName("com.kuang.demo01.Student");System.out.println(c2.hashCode());//通過類名.class獲得Class c3 = Student.class;System.out.println(c3.hashCode());//方式四:基本內置類型的包裝類都有一個TYPE屬性Class c4 = Integer.TYPE;System.out.println(c4);//獲得父類類型Class c5 = c1.getSuperclass();System.out.println(c5);} } class Person{public String name;public Person(){}public Person(String name){this.name = name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';} } class Student extends Person{public Student(){this.name = "Student";} } class Teacher extends Person{public Teacher(){this.name = "Teacher";} }有Class對象的類型
- class: 外部類成員(成員內部類,靜態內部類),局部內部類,匿名內部類。
- interface: 接口
- []:數組
- enum:枚舉
- annotation:注解@interface
- primitive type:基本數據類型
- void
Alt加鼠標滾輪(多行單列CV)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-u9SBbsk4-1608789860783)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224104141619.png)]
JAVA內存分析
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OJsmiRX3-1608789860785)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224104608420.png)]
類的加載與ClassLoader的理解
加載:
將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后生成一個代表這個類的java.lang.Class對象.
鏈接:
將Java類的二進制代碼合并到JVM的運行狀態之中的過程。
- 驗證:確保加載的類信息符合JVM規范,沒有安全方面的問題
- 準備:正式為類變量(static) 分配內存并設置類變量默認初始值的階段,這些內存都將在方法區中進行分配。
- 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程。
初始化:
- 執行類構造器< clinit> ()方法的過程。類構造器< clinit> ()方法是由編譯期自動收集類中所有類變量的賦值動作和靜態代碼塊中的語句合并產生的。(類構造 器是構造類信息的,不是構造該類對象的構造器)。
- 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。
- 虛擬機會保證一 個類的 ()方法在多線程環境中被正確加鎖和同步。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fFPjcKkb-1608789860786)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224110150937.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VNVNjvSa-1608789860786)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224105950235.png)]
package com.kuang.demo01;import java.lang.annotation.ElementType; public class Test04 {public static void main(String[] args) {A a=new A();System.out.println(A.m);//100/*1.加載到內存,產生一個類對應Class對象2.鏈接,鏈接結束后m=03.初始化<clinit>(){System. out . println( "A類靜態代碼塊初始化") ;m = 300;m=100;}m=100*/} } class A {static {System.out.println("A類靜態代碼塊初始化");m = 300;}static int m = 100;public A() {System.out.println("A類的參構造初始化");} }類初始化發生時間
類的主動引用(一定會發生類的初始化))
- 當虛擬機啟動,先初始化main方法所在的類
- new一個類的對象
- 調用類的靜態成員(除了final常量)和靜態方法
- 使用java.lang.reflect包的方法對類進行反 射調用
- 當初始化一個類, 如果其父類沒有被初始化,則先會初始化它的父類
類的被動引用(不會發生類的初始化)
- 當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜態變量,不會導致子類初始化
- 通過數組定義類引用,不會觸發此類的初始化
- 引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)
類加載器
類加載的作用:將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后在堆中生成一個代表這個類的java.lang(Class對象)作為方法區中類數據的訪問入口。
類緩存:標準的JavaSE類加載器可以按要求查找類,但一旦某企類被加載到類加載器中,它將維持加載(緩存) 一段時間。不過JVM垃圾回收機制可以回收這些Class對象
類加載器作用是用來把類(class)裝載進內存的。
雙親委派機制:如果自己定義和jdk同名的類,運行時虛擬機會在系統的類加載器中尋找,再去擴展類加載器中尋找,再去根加載器中尋找,如果存在同名的類,會使用根加載器中的類,而不使用自己定義的類(保證安全性)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Fr0RcHcm-1608789860787)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224112320572.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Hd6KjVQy-1608789860788)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224113637305.png)]
創建運行時類的對象
獲取運行時類的完整結構
- 實現的全部接口
- 所繼承的父類
- 全部的構造器
- 全部的方法
- 全部的Field
- 注解
小結
Class對象作用
創建類的對象:
調用Class對象的**newInstance()**方法
-
類必須有一個無參數的構造器。
-
類的構造器的訪問權限需要足夠
思考?難道沒有無參的構造器就不能創建對象了嗎?
只要在操作的時候明確的調用類中的構造器,并將參數傳遞進去之后,才可以實例化操作。步驟如下:
1)通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參類型的構造器
2)向構造器的形參中傳遞一一個對象數組進去,里面包含了構造器中所需的各個參數。
3)通過Constructor實例化對象
調用指定的方法
通過反射,調用類中的方法,通過Method類完成。
①通過Class類的getMethod(String name,Clas…parameterTypes)方法取得一個Method對象,并設置此方法操作時所需要的參數類型。
②之后使用Object invoke(Object obj, Object[] args)進行調用,并向方法中傳遞要設置的obj對象的參數信息。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PMHx0vq3-1608789860789)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201224132215782.png)]
invoke
Object invoke(Object obj, Object ... args)- Object對應原方法的返回值,若原方法無返回值,此時返回nul)
- 若原方法若為靜態方法,此時形參0bject obj可為null
- 若原方法形參列表為空,則Object[] args為null
- 若原方法聲明為private,則需要在調用此invoke()方法前,顯式調用方法對象的setAccessible(true)方法,將可訪問private的方法。
setAccessible
- Method和Field、Constructor對象都有setAccessible()方法。
- setAccessible作用是啟動和禁用訪問安全檢查的開關。
- 參數值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。
- 提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被調用,那么請設置為true。
- 使得原本無法訪問的私有成員也可以訪問
- 參數值為false則指示反射的對象應該實施Java語言訪問檢查
性能對比分析
package com.kuang.demo01;import com.kuang.demo01.User;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;public class Test09 {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {test1();//5mstest2();//4114mstest3();//1483ms}public static void test1(){User user = new User();long start = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {user.getName();}long end = System.currentTimeMillis();System.out.println(end-start+"ms");}public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c1 = user.getClass();Method getName = c1.getDeclaredMethod("getName",null);long start = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user,null);}long end = System.currentTimeMillis();System.out.println(end-start+"ms");}public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c1 = user.getClass();Method getName = c1.getDeclaredMethod("getName",null);getName.setAccessible(true);long start = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user,null);}long end = System.currentTimeMillis();System.out.println(end-start+"ms");} }反射操作泛型
- ParameterizedType :表示- -種參數化類型,比如Collection
- GenericArrayType :表示一種元素類型是參數化類型或者類型變量的數組類型
- TypeVariable :是各種類型變量的公共父接口
- WildcardType :代表-種通配符類型表達式
反射操作注解
總結
以上是生活随笔為你收集整理的JAVA注解和反射(笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吃你电脑内存的罪魁祸首找到了,就是它
- 下一篇: JAVASE阶段流程图