Java 註解(Annotation) 的基本使用和理解
1.註解
概念:相當於標籤
2.註解的型別
2.1 元註解
概念:元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面
元註解的種類
- @Retention
- @Documented
- @Target
- @Inherited
- @Repeatable
@Retention
作用:解釋/說明了註解的生命週期
取值如下:
RetentionPolicy.SOURCE 註解只在原始碼階段保留,在編譯器進行編譯時它將被丟棄忽視。 RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被載入到 JVM 中。 RetentionPolicy.RUNTIME 註解可以保留到程式執行的時候,它會被載入進入到 JVM 中
@Documented
作用:將註解的元素包含到javadoc文件中
@Target
作用:限定了註解作用的目標範圍,包括累、方法等
取值如下
- ElementType.PACKAGE 可以給一個包進行註解
- ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
- ElementType.CONSTRUCTOR 可以給構造方法進行註解
- ElementType.FIELD 可以給屬性進行註解
- ElementType.LOCAL_VARIABLE 可以給區域性變數進行註解
- ElementType.METHOD 可以給方法進行註解
- ElementType.PARAMETER 可以給一個方法內的引數進行註解
- ElementType.TYPE 可以給一個型別進行註解,比如類、介面、列舉
@Inherited
作用: 繼承註解(如一個父類使用了該註解,那麼它的子類沒有進行其他註解的話,則會繼承了父類的註解。)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
//父類使用了Test註解
@Test
public class A {
}
//子類沒有使用其他註解,則繼承了父類的註解
public class B extends A {}
@Repeatable
作用:可重複註解,即註解的值可以同時取多個
//1.
//定義Person的容器註解
@interface Persons {
Person[] value();
}
//2.
//定義Person的註解
//@Repeatable括號中的類是容器註解
@Repeatable(Persons.class)
@interface Person{
String role() default "";
}
//3.
//取多個值來修飾,即是藝術家也是廚師
@Person(role="artist")
@Person(role="chef")
public class SuperMan{
}
2.2 Java內建的註解
@Deprecated
作用:用來標記過時的元素的。
@SuppressWarnings
作用:被標記的元素會阻止編譯器發出的警告提醒
@Override
作用:子類要複寫父類中被Override修飾的方法
@SafeVarargs
作用:提醒開發者不要用引數做不安全的操作&阻止編譯器產生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!
}
@FunctionalInterface
作用:表示該介面為函式式。函式式介面=具有1個方法的普通介面
問題:為什麼要用額外的函式式介面標記
答案:函式式介面很容易轉化為Lambda表示式
比如
@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();
}
2.3 自定義註解
2.3.1 定義註解
//通過@interface定義Test註解
public @interface Test{
}
2.3.2 註解屬性
註解的屬性也叫成員變數。註解只有成員變數,沒有方法
註解的成員變數在註解的定義中以“無形參的方法”形式來宣告,其方法名定義了該成員變數的名字,其返回值定義了該成員變數的型別。
注意點:註解中定義屬性時它的型別必須是 8 種基本資料型別外加 類、介面、註解及它們的陣列
public @interface Test{
int id();
String msg();
}
我們在Test註解裡定義了id和msg兩個屬性,在使用他們的時候我們應該進行賦值
@Test(id=5,msg = "hello")
public class AnnotationTest {
}
你也可以設定預設值
public @interface Test {
int id() default 10;
String msg() default "hello";
}
這樣子id的預設值是10,msg的預設值是hello。因為有預設值所以可以在使用的時候括號裡可以不填
直接@Test()。
還有一種情況就是註解內只有一個屬性的時,可以直接註解括號裡填寫一個值@Test(10)
2.3.3 獲取註解
之前比喻了註解是標籤,那現在獲取標籤然後檢閱上面的內容資訊。那怎麼實現呢,通過反射
//判斷是否應用了某註解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
//獲取註解在這個元素的指定註解的物件
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
//獲取註解在這個元素的所有註解物件
public Annotation[] getAnnotations() {}
以下演示
註解類
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String name() default "hello";
int age() default 10;
}
被註解的類
@Test(name = "class",age = 100)
public class Person {
@Test(name = "hey",age = 40)
private String name;
@Test(name = "hi",age = 20)
public void son(){
}
}
類註解獲取:
Class aClass = Person.class;
//判斷是否應用了該註解
boolean hasAnnotation = aClass.isAnnotationPresent(Test.class);
if (hasAnnotation) {
//拿到Annotation註解
Annotation annotation = aClass.getAnnotation(Test.class);
Test myAnnotation = (Test) annotation;
//獲取name以及age
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.age());
}
name:class
age:100
方法註解獲取:
Method method = null;
try {
//拿到該方法物件(son為該方法名)
method = Person.class.getDeclaredMethod("son");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Annotation annotationMethod = method.getAnnotation(Test.class);
Test myAnnotation = (Test) annotationMethod;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.age());
name:hi
age:20
變數註解獲取
Class aClass = Person.class;
try {
Field field=aClass.getDeclaredField("name");
Test annotation=field.getAnnotation(Test.class);
System.out.print(annotation.name());
System.out.print(annotation.age());
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
name:hey
age:40
3.註解應用例項
JUnit
JUnit 這個是一個測試框架,典型使用方法如下:
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
ButterKnife
ButterKnife 是 Android 開發中大名鼎鼎的 IOC 框架,它減少了大量重複的程式碼。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_test)
TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
}
還有 Degger2以及Retrofit等等
之前整理的筆記借鑑了這位大神的文章