深度分析:注解和反射,注解自定義與反射具體使用實(shí)例全幫你搞明白

前言

注解就是源代碼的元數(shù)據(jù)谎势,通熟的講就是代碼中的標(biāo)簽凛膏。注解就有如下的特點(diǎn):

  1. 注解是一個(gè)附屬品,依賴于其他元素(包脏榆、類猖毫、方法、屬性等等)存在须喂。
  2. 注解本身沒有作用吁断,在恰當(dāng)?shù)臅r(shí)候由外部程序進(jìn)行解析才會(huì)發(fā)生作用。

注解有哪些坞生?

  1. 按來源分
    • JDK 自帶注解仔役,例如:@Override, @Deprecated, @SuppressWornings 。
    • 第三方注解是己。
    • 自定義注解又兵。
  2. 按生命周期劃分
    • SOURCE:只存在于源代碼中,編譯成 class 文件就不存在了卒废。
    • Class:存在于源代碼中和 class 文件中沛厨。
    • RUNTIME:注解保留到運(yùn)行時(shí)乘盼。

什么是元注解?

元注解指的是用于構(gòu)成注解的注解俄烁,包括如下幾個(gè):

  1. @Retention:指明 Annotation 的生命周期绸栅,傳入的值是一個(gè)枚舉類型,可選值為:
    • RetentionPolicy.SOURCE
    • RetentionPolicy.CLASS
    • RetentionPolicy.RUNTIME
  2. @Target:指明 Annotation 可以修飾程序哪些元素页屠,傳入的值為ElemetType[] 類型粹胯,值可為:
    • ElementType.CONSTRUCTOR :構(gòu)造器
    • ElementType.FIELD:屬性
    • ElementType.LOCAL_VARIABLE:局部變量
    • ElementType.METHOD:方法
    • ElementType.PACKAGE:包
    • ElementType.PARAMETER:參數(shù)
    • ElementType.TYPE:類、接口(包括注解類型和 enum 聲明)
  3. @Documented:使用此修飾的注解將會(huì)被 javadoc 工具提取成文檔辰企,使用此注解风纠,其 @Retention 必須被設(shè)置為 RetentionPolicy.RUNTIME
  4. @Inherited:具有繼承性牢贸。

如何自定義注解竹观?

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

  1. 使用 @interface 關(guān)鍵字定義。

  2. 自動(dòng)繼承 java.lang.annotation.Annotation 接口潜索。

  3. 配置參數(shù)的類型只能是八大基本類型臭增、String、Class竹习、enum誊抛、Annotation 和對應(yīng)的數(shù)組類型。

  4. 配置參數(shù)聲明的語法格式如下([] 表示可省略):

    類型 變量名() [default 默認(rèn)值];
    
    
  5. 如果只有一個(gè)配置參數(shù)整陌,其參數(shù)名必須為 value拗窃。

  6. 如果定義的注解含有配置參數(shù),那在使用注解時(shí)泌辫,必須指定參數(shù)值随夸,指定形式為:“參數(shù)名=參數(shù)值”。如果只有一個(gè)參數(shù)震放,直接寫參數(shù)值即可宾毒,定義中指定了默認(rèn)值的參數(shù)可以不指定值,但沒有的一定要指定值澜搅。

  7. 沒有成員的注解為標(biāo)記伍俘,包含成員的稱為元數(shù)據(jù)

自定義注解實(shí)例(定義和使用)

參考代碼:

(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();
    }
}

反射

什么是反射勉躺?

反射指的是程序在運(yùn)行期間借助反射 API 取得任何類的內(nèi)部信息癌瘾,并通過這些內(nèi)部信息去操作對應(yīng)對象的內(nèi)部屬性和方法。

任何一個(gè)類饵溅,在第一次使用時(shí)妨退,就會(huì)被 JVM 加載到堆內(nèi)存的方法區(qū)中。JVM 加載類成功后,就會(huì)在方法區(qū)中產(chǎn)生一個(gè)對應(yīng)的 Class 對象(一個(gè)類只要一個(gè) Class 對象)咬荷,這個(gè) Class 對象包含被加載類的全部結(jié)構(gòu)信息冠句。

如何獲取class對象?

(1)類的 class 屬性

每一個(gè)類幸乒,都有一個(gè) class 靜態(tài)屬性懦底,這個(gè)靜態(tài)屬性就是類對應(yīng)的 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 }

如何使用反射聚唐?

反射的基本使用包括創(chuàng)建對象,設(shè)置屬性和調(diào)用方法腔召。Class 對象中大多數(shù) get 方法有 Declared 和無 Declared杆查,他們的區(qū)別是:

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

反射實(shí)例:

Person類和注解類(準(zhǔn)備):

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用法一(獲取類的基本信息):

