1. 程式人生 > 實用技巧 >深度分析:註解和反射,註解自定義與反射具體使用例項全幫你搞明白

深度分析:註解和反射,註解自定義與反射具體使用例項全幫你搞明白

前言

註解就是原始碼的元資料,通熟的講就是程式碼中的標籤。註解就有如下的特點:

  1. 註解是一個附屬品,依賴於其他元素(包、類、方法、屬性等等)存在。
  2. 註解本身沒有作用,在恰當的時候由外部程式進行解析才會發生作用。

註解有哪些?

  1. 按來源分
    • JDK 自帶註解,例如:@Override, @Deprecated, @SuppressWornings 。
    • 第三方註解。
    • 自定義註解。
  2. 按生命週期劃分
    • SOURCE:只存在於原始碼中,編譯成 class 檔案就不存在了。
    • Class:存在於原始碼中和 class 檔案中。
    • RUNTIME:註解保留到執行時。

什麼是元註解?

元註解指的是用於構成註解的註解,包括如下幾個:

  1. @Retention:指明 Annotation 的生命週期,傳入的值是一個列舉型別,可選值為:
    • RetentionPolicy.SOURCE
    • RetentionPolicy.CLASS
    • RetentionPolicy.RUNTIME
  2. @Target:指明 Annotation 可以修飾程式哪些元素,傳入的值為ElemetType[] 型別,值可為:
    • ElementType.CONSTRUCTOR :構造器
    • ElementType.FIELD:屬性
    • ElementType.LOCAL_VARIABLE:區域性變數
    • ElementType.METHOD:方法
    • ElementType.PACKAGE
      :包
    • ElementType.PARAMETER:引數
    • ElementType.TYPE:類、介面(包括註解型別和 enum 宣告)
  3. @Documented:使用此修飾的註解將會被 javadoc 工具提取成文件,使用此註解,其 @Retention 必須被設定為 RetentionPolicy.RUNTIME 。
  4. @Inherited:具有繼承性。

如何自定義註解?

自定義註解需要注意的問題:

  1. 使用 @interface 關鍵字定義。

  2. 自動繼承 java.lang.annotation.Annotation 介面。

  3. 配置引數的型別只能是八大基本型別、String、Class、enum、Annotation 和對應的陣列型別。

  4. 配置引數宣告的語法格式如下([] 表示可省略):

    型別 變數名() [default 預設值];
    
    
  5. 如果只有一個配置引數,其引數名必須為 value。

  6. 如果定義的註解含有配置引數,那在使用註解時,必須指定引數值,指定形式為:“引數名=引數值”。如果只有一個引數,直接寫引數值即可,定義中指定了預設值的引數可以不指定值,但沒有的一定要指定值

  7. 沒有成員的註解為標記,包含成員的稱為元資料

自定義註解例項(定義和使用)

參考程式碼:

(1)Test01.java

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

public class Test01 {
    @Info(name = "張三",address="北京")
    public void test01(){

    }
    @One("value")
    public void test02(){

    }
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE,ElementType.METHOD})
    @interface Info {
        String name();
        String address();

        int age()default 18;
    }
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @interface One{
        String value();
    }
}

反射

什麼是反射?

反射指的是程式在執行期間藉助反射 API 取得任何類的內部資訊,並通過這些內部資訊去操作對應物件的內部屬性和方法。

任何一個類,在第一次使用時,就會被 JVM 載入到堆記憶體的方法區中。JVM 載入類成功後,就會在方法區中產生一個對應的 Class 物件(一個類只要一個 Class 物件),這個 Class 物件包含被載入類的全部結構資訊。

如何獲取class物件?

(1)類的 class 屬性

每一個類,都有一個 class 靜態屬性,這個靜態屬性就是類對應的 Class 物件。

1 Class<Person> cl1 = Person.class; 

(2)Object 物件 的 getClass() 方法

1 Person p1 = new Person();
2 Class<Person> cl2 = (Class<Person>) p1.getClass(); 

(3)通過 Class 類的 forName() 方法(最常用)

