1. 程式人生 > 實用技巧 >JavaSE學習筆記28:註解

JavaSE學習筆記28:註解

註解

  1. 註解,或者叫做註釋型別,英文單詞是Annotation
  2. 註解Annotation是一種引用資料型別,編譯之後也是生成xxx.class檔案
  3. 怎麼自定義註解
[修飾符列表] @interface 註解型別名{ 

}
package se7;
/*
自定義註解:MyAnnotation
 */
public @interface MyAnnotation {
    //????????
}

如何使用,用在什麼地方?

  1. 註解使用時的語法格式:@註解型別名

  2. 註解可以出現在類上、屬性上、方法上、變數上等...

    package se7;
    
    //預設情況下,註解可以出現在任意位置
    @MyAnnotation
    public class AnnotationTest01 {
        @MyAnnotation
        private int no;
    
        @MyAnnotation
        public AnnotationTest01(){}
    
        @MyAnnotation
        public static void m1(){
            @MyAnnotation
            int i = 100;
        }
    
        @MyAnnotation
        public void m2(@MyAnnotation String name,@MyAnnotation int k){
    
        }
    }
    @MyAnnotation
    interface MyInterface{
    
    }
    
    @MyAnnotation
    enum Season{
        SPRING,SUMMER,AUTUMN,WINTER
    }
    
  3. 註解還可以出現在註解型別上

    package se7;
    
    //註解修飾註解
    @MyAnnotation
    public @interface OtherAnnotation {
    }
    

JDK內建註解

JDK內建了哪些註解?

java.lang包下的註釋型別:

掌握:Deprecated 用 @Deprecated 註釋的程式元素,表示已經過時,不鼓勵程式設計師使用這樣的元素,通常是因為它很危險或存在更好的選擇。

掌握:Override 表示一個方法宣告打算重寫超類中的另一個方法宣告。

不要掌握:SuppressWarnings 指示應該在註釋元素(以及包含在該註釋元素中的所有程式元素)中取消顯示指定的編譯器警告。

Override註解

package se7;
/*
關於JDK lang包下的Override註解
原始碼:
    public @interface Override { }

標識性註解:給編譯器做參考的,編譯器看到方法上有這個註解的時候,編譯器會自動檢查該方法是否重寫了父類的方法,如果沒有重寫,報錯。

這個註解只是在編譯階段起作用,和執行期無關
 */

//@Override這個註解只能註解方法
//Override這個註解是給編譯器參考的,和執行階段沒有關係
//凡是java中的方法帶有這個註解的,編譯器都會進行編譯檢查,如果這個方法不是重寫父類的方法,編譯器報錯。

public class AnnotationTest02 {

    @Override
    public String toString(){
        return "toString";
    }
    /*
    //方法名寫錯會報錯,提示你進行重寫
    @Override
    public String toString2(){
        return "toString";
    }
    */
}

Deprecated註解

package se7;