獲得類的名字
獲得類的屬性
獲得指定屬性的值
獲得類的方法
獲得指定方法
獲取構(gòu)造器
獲得指定的構(gòu)造器

 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);

        //獲取構(gòu)造器
        System.out.println("<--------------------------獲得類的構(gòu)造器--------------------------------->");
        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);
        }
        //獲得指定的構(gòu)造器
        System.out.println("<---------------------------獲得類指定的構(gòu)造器------------------------------>");
        Constructor<?> declaredConstructor = person.getDeclaredConstructor(String.class, String.class, int.class);
        System.out.println(declaredConstructor);

reflect用法二(創(chuàng)建并操作對象):

反射方式創(chuàng)建對象
調(diào)用無參構(gòu)造器創(chuàng)建對象
通過有參構(gòu)造器
通過私有的構(gòu)造器
通過反射設(shè)置公有屬性
通過反射設(shè)置私有屬性
通過反射調(diào)用方法
通過反射調(diào)用私有方法

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

        //  反射方式創(chuàng)建對象
        System.out.println("<-----反射方式創(chuàng)建對象----->");
        Person instance01 = (Person) person.newInstance();
        System.out.println(instance01);

        //  調(diào)用無參構(gòu)造器創(chuàng)建對象
        System.out.println("<-----調(diào)用無參構(gòu)造器創(chuàng)建對象----->");
        Constructor<?> noneConstructor = person.getDeclaredConstructor();
        Person instance02 = (Person) noneConstructor.newInstance();
        System.out.println(instance02);

        //  調(diào)用有參構(gòu)造器創(chuàng)建對象
        System.out.println("<-----調(diào)用有參構(gòu)造器創(chuàng)建對象----->");
        Constructor<?> constructor = person.getDeclaredConstructor(String.class, String.class, int.class);
        Person instance03 = (Person) constructor.newInstance("張三", "編程", 18);
        System.out.println(instance03);

        //  調(diào)用私有的構(gòu)造器創(chuàng)建對象
        System.out.println("<-----調(diào)用私有的構(gòu)造器創(chuàng)建對象----->");
        java.lang.reflect.Constructor<?> privateConstructor = person.getDeclaredConstructor(String.class, String.class);
        privateConstructor.setAccessible(true);
        Object instance04 = privateConstructor.newInstance("張三", "編程");
        System.out.println(instance04);

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

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

reflect用法三(獲取泛型信息):

Java采用泛型擦除的機(jī)制來引入泛型,Java中的泛型僅僅是給編譯器 Javac使用的,確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換問題,但是,一旦編譯完成,所有和泛型有關(guān)的類型全部擦除浊仆。
為了通過反射操作這些類型,Java新增了 ParameterizedType, GenericArrayTypeType Variable和 WildcardType幾種類型來代表不能被歸一到class類中的類型但是又和原始類型齊名的類型客峭。

ParameterizedType:表示一種參數(shù)化類型比如 Collection< String>

GenericArrayType:表示種元素類型是參數(shù)化類型或者類型變量的數(shù)組類型

TypeVariable:是各種類型變量的公共父接口

WildcardType:代表種通配符類型表達(dá)式

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);
            }
        }
    }
}

運(yùn)行結(jié)果:

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);
        //獲取方法上的注解  略。氧卧。桃笙。

運(yùn)行結(jié)果:

最后

感謝你看到這里,看完有什么的不懂的可以在評論區(qū)問我沙绝,覺得文章對你有幫助的話記得給我點(diǎn)個(gè)贊,每天都會(huì)分享java相關(guān)技術(shù)文章或行業(yè)資訊鼠锈,歡迎大家關(guān)注和轉(zhuǎn)發(fā)文章闪檬!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市购笆,隨后出現(xiàn)的幾起案子械哟,更是在濱河造成了極大的恐慌菠赚,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異习绢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淘讥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門士袄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人襟锐,你說我怎么就攤上這事撤逢。” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵蚊荣,是天一觀的道長初狰。 經(jīng)常有香客問我,道長互例,這世上最難降的妖魔是什么奢入? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮媳叨,結(jié)果婚禮上腥光,老公的妹妹穿的比我還像新娘。我一直安慰自己肩杈,他們只是感情好柴我,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扩然,像睡著了一般艘儒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夫偶,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天界睁,我揣著相機(jī)與錄音,去河邊找鬼兵拢。 笑死翻斟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的说铃。 我是一名探鬼主播访惜,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腻扇!你這毒婦竟也來了债热?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤幼苛,失蹤者是張志新(化名)和其女友劉穎窒篱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舶沿,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡墙杯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了括荡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片高镐。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖一汽,靈堂內(nèi)的尸體忽然破棺而出避消,到底是詐尸還是另有隱情低滩,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布岩喷,位于F島的核電站恕沫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纱意。R本人自食惡果不足惜婶溯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偷霉。 院中可真熱鬧迄委,春花似錦、人聲如沸类少。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硫狞。三九已至信轿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間残吩,已是汗流浹背财忽。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泣侮,地道東北人即彪。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像活尊,于是被迫代替她去往敵國和親隶校。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348