本文轉載自[秒懂,Java 注解 (Annotation)你可以這樣學](https://blog.csdn.net/briblue/article/details/73824058),內容有刪改。
注解可以理解為標簽,是對代碼貼上的標簽。
# 注解語法
## 注解的定義
注解通過關鍵字`@interface`定義。
```java
public @interface TestAnnotation {
}
```
## 注解的應用
```java
@TestAnnotation
public class Test{
}
```
## 元注解
元注解是可以注解到注解上的注解,能夠應用到其他注解上面。元注解也是標簽,是一張特殊的標簽,作用是給其他普通標簽進行解釋說明的。
元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable共5種。
### @Retention
@Retention解釋說明了這個注解的存活時間,取值有:
* RetentionPolicy.SOURCE 注解只在源碼階段保留,在編譯器編譯時將會被丟棄忽視
* RetentionPolicy.CLASS 注解只被保留到編譯進行時,不會被加載到JVM中
* RetentionPolicy.RUNTIME 注解可以保留到程序運行時,會被加載到JVM中,在程序運行時可以獲取到該注解
```java
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
```
上面代碼中,指定TestAnnotation可以在程序運行時獲取到,它的生命周期很長。
### @Documented
@Documented的作用是能夠將注解中的元素包含到Javadoc中
### @Target
@Target指定了注解運用的地方,也就是該注解的使用場景被限制了,取值有:
* ElementType.ANNOTATION_TYPE 可以給一個注解進行注解
* ElementType.CONSTRUCTOR可以給構造方法進行注解
* ElementType.FIELD可以給屬性進行注解
* ElementType.LOCAL_VARIABLE可以給局部變量進行注解
* ElementType.METHOD可以給方法進行注解
* ElementType.PACKAGE可以給一個包進行注解
* ElementType.PARAMETER可以給一個方法內的參數進行注解
* ElementType.TYPE可以給一個類型進行注解,比如類、接口、枚舉
### @Inherited
如果一個父類被一個@Inherited注解過的注解進行注解的話,它的子類沒有被任何注解應用的話,這個子類就繼承了超類的注解:
```java
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}
```
此時B也擁有@Test這個注解。
### @Repeatable
@Repeatable的作用是標明一個注解可以被多次應用,也就是可以多次取值:
```java
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
```
可以理解為,Persons 是一張總的標簽,上面貼滿了 Person 這種同類型但內容不一樣的標簽。把 Persons 給一個 SuperMan 貼上,相當于同時給他貼了程序員、產品經理、畫家的標簽。
## 注解的屬性
注解的屬性也叫成員變量,注解只有成員變量,沒有成員方法。注解的成員變量以“無形參的方法”的形式來聲明,方法名定義了該成員變量的名字,返回值定義了該成員變量的類型。注解中定義屬性時它的類型必須是 8 種基本數據類型外加 類、接口、注解及它們的數組。
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
```
@TestAnnotation注解中擁有id和msg兩個屬性,使用的時候,應該給他們進行賦值。
```java
@TestAnnotation(id = 3, msg = "hello annotation")
public class Test {}
```
注解屬性的默認值需要使用default關鍵字進行指定:
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
public int id() default -1;
public String msg() default "Hi";
}
```
當只有一個屬性時,可以簡寫如下:
```java
@TestAnnotation(3)
public class Test {}
```
當沒有屬性時,可以簡寫如下:
```java
@TestAnnotation
public class Test {}
```
# Java中預置的注解
1、@Deprecated注解用來標記過時的方法、類、成員變量等,編譯器在編譯階段遇到這個注解時會發出提醒警告。
2、@Override注解標記子類中的方法是復寫的父類的,可以讓編譯器幫我們檢查方法重寫是否成功。
3、@SuppressWarnings注解標記阻止編譯器進行警告
4、@SafeVarargs是參數安全類型注解,它的目的是提醒開發者不要用參數做一些不安全的操作。
# 注解的提取
注解的提取可以理解為將注解標簽在合適的時候撕下來,檢閱上面的信息。注解的提取需要通過反射獲得。
1、通過Class對象的isAnnotationPresent方法來判斷是否應用了注解:
```java
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
```
2、然后通過getAnnotation方法或getAnnotations方法來獲取注解:
```java
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
public Annotation[] getAnnotations() {}
```
如果獲取到的 Annotation 如果不為 null,則就可以調用它們的屬性方法了。比如:
```java
@TestAnnotation()
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
}
}
```
除了獲取類上的注解,同樣可以通過反射獲取成員變量、成員方法上的注解,示例如下:
```java
@TestAnnotation(msg="hello")
public class Test {
@Check(value="hi")
int a;
@Perform
public void testMethod(){}
@SuppressWarnings("deprecation")
public void test1(){
Hero hero = new Hero();
hero.say();
hero.speak();
}
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
//獲取類的注解
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
try {
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
//獲取一個成員變量上的注解
Check check = a.getAnnotation(Check.class);
if ( check != null ) {
System.out.println("check value:"+check.value());
}
Method testMethod = Test.class.getDeclaredMethod("testMethod");
if ( testMethod != null ) {
// 獲取方法中的注解
Annotation[] ans = testMethod.getAnnotations();
for( int i = 0;i < ans.length;i++) {
System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
```
需要注意的是,如果一個注解要在運行時被成功提取,那么 @Retention(RetentionPolicy.RUNTIME) 是必須的。
# 注解使用場景
注解的用處有:
* 提供信息給編譯器:編譯器可以利用注解來探測錯誤和警告信息
* 編譯時的處理:軟件工具可以利用注解信息來生成代碼、Html文檔或其他處理
* 運行時的處理:可以在程序運行時接受提取
當開發者使用了Annotation 修飾了類、方法、Field 等成員之后,這些 Annotation 不會自己生效,必須由開發者提供相應的代碼來提取并處理 Annotation 信息。這些處理提取和處理 Annotation 的代碼統稱為 APT(Annotation Processing Tool)。
本文轉載自[秒懂,Java 注解 (Annotation)你可以這樣學](https://blog.csdn.net/briblue/article/details/73824058),內容有刪改。
- 導讀
- Java知識
- Java基本程序設計結構
- 【基礎知識】Java基礎
- 【源碼分析】Okio
- 【源碼分析】深入理解i++和++i
- 【專題分析】JVM與GC
- 【面試清單】Java基本程序設計結構
- 對象與類
- 【基礎知識】對象與類
- 【專題分析】Java類加載過程
- 【面試清單】對象與類
- 泛型
- 【基礎知識】泛型
- 【面試清單】泛型
- 集合
- 【基礎知識】集合
- 【源碼分析】SparseArray
- 【面試清單】集合
- 多線程
- 【基礎知識】多線程
- 【源碼分析】ThreadPoolExecutor源碼分析
- 【專題分析】volatile關鍵字
- 【面試清單】多線程
- Java新特性
- 【專題分析】Lambda表達式
- 【專題分析】注解
- 【面試清單】Java新特性
- Effective Java筆記
- Android知識
- Activity
- 【基礎知識】Activity
- 【專題分析】運行時權限
- 【專題分析】使用Intent打開三方應用
- 【源碼分析】Activity的工作過程
- 【面試清單】Activity
- 架構組件
- 【專題分析】MVC、MVP與MVVM
- 【專題分析】數據綁定
- 【面試清單】架構組件
- 界面
- 【專題分析】自定義View
- 【專題分析】ImageView的ScaleType屬性
- 【專題分析】ConstraintLayout 使用
- 【專題分析】搞懂點九圖
- 【專題分析】Adapter
- 【源碼分析】LayoutInflater
- 【源碼分析】ViewStub
- 【源碼分析】View三大流程
- 【源碼分析】觸摸事件分發機制
- 【源碼分析】按鍵事件分發機制
- 【源碼分析】Android窗口機制
- 【面試清單】界面
- 動畫和過渡
- 【基礎知識】動畫和過渡
- 【面試清單】動畫和過渡
- 圖片和圖形
- 【專題分析】圖片加載
- 【面試清單】圖片和圖形
- 后臺任務
- 應用數據和文件
- 基于網絡的內容
- 多線程與多進程
- 【基礎知識】多線程與多進程
- 【源碼分析】Handler
- 【源碼分析】AsyncTask
- 【專題分析】Service
- 【源碼分析】Parcelable
- 【專題分析】Binder
- 【源碼分析】Messenger
- 【面試清單】多線程與多進程
- 應用優化
- 【專題分析】布局優化
- 【專題分析】繪制優化
- 【專題分析】內存優化
- 【專題分析】啟動優化
- 【專題分析】電池優化
- 【專題分析】包大小優化
- 【面試清單】應用優化
- Android新特性
- 【專題分析】狀態欄、ActionBar和導航欄
- 【專題分析】應用圖標、通知欄適配
- 【專題分析】Android新版本重要變更
- 【專題分析】唯一標識符的最佳做法
- 開源庫源碼分析
- 【源碼分析】BaseRecyclerViewAdapterHelper
- 【源碼分析】ButterKnife
- 【源碼分析】Dagger2
- 【源碼分析】EventBus3(一)
- 【源碼分析】EventBus3(二)
- 【源碼分析】Glide
- 【源碼分析】OkHttp
- 【源碼分析】Retrofit
- 其他知識
- Flutter
- 原生開發與跨平臺開發
- 整體歸納
- 狀態及狀態管理
- 零碎知識點
- 添加Flutter到現有應用
- Git知識
- Git命令
- .gitignore文件
- 設計模式
- 創建型模式
- 結構型模式
- 行為型模式
- RxJava
- 基礎
- Linux知識
- 環境變量
- Linux命令
- ADB命令
- 算法
- 常見數據結構及實現
- 數組
- 排序算法
- 鏈表
- 二叉樹
- 棧和隊列
- 算法時間復雜度
- 常見算法思想
- 其他技術
- 正則表達式
- 編碼格式
- HTTP與HTTPS
- 【面試清單】其他知識
- 開發歸納
- Android零碎問題
- 其他零碎問題
- 開發思路