JavaSE:注解與反射(Annotation & Reflection)
? 注解和框架是所有框架的底層,如Mybatis,spring,框架的底層實作機制就是注解和反射,注解相比于注釋,除了能較為直接的表示出這部分模塊的功能,也能實作一定的具體功能,
01 初識注解
1.1 什么是注解
-
Annotation是從JDK5.0引入的新技術
-
Annotation的作用:
-
不是程式本身,但可以對程式做出解釋,(這一點和注釋comment沒什么區別)
-
可以被其他程式(比如:編譯器等)讀取,
-
-
Annotation的格式:
- 注解是以“@注釋名”在代碼中存在的,還可以添加一定引數值,如
@Override
@SuppressWarnings(Value="https://www.cnblogs.com/wutong666/archive/2023/04/01/unchecked")
- Annotation在哪里使用?
- 可以附加在package,class,method,field等上面,相當于給他們添加了額外的輔助資訊,我們可以通過反射機制編程實作對這些元資料的訪問,
1.2 內置注解
-
@Override- 定義在java.lang.Overide中,此注釋只適用于修辭方法,表明一個方法宣告打算重寫超類中的另一個方法申明,
-
@Deprecated- 定義在java.lang.Deprecated中,此注釋可適用于修辭方法,屬性,類,表示不鼓勵程式員使用這樣的元素,通常因為該方法比較危隙訓者存在更好的方法
-
@SupperWarnings-
定義在java.lang.SupperWarnings中,用來抑制編譯時的告警資訊,
-
但與前兩個注解不同的是,這個注解需要引數,具體如何設定參看JDK說明檔案
-
@SupperWarnings("all") -
@SupperWarnings("unchecked") -
@SupperWarning("unchecked","deprecation") -
等等....
-
1.3 元注解
-
元注解的作用就是負責注解其他注解,Java定義了4個標準的meta-annotation型別,它們被用來提供對其他annotation型別做說明,
-
這些型別和它們所支持的類在java.kang.annotation包中可以找到(@Target,@Retention,@Documented,@inherited)
-
@Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
-
@Retention:表示需要在什么級別保存該注釋資訊,用于描述注解的生命周期
- (SOURCE(源代碼)<CLASS(Javac編譯檔案)<RUNTIME(軟體運行))
-
@Documented:說明該注解將被包含在javadoc中
-
@Inherited:說明子類可以繼承父類中的注解
-
1.4 自定義注解
-
使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation介面
-
分析:
-
@interface用來宣告一個注解,格式:public @interface 注解名
- 需要注意,如果該檔案中已有public,則注解定義需去掉public,一個檔案中只能有一個public方法
-
其中的每一個方法實際上是宣告了一個配置引數
-
方法的名稱就是引數的名稱
-
回傳值型別就是引數的型別(回傳值只能是基本型別,Class,String,enum)
-
可以通過添加default來宣告引數的默認值
-
如果只有一個引數成員,一般引數名為value
- 如果引數名為value,則可以在賦值時省略"value = "https://www.cnblogs.com/wutong666/archive/2023/04/01/,直接寫賦值內容即可
-
注解元素必須要有值,我們定義注解元素時,經常使用空字串,0作為默認值
-
02 反射
2.1 反射概述
2.1.1 靜態 vs 動態語言
-
動態語言
-
是一類在運行時可以改變其結構的語言,例如新的函式、物件、甚至代碼可以被引進,已有的函式可以被洗掉或是其他結構上的變化,通俗點說就是在運行時代碼可根據某些條件改變自身結構,
-
主要動態語言:Object-C、C#、JavaScript、PHP、Python等
-
-
靜態語言
- 與動態語言相對應,運行時結構不可變的語言就是靜態語言,如Java、C、C++,
-
Java不是動態語言,但Java可以稱之為“準動態語言”,即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性,Java的動態性讓編程的時候更加靈活!
2.1.2 Java Reflection
-
Reflection(反射)是Java被視為“準動態語言的”關鍵,反射機制允許程式在執行期借助Reflection API取得任何類的內部資訊,并能直接操作任意物件的內部屬性及方法,
Class c = Class.forName("java.lang.String") -
加載完類之后,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊,我們可以通過這個物件看到類的結構,這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,人們形象稱之為反射