//表示這個類已過時
@Deprecated
public class AnnotationTest03 {
    public static void main(String[] args) {

        AnnotationTest03 at = new AnnotationTest03();
        at.doSome();
    }
    //表示這個方法已過時
    @Deprecated
    public void doSome(){
        System.out.println("do Something!");
    }
    /*
    Deprecated這個註解標註的元素已過時
    這個註解主要是向其他程式設計師傳達一個資訊,告知已過時,有更好的解決方案存在
     */
    @Deprecated
    public static void doOther(){
        System.out.println("do Other...");
    }
}
class T{
    public static void main(String[] args) {
        AnnotationTest03 at = new AnnotationTest03();
        at.doSome();

        AnnotationTest03.doOther();

        try {
            Class c = Class.forName("java.util.Date");
            Object obj = c.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

註解當中的屬性

package se7.annotation;

public @interface MyAnnotation {
    /**
     * 通常在註解當中可以定義屬性,以下是MyAnnotation的name屬性
     * 看著像一個方法,但實際上我們稱之為屬性name
     * @return
     */
    String name();
    //顏色屬性
    String color();
    //年齡屬性
    int age() default 78;//預設值是78,這裡寫了,測試程式可以不用寫
}
package se7.annotation;

public class MyAnnotationTest {

    //報錯原因:如果一個註解當中有屬性,必須給屬性賦值
    /*
    @MyAnnotation
    public void doSome(){}
    */

    //@MyAnnotation(屬性名=屬性值,屬性名=屬性值)
    @MyAnnotation(name="阿波",color = "黃色")//age因為指定了預設值,可以不寫
    public void doSome(){

    }
}

value屬性名省略

package se7.annotation2;

public @interface MyAnnotation {

    /*
    指定一個value屬性
     */
    String value();

    //String email();
}
package se7.annotation2;

public class MyAnnotationTest {

    @MyAnnotation(value = "hehe")
    public void doSome(){

    }
    /*
    如果一個註解的屬性的名字是value,
    並且只有一個屬性的話,在使用的時候,
    該屬性名可以省略,如下所示。
     */
    @MyAnnotation("haha")
    public void doOther(){

    }
}

註解中屬性的型別

package se7.annotation3;

public @interface MyAnnotation {
    /*
    註解當中的屬性可以是哪種型別?
    可以是:byte short int long float double boolean char String Class 列舉型別,以及這些型別的陣列形式。
     */
    int value1();

    String value2();

    int[] value3();

    String[] value4();

    Season value5();

    Season[] value6();

    Class parameterType();

    Class[] parameterTypes();
}
package se7.annotation3;

public class OtherAnnotationTest {

    //陣列是大括號
    @OtherAnnotation(age = 25,email = {"阿波@123.com","波波@321.com"},seasonArray = {Season.SPRING,Season.SUMMER})
    public void doSome(){

    }

    //如果陣列中只有1個元素,大括號可以省略
    @OtherAnnotation(age = 25,email = "阿波@123.com",seasonArray = Season.WINTER)
    public void doOther(){

    }
}
package se7.annotation3;

public @interface OtherAnnotation {

    int age();

    //郵箱地址屬性,支援多個(陣列)
    String[] email();

    /**
     * 季節陣列,Season是列舉型別
     * @return
     */
    Season[] seasonArray();
}
package se7.annotation3;

public enum Season {
    SPRING,SUMMER,AUTUMN,WINTER
}

元註解

用來標註“註解型別”的“註解”,稱為元註解

常見的元註解:Target、Retention

關於Target註解

這是一個元註解,用來標註 “註解型別” 的“註解“

這個Target註解用來標註“被標註的註解”可以出現在哪些位置上。

@Target(ElementType.METHOD)
//表示“被標註的註解”只能出現在方法上。

@Target(Value={CONSTRUCTOR},FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,NODUKE,PARAMETER,TYPE)
//這個表示該註解可以出現在:構造方法上、欄位上、區域性變數上、方法上...類上...

關於@Retention註解

這是一個元註解,這個Retention註解用來標註“被標註的註解”最終儲存在哪裡。

@Retention(RetentionPolicy.SOURCE)//表示該註解只能被保留在java原始檔中。
@Retention(RetentionPolicy.CLASS)//表示該註解被保留在class檔案中
@Retention(RetentionPolicy.RUNTIME)//表示該註解被儲存在class檔案中並且可以被反射機制所讀取

通過反射獲取註解

package se7.annotation4;

public class ReflectAnnotationTest {
    public static void main(String[] args) throws Exception {
        //獲取類
        Class c = Class.forName("se7.annotation4.MyAnnotationTest");

        //判斷類上面是否有@MyAnnotation
        System.out.println(c.isAnnotationPresent(MyAnnotation.class));//true

        if (c.isAnnotationPresent(MyAnnotation.class)){
            //獲取該註解物件
            MyAnnotation myAnnotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
            System.out.println("類上面的註解物件" + myAnnotation);//類上面的註解物件@se7.annotation4.MyAnnotation()

            //獲取註解物件的屬性,和調介面沒區別
            String value = myAnnotation.value();
            System.out.println(value);//上海浦東區,指定了新的值,不然還是預設值
        }

        //判斷String類上面是否存在這個註解
        Class stringClass = Class.forName("java.lang.String");
        System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class));//false
    }
}
package se7.annotation4;

@MyAnnotation("上海浦東區")//預設是廣州天河區,這裡指定了新的值
public class MyAnnotationTest {