1 try {
2     Class cl3 = Class.forName("com.llm.hkl.Person");
3 } catch (ClassNotFoundException e) {
4     e.printStackTrace();
5 }

(4)通過 ClassLoader 類(不常用)

1 ClassLoader cl = Person.class.getClassLoader();
2 try {
3     Class cl4 = cl.loadClass("com.llm.hkl.Person");
4 } catch (ClassNotFoundException e) {
5     e.printStackTrace();
6 }

如何使用反射?

反射的基本使用包括建立物件,設定屬性和呼叫方法。Class 物件中大多數 get 方法有 Declared 和無 Declared,他們的區別是:

  1. 無 Declared:只能獲取到 public 修飾的,包括當前類和所有父類。
  2. 有 Declared:獲取到當前類所有的(含有 private),但不包括其父類。

反射例項:

Person類和註解類(準備):

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

@Info(name = "張三", habbit = "程式設計")
public class Person {
    @Filed("張三")
    private String name;
    public String habbit;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHabbit() {
        return habbit;
    }

    public void setHabbit(String habbit) {
        this.habbit = habbit;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", habbit='" + habbit + '\'' +
                ", age=" + age +
                '}';
    }

    private String say(String str) {
        String str1 = name + "說:" + str;
        System.out.println(str1);
        return str1;
    }

    public void eat(String food) {
        System.out.println(name + "吃" + food);
    }

    public Person() {

    }

    public Person(String name, String habbit, int age) {
        this.name = name;
        this.habbit = habbit;
        this.age = age;
    }

    private Person(String name, String habbit) {
        this.name = name;
        this.habbit = habbit;
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    @interface Info {
        String name();

        String habbit();

        int age() default 18;
    }
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Filed{
        String value();
    }
}

reflect用法一(獲取類的基本資訊):

獲得類的名字
獲得類的屬性
獲得指定屬性的值
獲得類的方法
獲得指定方法
獲取構造器
獲得指定的構造器

 Class<?> person = Class.forName("Person");
        //獲得類的名字
        System.out.println("<-----------------------------------獲得類的名字-------------------------->");
        String name = person.getName();
        System.out.println(name);

        //獲得類的屬性
        System.out.println("<-------------------------------------獲得類的屬性--------------------->");
            //僅public
        System.out.println("<-----------------getFields----------------->");
        Field[] fields = person.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("<-----------------getDeclaredFields----------------->");
        Field[] declaredFields = person.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //獲得指定屬性的值
        System.out.println("<--------------------獲得類的指定屬性--------------------------------------->");
        Field name1 = person.getDeclaredField("name");
        System.out.println(name1);

        //獲得類的方法
        System.out.println("<--------------------獲得類的方法--------------------------------------->");
            //僅public
        System.out.println("<-----------------getMethods----------------->");
        Method[] methods = person.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("<-----------------getDeclaredMethods----------------->");
        Method[] declaredMethods = person.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        //獲得指定方法
        System.out.println("<-------------------------獲得類的指定方法--------------------------------------->");
        Method say = person.getDeclaredMethod("say",String.class);
        System.out.println(say);

        //獲取構造器
        System.out.println("<--------------------------獲得類的構造器--------------------------------->");
        System.out.println("<-----------------getConstructors----------------->");
            //僅public
        Constructor<?>[] constructors = person.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("<-----------------getDeclaredConstructors----------------->");
        Constructor<?>[] declaredConstructors = person.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        //獲得指定的構造器
        System.out.println("<---------------------------獲得類指定的構造器------------------------------>");
        Constructor<?> declaredConstructor = person.getDeclaredConstructor(String.class, String.class, int.class);
        System.out.println(declaredConstructor);

reflect用法二(建立並操作物件):

反射方式建立物件
呼叫無參構造器建立物件
通過有參構造器
通過私有的構造器
通過反射設定公有屬性
通過反射設定私有屬性
通過反射呼叫方法
通過反射呼叫私有方法

Class<?> person = Class.forName("Person");

        //  反射方式建立物件
        System.out.println("<-----反射方式建立物件----->");
        Person instance01 = (Person) person.newInstance();
        System.out.println(instance01);

