Java
中,註解(Annotation)很是重要,但對於不少開發者來講,這並不容易理解,甚至以爲有點神祕Java
註解的介紹 & 實戰攻略,但願大家會喜歡。
- 註解屬於
Java
中的一種類型,同 類class
和 接口interface
同樣- 在
Java SE 5.0
開始引入- 基礎做用:標識 / 解釋
Java
代碼
面向 編譯器 / APT
使用php
APT(Annotation Processing Tool)
:提取 & 處理Annotation
的代碼- 由於當開發者使用
Annotation
修飾類、方法、方法 等成員後,這些Annotation
不會本身生效,必須由開發者提供相應的代碼來提取並處理Annotation
信息。這些處理提取和處理Annotation
的代碼統稱爲APT
因此說,註解除了最基本的解釋 & 說明代碼,註解的應用場景還取決於你想如何利用它java
如出名的測試框架JUnit
= 採用註解進行代碼測試git
public class ExampleUnitTest {
@Test
public void Method() throws Exception {
...
}
}
// @Test 標記了要進行測試的方法Method() 複製代碼
如 Http
網絡請求庫 Retrofit
& IOC
框架ButterKnife
github
<-- Http網絡請求庫 Retrofit -->
// 採用 註解 描述 網絡請求參數
public interface GetRequest_Interface {
@GET("ajax.php?a=fy&f=auto&t=auto&w=hello%20world")
Call<Translation> getCall();
}
<-- IOC 框架ButterKnife -->
public class MainActivity extends AppCompatActivity {
// 經過註解設置資源
@BindView(R.id.test)
TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
}複製代碼
註解的類型包括:元註解 、 Java
內置註解 & 自定義註解。ajax
是一種基本註解 =
Android
系統內置的註解
// 元註解 做用於註解 & 解釋註解
// 元註解在註解定義時進行定義
@元註解
public @interface Carson_Annotation {
}複製代碼
Java
代碼 & 解釋Java
代碼// 註解做用於Java代碼 & 解釋Java代碼
@Carson_Annotation
public class Carson {
}
複製代碼
// 元註解@Retention(RetentionPolicy.RUNTIME)的做用:說明 註解Carson_Annotation的生命週期 = 保留到程序運行時 & 被加載到 JVM 中
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}
<-- 元註解@Retention參數說明 -->
// RetentionPolicy.RUNTIME:註解保留到程序運行時 & 會被加載進入到 JVM 中,因此在程序運行時能夠獲取到它們
// RetentionPolicy.CLASS:註解只被保留到編譯進行時 & 不會被加載到 JVM
// RetentionPolicy.SOURCE:註解只在源碼階段保留 & 在編譯器進行編譯時將被丟棄忽視。
複製代碼
Java
文檔註解Javadoc
文檔中// 元註解@Documented做用:說明 註解Carson_Annotation的元素包含到 Javadoc 文檔中
@Documented
public @interface Carson_Annotation {
}複製代碼
// 元註解@Target做用:限定了註解Carson_Annotation做用的目標範圍 = 方法
// 即註解Carson_Annotation只能用於解釋說明 某個方法
@Target(ElementType.METHOD)
public @interface Carson_Annotation {
}
<-- @Target取值參數說明 -->
// ElementType.PACKAGE:能夠給一個包進行註解
// ElementType.ANNOTATION_TYPE:能夠給一個註解進行註解
// ElementType.TYPE:能夠給一個類型進行註解,如類、接口、枚舉
// ElementType.CONSTRUCTOR:能夠給構造方法進行註解
// ElementType.METHOD:能夠給方法進行註解
// ElementType.PARAMETER 能夠給一個方法內的參數進行註解
// ElementType.FIELD:能夠給屬性進行註解
// ElementType.LOCAL_VARIABLE:能夠給局部變量進行註解
複製代碼
- 前提:子類沒有被任何註解應用
- 如,註解
@Carson_Annotation
(被元註解@Inherited
註解)做用於A類,那麼A類的子類則繼承了A類的註解- 具體使用
// 元註解@Inherited 做用於 註解Carson_Annotation
@Inherited
public @interface Carson_Annotation {
}
// 註解Carson_Annotation 做用於A類
@Carson_Annotation
public class A {
}
// B類繼承了A類,即B類 = A類的子類,且B類沒被任何註解應用
// 那麼B類繼承了A類的註解 Carson_Annotation
public class B extends A {}複製代碼
Java
1.8後引進
// 1. 定義 容器註解 @ 職業
public @interface Job {
Person[] value();
}
<-- 容器註解介紹 -->
// 定義:自己也是一個註解
// 做用:存放其它註解
// 具體使用:必須有一個 value 屬性;類型 = 被 @Repeatable 註解的註解數組
// 如本例中,被 @Repeatable 做用 = @Person ,因此value屬性 = Person []數組
// 2. 定義@Person
// 3. 使用@Repeatable 註解 @Person
// 注:@Repeatable 括號中的類 = 容器註解
@Repeatable(Job.class)
public @interface Person{
String role default "";
}
// 在使用@Person(被@Repeatable 註解 )時,能夠取多個值來解釋Java代碼
// 下面註解表示:Carson類便是產品經理,又是程序猿
@Person(role="coder")
@Person(role="PM")
public class Carson{
}
複製代碼
注:一個註解能夠被多個元註解做用
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}複製代碼
類型:Java
中 內置的註解有5類,具體包括:
數組
下面我將對這5類內置註解進行講解安全
// 用 註解@Deprecated 標記類中已過期的 方法Hello()
public class Buyer2 {
@Deprecated
public void Hello(){
System.out.println("Hello 2015!");
}
}複製代碼
使用該類中被 @Deprecated
做用的方法時,IDE
會提示該方法已過期 / 拋棄markdown
主要應用於開發者須要忽略警告時
// 在括號內傳入須要忽略警告的屬性
@SuppressWarnings("deprecation")
public void test(){
Buyer2 mBuyer2 = new Buyer2();
mBuyer2.hello();
// IDE將不會發出警告(即不會在hello()出現劃線)
}複製代碼
Java
1.7 後引入
unchecked
警告// 如下是官方例子
// 雖然編譯階段不報錯,但運行時會拋出 ClassCastException 異常
// 因此該註解只是做提示做用,可是實際上仍是要開發者本身處理問題
@SafeVarargs // Not actually safe!
static void m(List<String>... stringLists) {
Object[] array = stringLists;
List<Integer> tmpList = Arrays.asList(42);
array[0] = tmpList; // Semantically invalid, but compiles without warnings
String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}複製代碼
定義:函數式接口註解網絡
Java
1.8 後引入的新特性多線程
做用:表示該接口 = 函數式接口
函數式接口 (Functional Interface) = 1個具備1個方法的普通接口
具體使用
// 多線程開發中經常使用的 Runnable 就是一個典型的函數式接口(被 @FunctionalInterface 註解)
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
<--額外:爲何要用函數式接口標記 -->
// 緣由:函數式接口很容易轉換爲 Lambda 表達式
// 這是另一個很大話題,此處不做過多講解,感興趣的同窗可自行了解複製代碼
註解的基礎使用包括定義、屬性 & 具體使用
// 經過 @interface 關鍵字進行定義
// 形式相似於接口,區別在於多了一個 @ 符號
public @interface Carson_Annotation {
}
// 上面的代碼建立了一個名爲 Carson_Annotaion 的註解複製代碼
<-- 1. 定義 註解的屬性 -->
// 註解的屬性 在定義該註解自己時 進行定義
public @interface Carson_Annotation {
// 註解的屬性 = 成員變量
// 註解只有成員變量,沒有方法
// 註解@Carson_Annotation中有2個屬性:id 和 msg
int id();
String msg() default "Hi" ;
// 說明:
// 註解的屬性以 「無形參的方法」 形式來聲明
// 方法名 = 屬性名
// 方法返回值 = 屬性類型 = 8 種基本數據類型 + 類、接口、註解及對應數組類型
// 用 default 關鍵值指定 屬性的默認值,如上面的msg的默認值 = 」Hi「
}
<-- 2. 賦值 註解的屬性 -->
// 註解的屬性在使用時進行賦值
// 註解屬性的賦值方式 = 註解括號內以 「value=」xx」 「 形式;用 」,「隔開多個屬性
// 註解Carson_Annotation 做用於A類
// 在做用 / 使用時對註解屬性進行賦值
@Carson_Annotation(id=1,msg="hello,i am Carson")
public class A {
}
// 特別說明:若註解只有一個屬性,則賦值時」value「能夠省略
// 假設註解@Carson_Annotation只有1個msg屬性
// 賦值時直接賦值便可
@Carson_Annotation("hello,i am Carson")
public class A {
}
複製代碼
// 在類 / 成員變量 / 方法 定義前 加上 「@註解名」 就可使用該註解
@Carson_Annotation
public class Carson {
@Carson_Annotation
int a;
@Carson_Annotation
public void bMethod(){}
}
// 即註解@Carson_Annotation 用於標識解釋 Carson類 / a變量 / b方法複製代碼
Java
反射技術 因爲反射須要較高時間成本,因此註解使用時要謹慎考慮時間成本
<-- 步驟1:判斷該類是否應用了某個註解 -->
// 手段:採用 Class.isAnnotationPresent()
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
<-- 步驟2:獲取 註解對象(Annotation)-->
// 手段1:採用getAnnotation() ;返回指定類型的註解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
// 手段2:採用getAnnotations() ;返回該元素上的全部註解
public Annotation[] getAnnotations() {}
複製代碼
步驟1:自定義2個註解
Carson_Annotation.java
// 由於註解@Carson_Annotation須要在程序運行時使用
// 因此必須採用元註解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
// 註解@Carson_Annotation中有2個屬性:id 和 msg
int id();
String msg() default "Hi";
}複製代碼
Carson_Annotation2.java
// 由於註解@Carson_Annotation2須要在程序運行時使用
// 因此必須採用元註解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation2 {
// 註解@Carson_Annotation2中有2個屬性:id 和 msg
int id();
String msg() default "Hi";
}複製代碼
步驟2:定義1個被註解的類
Test.java
// 1個註解做用於Test類
@Carson_Annotation(id = 1,msg="我是類Test")
public class Test {
// 1個註解 做用於Test類成員變量a
@Carson_Annotation(id = 2,msg="我是變量a")
int a;
// 2個註解 做用於Test類方法
@Carson_Annotation(id = 3,msg="我是方法b")
@Carson_Annotation2(id = 4,msg="我是方法bb(來自第2個註解)")
public void bMethod(){}
}複製代碼
步驟3:分別獲取一個類、方法 & 成員變量上的註解
private static final String TAG = "Annotation";
/**
* 講解1:獲取類上的註解
*/
// 1. 判斷Test類是否應用了註解@Carson_Annotation
boolean hasAnnotation = Test.class.isAnnotationPresent(Carson_Annotation.class);
// 2. 若是應用了註解 = hasAnnotation = true
// 則獲取類上的註解對象
if ( hasAnnotation ) {
Carson_Annotation classAnnotation = Test.class.getAnnotation(Carson_Annotation.class);
// 3. 獲取註解對象的值
Log.d(TAG, "我是Test類上的註解");
Log.d(TAG, "id:" + classAnnotation.id());
Log.d(TAG, "msg:" + classAnnotation.msg());
}
/**
* 講解2:獲取類成員變量a上的註解
*/
try {
// 1. 獲取類上的成員變量a
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
// 2. 獲取成員變量a上的註解@Carson_Annotation
Carson_Annotation variableAnnotation = a.getAnnotation(Carson_Annotation.class);
// 3. 若成員變量應用了註解 = hasAnnotation = true
// 則獲取註解對象上的值 = id & msg
if ( variableAnnotation != null ) {
Log.d(TAG, "我是類成員變量a上的註解");
Log.d(TAG, "id:" + variableAnnotation.id());
Log.d(TAG, "msg:" + variableAnnotation.msg());
}
/**
* 講解3:獲取類方法bMethod上的註解
*/
// 1. 獲取類方法bMethod
Method testMethod = Test.class.getDeclaredMethod("bMethod");
// 2. 獲取方法上的註解
if ( testMethod != null ) {
// 由於方法上有2個註解,因此採用getAnnotations()得到全部類型的註解
Annotation[] ans = testMethod.getAnnotations();
Log.d(TAG, "我是類方法bMethod的註解");
// 3. 獲取註解的值(經過循環)
for( int i = 0;i < ans.length ;i++) {
Log.d(TAG, "類方法bMethod的" + "註解"+ i+ 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());
}
}複製代碼
Carson的Github地址:Java_Annotation
下面,我將經過註解實現一個最多見的應用場景:測試代碼
經過註解,檢查一個類中的方法是否存在異常
Carson_Test.java
// 由於註解@Carson_Test須要在程序運行時使用
// 因此必須採用元註解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Test {
}複製代碼
Test.java
public class Test {
@Carson_Test
public void method1(){
System.out.println("method1正常運行 = " + (1+1));
}
@Carson_Test
public void method2(){
System.out.println("method2正常運行 = " + (2*3));
}
@Carson_Test
public void method3(){
System.out.println("method3正常運行 = " + (2/0));
}
}複製代碼
Bug
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 獲取測試類Test對象
Test mTest = new Test();
Class mTest_Class = mTest.getClass();
// 2. 獲取測試類Test的全部方法(經過數組存放)
Method[] method = mTest_Class.getDeclaredMethods();
// 3. 經過註解@Carson_Test 測試類中的方法
// a. 遍歷類中全部方法
for ( Method m: method ) {
// b. 只有被 @Carson_Test 標註過的方法才容許進行測試
if ( m.isAnnotationPresent( Carson_Test.class )) {
try {
m.setAccessible(true);
// c. 經過反射調用測試類中的方法
m.invoke(mTest);
// d. 捕捉方法出現的異常 & 輸出異常信息
} catch (Exception e) {
System.out.println( "Test類出現Bug了!!!");
System.out.println( "出現Bug的方法:" + m.getName());
System.out.println( "Bug類型:" + e.getCause().getClass().getSimpleName());
System.out.println( "Bug信息:" + e.getCause().getMessage());
}
}
}
}複製代碼
Carson_Ho的Github地址:Annation_Debug
本文全面講解了Java
註解(Annotation
)的相關知識,相信您對Java
註解已經瞭解深入。
下面我將繼續對 Android
中的知識進行深刻講解 ,有興趣能夠繼續關注Carson_Ho的安卓開發筆記