上一章我講了連接池,本來是說要講注解的,講注解的話要講好多鋪墊才行,算了試著說一說了,
注解及反射的使用
- 一、注解是什么?
- 二、怎么使用
- @Target
- @Retention
- @Documented
- @Inherited
- 注解示例
- 三、反射是什么?
- 反射機制的相關類
- 反射示例
- 反射與注解的配合使用
- 更多玩法
好的,下面正式開始
一、注解是什么?
當然了,這個意義還是要講,畢竟我一開始對這個注解也很陌生,注解?一想想到注釋了
注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制,Java 語言中的類、方法、變數、引數和包等都可以被標注,和 Javadoc(也就是所說的檔案注釋) 不同,Java 標注可以通過反射獲取標注內容,在編譯器生成類檔案時,標注可以被嵌入到位元組碼中,Java 虛擬機可以保留標注內容,在運行時可以獲取到標注內容 , 當然它也支持自定義 Java 標注,
上面這個太官方了點
我就用土鱉的言語說下,注釋 是為了方便我們閱讀代碼而標注的,而注解的出現我認為是方便我們去實作一些自定義的功能,或者說是方便一些功能的實作,使得我們能夠根據注解上的value去操作到對應的屬性,或找到對應的類,這使得代碼變得更為靈活,
二、怎么使用
這個注解它是用4個元注解去修飾這個注解,也就是說用元注解規定我們這個自定義注解的作用域等,有下面這四個元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
@Target
@Target說明了Annotation所修飾的物件范圍:Annotation可被用于 packages、types(類、介面、列舉、Annotation型別)、
型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如回圈變數、catch引數),
在Annotation型別的宣告中使用了target可更加明晰其修飾的目標,
1.CONSTRUCTOR:用于描述構造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述區域變數
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述引數
7.TYPE:用于描述類、介面(包括注解型別) 或enum宣告
@Retention
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;
而另一些卻被編譯在class檔案中;編譯在class檔案中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取
(請注意并不影響class的執行,因為Annotation與class在使用上是被分離的),使用這個meta-Annotation可以對 Annotation的“生命周期”限制,
1.SOURCE:在源檔案中有效(即源檔案保留)
2.CLASS:在class檔案中有效(即class保留)
3.RUNTIME:在運行時有效(即運行時保留)
@Documented
@Documented指示某一型別的注釋將通過 javadoc 和類似的默認工具進行檔案化,應使用此型別來注釋這些型別的宣告:其注釋會影響由其客戶端注釋的元素的使用,
如果型別宣告是用 Documented 來注釋的,則其注釋將成為注釋元素的公共 API 的一部分,
@Inherited
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的型別是被繼承的,如果一個使用了@Inherited修飾的annotation型別被用于一個class,則這個annotation將被用于該class的子類,
注意:@Inherited annotation型別是被標注過的class的子類所繼承,類并不從它所實作的介面繼承annotation,方法并不從它所多載的方法繼承annotation,
當@Inherited annotation型別標注的annotation的Retention是RetentionPolicy.RUNTIME,
則反射API增強了這種繼承性,如果我們使用java.lang.reflect去查詢一個@Inherited annotation型別的annotation時,反射代碼檢查將展開作業:檢查class和其父類,
直到發現指定的annotation型別被發現,或者到達類繼承結構的頂層,
我們使用的時候主要還是使用前面兩個,@Target就是告訴編譯器,我這個是修飾在類上還是構造方法還是哪里,@Retention就是告訴編譯器,我的生命周期有多長,在哪里死去,
注解示例
其實要真正使用上注解,至少還需要搭配上泛型,反射的知識,那我下面先簡單寫兩段代碼,看一下這個自定義注解怎么寫出來,后面再介紹下反射,泛型的話我想,大家就自己學一下吧,哈哈,有的人可能早就知道了,
/**
*
* @author T_Antry
* @describe 用于與資料庫對應
* @target - 標記這個注解應該是那種java成員:類,欄位、屬性、方法
* @Retention - 標識這個注解怎么保存,只在代碼中,還是編入class檔案中,或者是在運行時可以反射
* @Documented - 標記這些注解是否包含在用戶檔案中
* @Inherited - 標記這個注解是繼承哪個注解類(默認注解沒有繼承于任何注解)
* return
* 2020年8月11日
*/
@Target(ElementType.FIELD)//宣告是修飾屬性的
@Retention(RetentionPolicy.RUNTIME)//通常都是選這個屬性,運行時存活,因為一般要配合反射使用
@Documented
@Inherited
public @interface MyField {
String value();//宣告了一個value的key她的值是string
}
一般默認都要有一個value,怎么說呢,用于我們通過value去查找到對應的類或者屬性,
現在看一下我寫的一個模板類,并將上面寫的注解放在屬性上方
/**
*
* @author T_Antry
* @describe 地區模板類
* T_Antry-0718
* 2020年7月19日
*/
public class Area {
@MyField("id")
private int id;
@MyField("pid")
private int pid;
@MyField("name")
private String name;
public Area() {
}
public Area(int id, int pid, String name) {
super();
this.id = id;
this.pid = pid;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
看到這個其實,還沒有任何意義,甚至不知道要干嘛,什么鬼注解有屁用,是的,還是毫無卵用,所以這里還是要介紹一下反射,
三、反射是什么?
反射是java高級特性
JAVA反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制,
也就是拿到一個類或物件,就可以通過反射獲取它的所有屬性等,即使你不知道這個類有些什么,
反射機制的相關類
那反射可以拿到類,屬性,方法,構造,這些在java中我們也可以稱之為物件,既然可以稱之為物件,那么他們都有對應的類吧,是的
| 類名 | 對應 |
|---|---|
| Class | 代表類的物體,在運行的Java應用程式中表示類和介面 |
| Field | 代表類的成員變數(成員變數也稱為類的屬性) |
| Method | 代表類的方法 |
| Constroctor | 代表類的構造方法 |
獲得類相關的方法
| 方法 | 用途 |
|---|---|
| asSubclass(Class clazz) | 把傳遞的類的物件轉換成代表其子類的物件 |
| Cast | 把物件轉換成代表類或是介面的物件 |
| getClassLoader() | 獲得類的加載器 |
| getClasses() | 回傳一個陣列,陣列中包含該類中所有公共類和介面類的物件 |
| getDeclaredClasses() | 回傳一個陣列,陣列中包含該類中所有類和介面類的物件 |
| forName(String className) | 根據類名回傳類的物件 |
| getName() | 獲得類的完整路徑名字 |
| newInstance() | 創建類的實體 |
| getPackage() | 獲得類的包 |
| getSimpleName() | 獲得類的名字 |
| getSuperclass() | 獲得當前類繼承的父類的名字 |
| getInterfaces() | 獲得當前類實作的類或是介面 |
獲得類中屬性相關的方法
| 方法 | 用途 |
|---|---|
| getField(String name) | 獲得某個公有的屬性物件 |
| getFields() | 獲得所有公有的屬性物件 |
| getDeclaredField(String name) | 獲得某個屬性物件 |
| getDeclaredFields() | 獲得所有屬性物件 |
獲得類中注解相關的方法
| 方法 | 用途 |
|---|---|
| getAnnotation(Class <A> annotationClass) | 回傳該類中與引數型別匹配的公有注解物件 |
| getAnnotations() | 回傳該類所有的公有注解物件 |
| getDeclaredAnnotation(Class <A> annotationClass) | 回傳該類中與引數型別匹配的所有注解物件 |
| getDeclaredAnnotations() | 回傳該類所有的注解物件 |
獲得類中方法相關的方法
| 方法 | 用途 |
|---|---|
| getConstructor(Class…<?> parameterTypes) | 獲得該類中與引數型別匹配的公有構造方法 |
| getConstructors() | 獲得該類的所有公有構造方法 |
| getDeclaredConstructor(Class…<?> parameterTypes) | 獲得該類中與引數型別匹配的構造方法 |
| getDeclaredConstructors() | 獲得該類所有構造方法 |
獲得類中構造器相關的方法
| 方法 | 用途 |
|---|---|
| getMethod(String name, Class…<?> parameterTypes) | 獲得該類某個公有的方法 |
| getMethods() | 獲得該類所有公有的方法 |
| getDeclaredMethod(String name, Class…<?> parameterTypes) | 獲得該類某個方法 |
| getDeclaredMethods() | 獲得該類所有方法 |
類中其他重要的方法
| 方法 | 用途 |
|---|---|
| isAnnotation() | 如果是注解型別則回傳true |
| isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定型別注解型別則回傳true |
| isAnonymousClass() | 如果是匿名類則回傳true |
| isArray() | 如果是一個陣列類則回傳true |
| isEnum() | 如果是列舉類則回傳true |
| isInstance(Object obj) | 如果obj是該類的實體則回傳true |
| isInterface() | 如果是介面類則回傳true |
| isLocalClass() | 如果是區域類則回傳true |
| isMemberClass() | 如果是內部類則回傳true |
Field類
Field代表類的成員變數(成員變數也稱為類的屬性),
| 方法 | 用途 |
|---|---|
| equals(Object obj) | 屬性與obj相等則回傳true |
| get(Object obj) | 獲得obj中對應的屬性值 |
| set(Object obj, Object value) | 設定obj中對應屬性值 |
Method類
| 方法 | 用途 |
|---|---|
| invoke(Object obj, Object… args) | 傳遞object物件及引數呼叫該物件對應的方法 |
Constructor類
| 方法 | 用途 |
|---|---|
| newInstance(Object… initargs) | 根據傳遞的引數創建類的物件 |
反射示例
首先我先寫一個模板類
/**
*
* @author T_Antry
* @describe 學生模板類
* T_Antry-Test
* 2020年9月7日
*/
public class Student {
private int id;
private String name;
public void doSome() {
}
public Student() {
// TODO Auto-generated constructor stub
}
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
非常簡單測驗類,只測驗屬性,其它的暫且不多講,認真理解一下屬性,其它的內容是一樣的,也希望通過這樣比較簡潔的介紹,能夠更清楚地了解如何配合注解的使用,
/**
*
* @author T_Antry
* @describe 反射測驗
* T_Antry-Test
* 2020年9月9日
*/
public class Test {
/**
*
* @param args
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @describe
*/
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Student st = new Student(520, "T_Antry");//創建一個實體
System.out.println("----------Begin");
System.out.println(st);
System.out.println("----------");
/**
* 首先我們要拿到類
*/
Class<?> c = st.getClass();
Field[] fArr = c.getDeclaredFields();//獲取本類所有屬性,包括私有屬性
// fArr = c.getFields();//只獲取公有屬性,且包括繼承來的屬性
for (Field field : fArr) {
field.setAccessible(true);//注意,我們的模板類寫的是幾個私有成員,這條是開啟權限,使得我們都能夠操作和讀取私有屬性的值
System.out.println(field.get(st));
}
}
}
我們看下運行的結果

可以看到我們通過反射,拿到類中的所有屬性,雖然我這邊只寫了私有屬性,大家可以自己寫點有公有屬性的,也可以寫一些父類,可以看到getDeclaredFields是可以獲得當前類的所有屬性,getFields是可以獲得當前類以及繼承的所有父類的公有屬性,
拿到屬性之后,我們可以遍歷屬性并查看傳入物件的屬性值,但是這邊要注意的是需要呼叫**setAccessible(true)**方法使得我們可以查看私有成員,
反射與注解的配合使用
首先在Student類中,我找一個屬性加上前面注解示例中寫的Myfield注解,例如我在name屬性上加一個注解,且給一個value為"fuck"
想了想還是讓大家看下我修改后的類吧
import anno.MyField;
/**
*
* @author T_Antry
* @describe 學生模板類
* T_Antry-Test
* 2020年9月7日
*/
public class Student {
private int id;
@MyField("fuck")
private String name;
public void doSome() {
}
public Student() {
// TODO Auto-generated constructor stub
}
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
現在模板類修改好了,我就可以開始寫特使類,接下來我要把類的屬性反射出來,同時還要找到帶有注解的屬性,把它列印出來,
更新之后的測驗類,我在列印上為了更清楚,做了少許修改,可以copy去運行,包匯入就行,
**
*
* @author T_Antry
* @describe 反射測驗
* T_Antry-Test
* 2020年9月9日
*/
public class Test {
/**
*
* @param args
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @describe
*/
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Student st = new Student(520, "T_Antry");//創建一個實體
System.out.println("----------Begin");
System.out.println(st);
System.out.println("----------反射示例");
/**
* 首先我們要拿到類
*/
Class<?> c = st.getClass();
Field[] fArr = c.getDeclaredFields();//獲取本類所有屬性,包括私有屬性
// fArr = c.getFields();//只獲取公有屬性,且包括繼承來的屬性
for (Field field : fArr) {
field.setAccessible(true);//注意,我們的模板類寫的是幾個私有成員,這條是開啟權限,使得我們都能夠操作和讀取私有屬性的值
System.out.println(field.getName()+":" +field.get(st));
}
System.out.println("----------反射+注解示例1:沒有注解的不列印");
for (Field field : fArr) {
field.setAccessible(true);//注意,我們的模板類寫的是幾個私有成員,這條是開啟權限,使得我們都能夠操作和讀取私有屬性的值
MyField anno = field.getDeclaredAnnotation(MyField.class);
if(null==anno)
continue;
System.out.println(field.getName()+":" +field.get(st));
}
}
}
運行結果

如圖可見,我們只列印了name屬性,因為我在name屬性上面加了注解,這里檢測到有注解,就列印了,
更多玩法
當然還有很多玩法,例如你還可以拿到注解上value的值去做一些事請,比如ORM原理就是才有這種做法,
實際上,我認為,注解可能更經常用于類的上方,隨后通過注解去找到對應的類,這種做法十分有利于代碼的簡潔,但是這個實體,因為還涉及網路技術,服務器,以及整個專案的運轉,也可能是我水平不夠,就不好一下子介紹完,哈哈,后面有可能的話,希望還是可以將一下Servlet注解原理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/11719.html
標籤:其他