2.1.3 Java反射機制研究及應用
-
Java反射機制提供的功能:
- 在運行時判斷任意一個物件所屬的類
- 在運行時構造任意一個類的物件
- 在運行時判斷任意一個類所具有的的成員變數和方法
- 在運行時獲取泛型資訊
- 在運行時呼叫任意一個物件的成員變數和方法
- 在運行時處理注解
- 生成動態代理
-
Java反射的優點和缺點
-
優點:
- 可以實作動態創建物件和編譯,體現出很大的靈活性
-
缺點:
- 對性能會有影響,使用反射基本上是一個解釋操作,我們可以告訴JVM,我們希望做什么并且讓它滿足我們的需求,這類操作總是慢于直接執行相同的操作,
-
-
反射相關的主要API
-
java.lang.Class:代表一個類 -
java.lang.reflect.Method:代表類的方法 -
java.lang.reflect.Field:代表類的成員變數 -
java.lang.reflect.Constructor:代表類的構造器
-
-
Class類
-
在Object類中定義了如下的方法,此方法將被所有子類繼承
public final Class getClass() -
以上的回傳值型別是一個Class類,此類是Java反射的源頭,實際上所謂反射從程式的運行結果來看也很好理解,即:可以通過物件反射求出類的名稱,
-
物件照鏡子后可以得到的資訊包括:某個類的屬性、方法和構造器、某個類到底實作了哪些介面,對于每個類而言,JRE都為其保留一個不變的Class型別的物件,一個Class物件包含了特定的某個結構(class/interface/enum/annotation/primitive type/void/[])的有關資訊
- Class本身也是一個類
- Class物件只能由系統建立物件
-

-
哪些型別可以有Class物件?
-
class:外部類,成員(成員內部類,靜態內部類),區域內部類、匿名內部類
-
interface:介面
-
[]:陣列
-
enum:列舉
-
annotation:注解@interface
-
primitive type:基本資料型別
-
void
-
2.2 Java記憶體分析
2.2.1 Java記憶體分配

2.2.2 類的加載程序
? 當程式主動使用某個類時,如果該類還未被加載到記憶體中,則系統會通過如下三個步驟來對該類進行初始化,

2.2.3 類加載器
-
類加載器的作用:將class檔案位元組碼內容加載到記憶體中,并將這些靜態資料轉換成方法區的運行時資料結構,然后在堆中生成一個代表這個類的java.lan.Class物件,作為方法區中類資料的訪問入口,
-
類快取:標準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(快取)一段時間,不過JVM垃圾回識訓制可以回收這些Class物件,

-
JVM規范定義了如下型別的類的加載器:
-
引導類加載器:用C++撰寫的,是JVM自帶的類加載器,負責Java平臺核心庫,用來裝載核心類別庫,需注意此庫無法直接獲取,
-
擴展類加載器:負責jre/lib/ext目錄下的jar包或-D java.ext.dirs指定目錄下的jar包裝入作業庫
-
系統類加載器:負責java -classpath或-D java.class.path所指的目錄下的類與jar包裝入作業,是最常用的加載器,

-
-
雙親委派機制:是防止同名包、類與 jdk 中的相沖突,實際上加載類的時候,先通知 appLoader,看 appLoader 是否已經快取,沒有的話,appLoader 又委派給他的父類加載器(extLoader)詢問,看他是不是能已經快取加載,沒有的話,extLoader 又委派他的父類加載器(bootstrapLoader)詢問,BootstrapLoader看是不是自己已快取或者能加載的,有就加載,沒有再回傳 extLoader,extLoader 能加載就加載,不能的話再回傳給 appLoader 加載,再回傳的路中,誰能加載,加載的同時也加快取里,正是由于不停的找自己父級,所以才有 Parents 加載機制,翻譯過來叫 雙親委派機制
2.2.4 類的加載與ClassLoader的理解
-
加載:將class檔案位元組碼加載到記憶體中,并將這些靜態資料轉換成方法區的運行時資料結構,然后生成一個代表這個類的java.lang.Class物件
-
鏈接:將Java類的二進制代碼合并到JVM的運行狀態之中的程序
- 驗證:確保加載的類的資訊符合JVM規范,沒有安全方面的問題
- 準備:正式為類變數(static)分配記憶體并設定類變數默認初始值的階段,這些記憶體都將在方法區中進行分配,
- 決議:虛擬機常量池內的符號參考(常量名)替換為直接參考(地址)的程序
-
初始化:
-
執行類構造器
()方法,類構造器 ()方法是由編譯器自動收集類中所有類變數的賦值動作和靜態代碼塊中的陳述句合并產生的,(類構造器是構造類資訊的,不是構造該類物件的構造器) -
當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化,
-
虛擬機會保證一個類的
()方法在多執行緒環境中被正確加鎖和同步 
-
2.2.5 獲取運行時類的完整結構
-
通過反射
-
包括如下資訊:Field、Method、Constructors、Superclass、Interface、Annotation
-
實作的全部介面
-
所繼承的父類
-
全部的構造器
-
全部的方法
-
全部的Field
-
注解···
-
小結
-
在實際的操作中,取得類的資訊的操作代碼,并不會經常開發
-
一定要熟悉java.lang.reflect包的作用和反射機制
-
如何取得屬性、方法、構造器的名稱,修飾等,
2.3 動態創建物件執行方法
-
創建類的物件:呼叫Class物件的newInstance()方法
-
1)類必須有一個無引數的構造器,(無參構造器必須有)
-
2)類的構造器的訪問權限需要足夠
-
-
除了呼叫無參構造器創建物件外,也可以
-
1)通過Class類的getDeclaredConstructor(Class...parameterTypes)取得本類的制定形參型別的構造器
-
2)向構造器的形參中傳遞一個物件陣列進去,里面包含了構造器中所需的各個引數
-
3)通過Constructor實體化物件
-
-
利用反射呼叫指定的方法
通過反射,呼叫類中的方法,通過Method類完成,
-
通過Class類的getMethod(String name,Class...parameterTypes)方法取得一個Method物件,并設定此方法操作時所需要的引數型別(防止出現方法重寫,利用引數型別和方法名確定具體的方法)
-
之后使用Object invoke(Object obj , Object[] args)進行呼叫,并向方法中傳遞要設定的obj物件的引數資訊,

