本文主要參考與借鑑frank909 文章,但更爲簡單,詳細。java
Annotation 中文譯過來就是註解、標釋的意思。Annotation是一種應用於類、方法、參數、變量、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元數據的一種工具。 在 Java 中註解是一個很重要的知識點,註解目前很是的流行,不少主流框架都支持註解,並且本身編寫代碼的時候也會盡可能的去用註解,一是方便,二是代碼更加簡潔。android
由於日常開發少見,相信有很多的人員會認爲註解的地位不高。其實同 classs 和 interface 同樣,註解也屬於一種類型。它是在 Java SE 5.0 版本中開始引入的概念。git
package java.lang;
import java.lang.annotation.*;
/** * Indicates that a method declaration is intended to override a * method declaration in a supertype. If a method is annotated with * this annotation type compilers are required to generate an error * message unless at least one of the following conditions hold: * * <ul><li> * The method does override or implement a method declared in a * supertype. * </li><li> * The method has a signature that is override-equivalent to that of * any public method declared in {@linkplain Object}. * </li></ul> */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
複製代碼
它的形式跟接口很相似,不過前面多了一個 @ 符號。上面的代碼就建立了一個名字爲 Override的註解。github
你能夠簡單理解爲建立了一張名字爲 Override的標籤。api
**Annotations僅僅是元數據,和業務邏輯無關。**理解起來有點困難,但就是這樣。若是Annotations不包含業務邏輯,那麼必須有人來實現這些邏輯。元數據的用戶來作這個事情。Annotations僅僅提供它定義的屬性(類/方法/包/域)的信息。Annotations的用戶(一樣是一些代碼)來讀取這些信息並實現必要的邏輯。數組
元註解是什麼意思呢?安全
元註解是能夠註解到註解上的註解,或者說元註解是一種基本註解,可是它可以應用到其它的註解上面。網絡
若是難於理解的話,你能夠這樣理解。元註解也是一張標籤,可是它是一張特殊的標籤,它的做用和目的就是給其餘普通的標籤進行解釋說明的。app
元標籤有 @Retention、@Documented、@Target(當一個註解被 @Target 註解時,這個註解就被限定了運用的場景 )、@Inherited、@Repeatable 5 種。框架
@target | 表示該註解能夠用於什麼地方,可能的ElementType參數有: CONSTRUCTOR:構造器的聲明 FIELD:域聲明(包括enum實例) LOCAL_VARIABLE:局部變量聲明 METHOD:方法聲明 PACKAGE:包聲明 PARAMETER:參數聲明 TYPE:類、接口(包括註解類型)或enum聲明 |
---|---|
@Retention | 表示須要在什麼級別保存該註解信息。可選的RetentionPolicy參數包括: SOURCE:註解將被編譯器丟棄; CLASS:註解在class文件中可用,但會被VM丟棄; RUNTIME:VM將在運行期間保留註解,所以能夠經過反射機制讀取註解的信息。 |
@Document | 將註解包含在Javadoc中 |
@Inherited | 容許子類繼承父類中的註解 |
@Repeatable | 可重複 (@Repeatable 是 Java 1.8) |
註解的屬性也叫作成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以「無形參的方法」形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。須要注意的是,在註解中定義屬性時它的類型必須是 8 種基本數據類型外加 類、接口、註解及它們的數組。
註解中屬性能夠有默認值,默認值須要用 default 關鍵值指定。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person {
int id() default -10998;
String msg() default "no hello";
}
複製代碼
上面代碼定義了 Person這個註解中擁有 id 和 msg 兩個屬性。在使用的時候,咱們應該給它們進行賦值。賦值的方式是在註解的括號內以 value=」」 形式,多個屬性以前用 ,隔開。 一個註解內僅僅只有一個名字爲 value 的屬性時,這個註解時能夠直接接屬性值填寫到括號內。 還須要注意的一種狀況是一個註解沒有任何屬性 括號能夠省略。。
public @interface NoUse {
}
複製代碼
public @interface Chou {
String value() default "You";
}
複製代碼
@Person(id = 10758, msg = "hello android")//或者直接默認@Person()
public class Liming {
@Chou("She")
String beautiful;
@NoUse
public void say() {
}
}
複製代碼
Java 語言自己已經提供了幾個現成的註解。
這個元素是用來標記過期的元素,想必你們在平常開發中常常碰到。編譯器在編譯階段遇到這個註解時會發出提醒警告,告訴開發者正在調用一個過期的元素好比過期的方法、過期的類、過期的成員變量。
@Person(id = 10758, msg = "hello android")//或者直接默認@Person()
public class Liming {
@Chou("She")
String beautiful;
@NoUse
public void say() {
System.out.println(" say is using ");
}
@Deprecated
public void speak() {
System.out.println(" speak is out of date ");
}
}
複製代碼
Liming類,它有兩個方法 say() 和 speak() ,其中 speak() 被 @Deprecated 註解。而後咱們在 IDE 中分別調用它們。
能夠看到,speak() 方法上面被一條直線劃了一條,這其實就是編譯器識別後的提醒效果。
阻止警告。調用被 @Deprecated 註解的方法後,編譯器會警告提醒,而有時候開發者會忽略這種警告,他們能夠在調用的地方經過 @SuppressWarnings 達到目的。
參數安全類型註解。它的目的是提醒開發者不要用參數作一些不安全的操做,它的存在會阻止編譯器產生 unchecked 這樣的警告,在 Java 1.7 的版本中加入。
上面的代碼中,編譯階段不會報錯,運行時會拋出 ClassCastException 這個異常。
函數式接口註解,這個是 Java 1.8 版本引入的新特性。
函數式接口 (Functional Interface) 就是一個具備一個方法的普通接口。
@FunctionalInterface
public interface Runnable {
/** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */
public abstract void run();
}
複製代碼
咱們進行線程開發中經常使用的 Runnable 就是一個典型的函數式接口,上面源碼能夠看到它就被 @FunctionalInterface 註解。
可能有人會疑惑,函數式接口標記,函數式接口能夠很容易轉換爲 Lambda 表達式。
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
public Annotation[] getAnnotations() {}
若是獲取到的 Annotation 若是不爲 null,則就能夠調用它們的屬性方法了。
屬性、方法上的註解照樣是能夠的。一樣仍是要藉助於反射。
public static void getAnnotation() {
boolean hasAnnotation = MainActivity.class.isAnnotationPresent(Person.class);
if (hasAnnotation) {
Person testPerson = MainActivity.class.getAnnotation(Person.class);
System.out.println("id is " + testPerson.id() + " msg is " + testPerson.msg());
}
}
public static void getField() {
try {
Field a = Liming.class.getDeclaredField("beautiful");
a.setAccessible(true);
Chou chou = a.getAnnotation(Chou.class);
if (chou != null) {
System.out.println("check value:" + chou.value());
}
Method noUse = Liming.class.getDeclaredMethod("say");
if (noUse != null) { // 獲取方法中的註解
Annotation[] ans = noUse.getAnnotations();
for (int i = 0; i < ans.length; i++) {
System.out.println("method noUse annotation:" + ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("NoSuchFieldException");
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("NoSuchMethodException");
}
}
複製代碼
say is using
speak is out of date
id is -10998 msg is this is not default
check value:She
method noUse annotation:NoUse
Process finished with exit code 0
複製代碼
當開發者使用了Annotation 修飾了類、方法、Field 等成員以後,這些 Annotation 不會本身生效,必須由開發者提供相應的代碼來提取並處理 Annotation 信息。這些處理提取和處理 Annotation 的代碼統稱爲 APT(Annotation Processing Tool)。
註解是一系列元數據,它提供數據用來解釋程序代碼,可是註解並不是是所解釋的代碼自己的一部分。註解對於代碼的運行效果沒有直接影響。
註解有許多用處,主要以下:
- 提供信息給編譯器: 編譯器能夠利用註解來探測錯誤和警告信息
- 編譯階段時的處理: 軟件工具能夠用來利用註解信息來生成代碼、Html文檔或者作其它相應處理。
- 運行時的處理: 某些註解能夠在程序運行的時候接受代碼的提取
複製代碼
需求:自定義註解與實現,檢查MaSaGei類中的錯誤並反饋
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckOut {
}
複製代碼
public class MaSaGei {
@CheckOut
public void testOne() {
System.out.println(" 1 + 0 = " + ((1 + 1) / 2));
}
@CheckOut
public void testTwo() {
System.out.println(" 1 + 1 = " + (8 / 4));
}
@CheckOut
public void testThree() {
System.out.println(" 1 + 2 = " + (6 / 2));
}
@CheckOut
public void testFour() {
System.out.println(" 1 / 3 = " + (6 / 0));
}
}
複製代碼
public class CheckOutTool {
public static void checkAll() {
MaSaGei maSaGei = new MaSaGei();
Class clazz = maSaGei.getClass();
Method[] method = clazz.getDeclaredMethods();
StringBuilder log = new StringBuilder();
// 記錄異常的次數
int errornum = 0;
for (Method m : method) {
if (m.isAnnotationPresent(CheckOut.class)) {
m.setAccessible(true);
try {
m.invoke(maSaGei, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
errornum++;
log.append(m.getName());
log.append(" ");
log.append("has error ");
log.append("\\n\\r caused by ");
log.append(e.getCause().getClass().getSimpleName());
log.append("\n\r");
log.append(e.getCause().getMessage());
log.append("\n\r");
}
}
}
log.append(clazz.getSimpleName());
log.append(" has ");
log.append(errornum);
log.append(" error."); // 生成測試報告
System.out.println(log.toString());
}
}
複製代碼
結果以下:
testFour has error \n\r caused by ArithmeticException
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.heng.subhey.annotation.CheckOutTool.checkAll(CheckOutTool.java:18)
at com.heng.subhey.MainActivity.main(MainActivity.java:22)
Caused by: java.lang.ArithmeticException: / by zero
/ by zero
at com.heng.subhey.annotation.MaSaGei.testFour(MaSaGei.java:21)
... 6 more
MaSaGei has 1 error.
Process finished with exit code 0
複製代碼
/** * Example local unit test, which will execute on the development machine (host). * * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> */
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
複製代碼
@Test 標記了要進行測試的方法 addition_isCorrect()。
ButterKnife 是 Android 開發中大名鼎鼎的 IOC 框架,它減小了大量重複的代碼。
public class GoPayActivity extends BaseActivity {
@BindView(R.id.title_tv)
TextView title_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
title_tv.setText("支付");
}
@Override
protected int getLayoutId() {
return R.layout.activity_go_pay;
}
@OnClick({R.id.title_back, R.id.pay_confirm})
public void onClick(View v) {
switch (v.getId()) {
case R.id.title_back:
finish();
break;
case R.id.pay_confirm:
ToastUtils.showLong("支付環境安全檢測中...");
title_tv.postDelayed(new Runnable() {
@Override
public void run() {
ToastUtils.showShort("即將跳轉至支付頁面");
Intent charge = new Intent(GoPayActivity.this, KeyWriteActivity.class);
startActivity(charge);
}
}, 2 * 1000);
break;
}
}
}
複製代碼
Http 網絡訪問框架
public interface GitHubService { @GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user); }
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class);
複製代碼