1. 程式人生 > >Java 註解(Annotation) 的基本使用和理解

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等等

之前整理的筆記借鑑了這位大神的文章