    //@MyAnnotation,報錯
    int i;

    //@MyAnnotation,報錯
    public MyAnnotationTest() {
    }

    @MyAnnotation
    public void doSome(){
        //@MyAnnotation,報錯
        int i;
    }
}
package se7.annotation4;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//只允許該註解可以標註類、方法
@Target({ElementType.TYPE,ElementType.METHOD})
//希望這個註解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    /*
    value屬性
     */
    String value() default "廣州天河區";//預設值
}

獲取使用者名稱和密碼例子

package se7.annotation5;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    /*
    username屬性
     */
    String username();
    /*
    password屬性
     */
    String password();
}
import java.lang.reflect.Method;

public class MyAnnotationTest {

    @MyAnnotation(username = "admin",password = "123")
    public void doSome(){

    }

    public static void main(String[] args) throws Exception {

        //獲取MyAnnotationTest的doSome()方法上面的註解資訊
        Class c = Class.forName("se7.annotation5.MyAnnotationTest");
        //獲取doSome方法
        Method doSomeMethod = c.getDeclaredMethod("doSome");
        //判斷該方法上是否存在這個註解
        if (doSomeMethod.isAnnotationPresent(MyAnnotation.class)){
            MyAnnotation myAnnotation = doSomeMethod.getAnnotation(MyAnnotation.class);
            System.out.println(myAnnotation.username());//admin
            System.out.println(myAnnotation.password());//123
        }
    }
}

註解在開發中的作用

需求:假設有這樣一個註解,叫做:@ID,這個註解只能出現在類上面,當這個類上有這個註解的時候,要求類中必須有一個int型別的id屬性,如果沒有這個屬性就包異常,如果有這個屬性則正常執行。

例子

package se7.annotation6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//表示這個註解只能出現在類上面
@Target(ElementType.TYPE)
//表示該註解可以被反射機制讀取到
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {

}
//這個註解@ID用來標註類,被標註的類中必須有一個int型別的id屬性,沒有就報異常
package se7.annotation6;

@Id
public class User {

    //int id;
    String name;
    String password;
}
package se7.annotation6;

/*
自定義異常
 */
public class HasNotIdPropertyException extends RuntimeException{
    public HasNotIdPropertyException(){

    }
    public HasNotIdPropertyException(String s){
        super(s);
    }
}
package se7.annotation6;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        //獲取類
        Class userClass = Class.forName("se7.annotation6.User");
        //判斷類上是否存在ID註解
        if (userClass.isAnnotationPresent(Id.class)){
            //當一個類上面有ID註解的時候,要求類中必須存在int型別的id屬性
            //如果沒有int型別的id屬性則報異常
            //獲取類的屬性
            Field[] fields = userClass.getDeclaredFields();
            boolean isOk = false;//給一個預設的標記
            for (Field field : fields){
                if("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())){
                    //表示這個類是合法的類,有@ID註解,則這個類中必須有int型別的id
                    isOk = true;//表示合法
                    break;
                }
            }
            //判斷是否合法
            if (!isOk){
                throw new HasNotIdPropertyException("被@ID註解標註的類中必須要有一個int型別的id屬性");
            }
            //若沒有id屬性,如下
            //Exception in thread "main" se7.annotation6.HasNotIdPropertyException: 被@ID註解標註的類中必須要有一個int型別的id屬性
        }
    }
}