Java註解與反射的使用

eck_燃發表於2020-12-27

學前須知

具有Java基礎知識,包括JVM類載入,反射等基礎。
反射是框架的前提,通過註解和反射機制,java實現了動態語言的一部分特性

元註解

Java 定義了一套註解,共有 7 個,3 個在 java.lang 中,剩下 4 個在 java.lang.annotation 中。

java.lang 中的註解

@Override - 檢查該方法是否是重寫方法。如果發現其父類,或者是引用的介面中並沒有該方法時,會報編譯錯誤。
@Deprecated - 標記過時方法。如果使用該方法,會報編譯警告。有這個註解的方法不建議使用
@SuppressWarnings - 指示編譯器去忽略註解中宣告的警告。去除告警波浪線,專治強迫症。

java.lang.annotation中的註解

@Target - 註解使用的範圍,比如包,類,方法,欄位
@Retention - 表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期(一般都是RUNTIME級別)
@Documented - 標記這些註解是否包含在javadoc中
@Inherited - 說明子類可以繼承父類的註解

// 使用註解
@MyAnnotation
public class Test01 {

}
// 定義一個註解
// 註解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
// 註解在什麼地方有效,一般是Runtime
@Retention(value = RetentionPolicy.RUNTIME)
// 將註解生成在JavaDoc中
@Documented
// 子類繼承父類註解
@Inherited
// 定義一個註解
@interface MyAnnotation{

}

註解的定義和傳參

// 無預設值的註解
@MyAnnotation2(name = "qwer")
public class Test02 {
}
// 引數預設為value,且有預設值的註解
@MyAnnotation3
class Test02_2 {
}
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    // 預設值名是value
    String value() default "";
    // 定義變數後面要加()
    String name();
    // 可以給變數賦預設值
    int age() default 18;
}
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    // 預設值名是value
    String value() default "預設值";
}

反射

Reflection (反射)是Java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並能直接操作任意物件的內部屬性及方法

載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一 個類只有
一個Class物件), 這個物件就包含了完整的類的結構資訊。我們可以通過這個對
象看到類的結構。這個物件就像一面鏡子, 透過這個鏡子看到類的結構,所以,
我們形象的稱之為:反射
在這裡插入圖片描述

五種獲取Class物件的方法

在這裡插入圖片描述

反射的使用

public class Test03 {
    public static void main(String[] args) throws Exception{
        Class c1 = Class.forName("Test");
        Method methods[] = c1.getMethods();

        System.out.println("獲取到method[0]方法名 "+methods[0].getName());

        // 注意!,獲取類的方法順序與類定義方法的順序不一定一致 多次執行結果會有所不同
        // 通過反射,呼叫空參方法和有參方法
        methods[0].invoke(new Test(),null);
        methods[1].invoke(new Test(),"aaa"); //String.Class
        System.out.println("============================");
        Class c2 = Class.forName("Person");
        Method method1 = c2.getMethod("setName",String.class);
        // 類載入的理解,我set引數李四,最後打出來還是張三,參考圖片理解
        method1.invoke(new Person(),"李四");
        Method method2 = c2.getMethod("getName",null);
        System.out.println(method2.invoke(new Person()));
        // 獲取欄位
        Field f1 = c2.getDeclaredField("name");
        Field f2[] = c2.getFields();//獲取許可權為public的引數
        Field f3[] = c2.getDeclaredFields();//獲取所有的引數
        System.out.println(f1);

        System.out.println("============================");
        for (int i = 0; i < f3.length ; i++) {
            System.out.println(f3[i]);
        }
    }
}

class Test{
    public void fun1(){
        System.out.println("method f1 running");
    }
    public void fun2(String str){
        System.out.println("method f1 running args:"+str);
    }
   }
class Person{
    public String name = "張三";
    private int age = 18;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

參考圖片
在這裡插入圖片描述

反射建立物件

public class Test04 {
    public static void main(String[] args) throws Exception{
        Class c1 = Class.forName("User");
        // 無參的構造器,構造方法
        Constructor constructor1 = c1.getConstructor();
        User user1 = (User)constructor1.newInstance();
        System.out.println(user1.toString());
        // 有參的構造器,構造方法
        Constructor constructor2 = c1.getConstructor(String.class,String.class);
        //constructor2.setAccessible(true);//關閉許可權檢查,提升效能
        User user2 = (User)constructor2.newInstance("張三","123456");
        System.out.println(user2.toString());
    }
}
class User{
    public String username;
    public String password;
    public String getUsername() {
        return username;
    }
    public User() {

    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

反射操作註解

public class Test05 {
    public static void main(String[] args) throws  Exception{
        Class c1 = Class.forName("MyUser");
        Annotation annotations[] = c1.getAnnotations();

        TableUser userAnno = (TableUser) c1.getAnnotation(TableUser.class);
        String value = userAnno.value();
        System.out.printf(value);
        // 獲得類指定的註解
        Field id = c1.getDeclaredField("id");
        FieldUser fieldAnno = (FieldUser) id.getAnnotation(FieldUser.class);
        String columName = fieldAnno.columName();
        String type = fieldAnno.type();
        int length = fieldAnno.length();
        System.out.printf("%s %s %d",columName,type,length);
    }
}
@TableUser("user")
class MyUser{
    @FieldUser(columName = "db_id",type = "varchar",length = 64)
    public  String id;
    @FieldUser(columName = "db_age",type = "number",length = 3)
    public int age;
}

// 類的註解
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface TableUser{
    String value();
}
// 屬性的註解
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface FieldUser{
    String columName();
    String type();
    int length();
}

練習

註解運算元據庫示例
效果如圖
在這裡插入圖片描述
在這裡插入圖片描述

專案地址:
https://github.com/13884566853/annotation-test
補充
從 Java 7 開始,額外新增了 3 個註解:

@SafeVarargs - Java 7 開始支援,忽略任何使用引數為泛型變數的方法或建構函式呼叫產生的警告。
@FunctionalInterface - Java 8 開始支援,標識一個匿名函式或函式式介面。
@Repeatable - Java 8 開始支援,標識某註解可以在同一個宣告上使用多次。

相關文章