-
Object invoke(Object obj,Object...args)
-
Object對應原方法的回傳值,若原方法無回傳值,此時回傳null
-
原方法若為靜態方法,此時形參Object obj可為null
-
若原方法形參串列為空,則Object[] args為null
-
若原方宣告為private,則需要在呼叫次invoke()方法前,顯式呼叫方法物件的setAccessible(true),將可訪問private的方法
-
-
SetAccessible
-
Method和Field、Constructor物件都有setAccesible()方法,
-
setAccessible作用是啟動和禁用訪問安全檢查的開關,
-
引數值為true則指示反射的物件在使用時應當取消Java語言訪問檢查,
-
提高反射的效率,如果代碼中必須用反射,而該句代碼需要頻繁被呼叫,那么設定為true,
-
使得原本無法訪問的私有成員也可以訪問,
-
-
引數值為false則指示反射的物件應該實施Java語言訪問檢查
-
-
2.4 反射操作泛型
-
Java采用泛型擦除機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保資料的安全性和免去強制型別轉換的問題,但是,一旦編譯完成,所有和泛型有關的型別全部擦除
-
為了通過反射操作這些型別,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種型別來代表不能被歸一到Class類中的型別但是又和原始型別齊名的型別
-
ParameterizedType:表示一種引數化型別,如Collection
-
GenericArrayType:表示一種元素型別是引數化型別或者型別變數的陣列型別
-
TypeVariable:是各種型別變數的公共父介面
-
WildcardType:代表一種通配符型別運算式
-
2.5 反射操作注解
- 這部分很重要,開始和后面框架出現結合(熟知注解對于后期框架學習很重要)
- 放一個案例在這里說明相關功能
- 日后將在框架學習筆記中進一步闡述用反射操作注解的重要性
- 先定義一個類注解(關注Target里面引數設定)
//類名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeTong{
String value();
}
- 再定義一個屬性的注解(需要注意自定義的兩個注解都有引數)
//屬性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTong{
String colunmName();
String type();
int length();
}
- 此后定義一個student類,用于后面的反射
@TypeTong(value = "https://www.cnblogs.com/wutong666/archive/2023/04/01/db_student")
class student2{
@FieldTong(colunmName = "db_name", type = "varchar",length = 3 )
private String name;
@FieldTong(colunmName = "db_id", type = "int",length = 10 )
private int id;
@FieldTong(colunmName = "db_age", type = "int",length = 10 )
private int age;
public student2() {
}
public student2(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;
}
@Override
public String toString() {
return "student2{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
- main函式編制
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class s1 = Class.forName("AnnotationandReflection.Demo03.student2");
//通過反射獲得注解
Annotation[] annotations = s1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//通過反射獲得指定注解屬性值
System.out.println("==========================");
TypeTong typeTong = (TypeTong)s1.getAnnotation(TypeTong.class);
String value = https://www.cnblogs.com/wutong666/archive/2023/04/01/typeTong.value();
System.out.println(value);
//獲得類指定的注解
System.out.println("==========================");
Field f = s1.getDeclaredField("id");
FieldTong annotation = f.getAnnotation(FieldTong.class);
System.out.println(annotation.colunmName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
- 程式運行后結果如下:

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548890.html
標籤:其他
上一篇:三天吃透MySQL面試八股文
下一篇:一次線上OOM問題的個人復盤