        //  呼叫無參構造器建立物件
        System.out.println("<-----呼叫無參構造器建立物件----->");
        Constructor<?> noneConstructor = person.getDeclaredConstructor();
        Person instance02 = (Person) noneConstructor.newInstance();
        System.out.println(instance02);

        //  呼叫有參構造器建立物件
        System.out.println("<-----呼叫有參構造器建立物件----->");
        Constructor<?> constructor = person.getDeclaredConstructor(String.class, String.class, int.class);
        Person instance03 = (Person) constructor.newInstance("張三", "程式設計", 18);
        System.out.println(instance03);

        //  呼叫私有的構造器建立物件
        System.out.println("<-----呼叫私有的構造器建立物件----->");
        java.lang.reflect.Constructor<?> privateConstructor = person.getDeclaredConstructor(String.class, String.class);
        privateConstructor.setAccessible(true);
        Object instance04 = privateConstructor.newInstance("張三", "程式設計");
        System.out.println(instance04);

        //  通過反射設定公有屬性
        System.out.println("<-----通過反射設定公有屬性----->");
        Field habbit = person.getDeclaredField("habbit");
        habbit.set(instance01, "程式設計");
        System.out.println(instance01);

        //  通過反射設定私有屬性
        System.out.println("<-----通過反射設定私有屬性----->");
        Field name = person.getDeclaredField("name");
        name.setAccessible(true);
        name.set(instance01, "張三");
        System.out.println(instance01);
        //  通過反射呼叫公有方法
        System.out.println("<-----通過反射調公用方法----->");
        Method eat = person.getDeclaredMethod("eat", String.class);
        eat.invoke(instance01,"飯");
        //  通過反射呼叫私有方法
        System.out.println("<-----通過反射呼叫私有方法----->");
        Method say = person.getDeclaredMethod("say", String.class);
        say.setAccessible(true);
        say.invoke(instance01,"呵呵");

reflect用法三(獲取泛型資訊):

Java採用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器 Javac使用的,確保資料的安全性和免去強制型別轉換問題,但是,一旦編譯完成,所有和泛型有關的型別全部擦除。
為了通過反射操作這些型別,Java新增了 ParameterizedType, GenericArrayTypeType Variable和 WildcardType幾種型別來代表不能被歸一到class類中的型別但是又和原始型別齊名的型別。

ParameterizedType:表示一種引數化型別比如 Collection< String>

GenericArrayType:表示種元素型別是引數化型別或者型別變數的陣列型別

TypeVariable:是各種型別變數的公共父介面

WildcardType:代表種萬用字元型別表示式

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test03 {
    public void test01(Map<String, Person> map, List<Person> list) {
        System.out.println("test01");
    }
    public Map<String, Person> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws Exception {
        Class<Test03> test03 = Test03.class;

        System.out.println("<--------test01---------->");
        Method test01 = test03.getDeclaredMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = test01.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            if (genericParameterType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        System.out.println("<--------test02---------->");
        Method test02 = test03.getDeclaredMethod("test02");
        Type genericReturnType = test02.getGenericReturnType();
        System.out.println(genericReturnType);
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
}

執行結果:

reflect用法四(獲取註解資訊):

獲取類上的註解
獲取屬性上的註解
獲取方法上的註解

		Class<?> person = Class.forName("Person");
        System.out.println("<----------獲取類上的註解------------->");
        //獲取類上的註解
        Info annotation = person.getAnnotation(Info.class);
        System.out.println(annotation.name());
        System.out.println(annotation.habbit());
        System.out.println(annotation.age());

        System.out.println("<----------獲取屬性上的註解------------->");
        //獲取屬性上的註解
        Field name = person.getDeclaredField("name");
        Person.Filed file = name.getAnnotation(Person.Filed.class);
        String value = file.value();
        System.out.println(value);
        //獲取方法上的註解  略。。。

執行結果:

最後

感謝你看到這裡,看完有什麼的不懂的可以在評論區問我,覺得文章對你有幫助的話記得給我點個贊,每天都會分享java相關技術文章或行業資訊,歡迎大家關注和轉發文章!