JavaSE-單元測試存和、反射、注解


[TOC]

第一章:單元測試

1.1 測試分類

  • 黑盒測試衷旅,不需要寫代碼捐腿,給輸入值,看程序是否能夠輸出期望的值柿顶。
  • 白盒測試茄袖,需要寫代碼的。關(guān)注程序具體的執(zhí)行流程嘁锯。
    • 利用java中的junit依賴環(huán)境是白盒測試的一種方式

1.2 Junit的使用步驟

  1. 定義一個測試類(測試用例)
  2. 定義測試方法:可以獨立運行
  3. 給方法加@Test
  4. 導(dǎo)入junit依賴環(huán)境
  5. 判定結(jié)果:
    • 紅色:失敗
    • 綠色:成功
    • 一般我們會使用斷言操作來處理結(jié)果
      • Assert.assertEquals(期望的結(jié)果,運算的結(jié)果);
  6. Before和After
    • @Before:
      • 修飾的方法會在測試方法之前被自動執(zhí)行
    • @After:
      • 修飾的方法會在測試方法執(zhí)行之后自動被執(zhí)行

1.3 Junit使用

  • 程序代碼

    /*
    * 計算器類
    * */
    public class Calc {
      public int add(int num1,int num2){
        return num1+num2;
      }
      public int subtract(int num1,int num2){
        return num1-num2;
      }
      public int multi(int num1,int num2){
        return num1*num2;
      }
      public int div(int num1,int num2){
        return num1+num2;
      }
    }
    
  • 測試代碼

    package cn.leilei.junitTest;
    
    import cn.leilei.junitDemo.Calc;
    import org.junit.After;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    
    public class CalcTest {
      private Calc calc;
      // 測試之前會執(zhí)行的代碼
      @Before
      public void init(){
        calc = new Calc();
      }
      @Test
      public void addTest(){
        int num = calc.add(10,20);
        // 斷言結(jié)果和計算結(jié)果判定
        Assert.assertEquals(30,num);
      }
      @Test
      public void subtractTest(){
        int num = calc.subtract(10,20);
        Assert.assertEquals(-10,num);
      }
      @Test
      public void multiTest(){
        int num = calc.multi(10,20);
        Assert.assertEquals(200,num);
      }
      @Test
      public void divTest(){
        int num = calc.div(20,10);
        Assert.assertEquals(2,num);
      }
      // 測試完畢后會執(zhí)行的方法
      @After
      public  void  after(){
        System.out.println("程序測試完畢");
      }
    
    }
    
    
  • 結(jié)果


  • 綠色標識宪祥,測試通過
  • 紅色標識,測試異常

第二章:反射

2.1 反射介紹

? Java反射機制指的是在Java程序運行狀態(tài)中家乘,對于任何一個類蝗羊,都可以獲得這個類的所有屬性和方法;對于給定的一個對象仁锯,都能夠調(diào)用它的任意一個屬性和方法耀找。這種動態(tài)獲取類的內(nèi)容以及動態(tài)調(diào)用對象的方法稱為反射機制。

? Java的反射機制允許編程人員在對類未知的情況下业崖,獲取類相關(guān)信息的方式變得更加多樣靈活野芒,調(diào)用類中相應(yīng)方法,是Java增加其靈活性與動態(tài)性的一種機制双炕。

2.2 反射的好處

? 首先狞悲,反射機制極大的提高了程序的靈活性和擴展性,降低模塊的耦合性妇斤,提高自身的適應(yīng)能力摇锋。

? 其次,通過反射機制可以讓程序創(chuàng)建和控制任何類的對象趟济,無需提前硬編碼目標類乱投。

? 再次,使用反射機制能夠在運行時構(gòu)造一個類的對象顷编、判斷一個類所具有的成員變量和方法戚炫、調(diào)用一個對象的方法。

? 最后媳纬,反射機制是構(gòu)建框架技術(shù)的基礎(chǔ)所在双肤,使用反射可以避免將代碼寫死在框架中施掏。

? 正是反射有以上的特征,所以它能動態(tài)編譯和創(chuàng)建對象茅糜,極大的激發(fā)了編程語言的靈活性七芭,強化了多態(tài)的特性,進一步提升了面向?qū)ο缶幊痰某橄竽芰γ镒福蚨艿骄幊探绲那嗖A

2.3 反射的缺點

反射機制的功能非常強大狸驳,但不能濫用。在能不使用反射完成時缩赛,盡量不要使用耙箍,原因有以下幾點:

1、性能問題酥馍。

Java反射機制中包含了一些動態(tài)類型辩昆,所以Java虛擬機不能夠?qū)@些動態(tài)代碼進行優(yōu)化。因此旨袒,反射操作的效率要比正常操作效率低很多汁针。我們應(yīng)該避免在對性能要求很高的程序或經(jīng)常被執(zhí)行的代碼中使用反射。而且砚尽,如何使用反射決定了性能的高低施无。如果它作為程序中較少運行的部分,性能將不會成為一個問題尉辑。

2帆精、安全限制。

使用反射通常需要程序的運行沒有安全方面的限制隧魄。如果一個程序?qū)Π踩蕴岢鲆笞苛罚瑒t最好不要使用反射。

3购啄、程序健壯性襟企。

反射允許代碼執(zhí)行一些通常不被允許的操作,所以使用反射有可能會導(dǎo)致意想不到的后果狮含。反射代碼破壞了Java程序結(jié)構(gòu)的抽象性顽悼,所以當程序運行的平臺發(fā)生變化的時候,由于抽象的邏輯結(jié)構(gòu)不能被識別几迄,代碼產(chǎn)生的效果與之前會產(chǎn)生差異蔚龙。

2.4 在Java中使用反射機制

1. 獲取Class對象的方式

  • 獲取方式

    1. Class.forName("全類名"),將字節(jié)碼文件加載進內(nèi)存映胁,返回Class對象
    2. 類名.class木羹,通過類名的屬性class獲取
    3. 對象.getClass():getClass()方法在Object類中定義著
  • 代碼

    • Person類

      public class Person {
        private String name;
        private int age;
        private boolean gender;
        public int height;
        public Person(String name, int age, boolean gender) {
          this.name = name;
          this.age = age;
          this.gender = gender;
        }
        public Person() {
        }
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        public int getAge() {
          return age;
        }
        public void setAge(int age) {
          this.age = age;
        }
        public boolean isGender() {
          return gender;
        }
        public void setGender(boolean gender) {
          this.gender = gender;
        }
        public void eat(){
          System.out.println("吃....");
        }
        public void eat(String food){
          System.out.println("吃"+food);
        }
      }
      
      
    • Test01類

      
      public class Test01 {
        public static void main(String[] args) throws Exception {
          // 方式1
          Class cls = Class.forName("cn.leilei.reflect.Person");
          // 方式2
          Class cls2 = Person.class;
          // 方式3
          Class cls3 = new Person().getClass();
          System.out.println(cls==cls2); // true
          System.out.println(cls==cls3); // true
          // 三種方式獲取的都是同一個Class類型對象
        }
      }
      

2. Class對象功能-獲取成員變量

  • 獲取成員變量的方式

    • Field[] getFields() :獲取所有public修飾的成員變量
    • Field getField(String name) 獲取指定名稱的 public修飾的成員變量
    • Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符
    • Field getDeclaredField(String name) 獲取指定名稱的成員變量,不考慮修飾符
  • 成員變量相關(guān)操作

    1. 設(shè)置值
      • void set(Object obj, Object value)
    2. 獲取值
      • get(Object obj)
    3. 忽略訪問權(quán)限修飾符的安全檢查
      • setAccessible(true):暴力反射
  • 代碼

    public class Test02 {
      public static void main(String[] args) throws Exception {
        // 【動態(tài)加載本地字節(jié)碼文件坑填,并獲取Person的Class對象】
        Class cls = Class.forName("cn.leilei.reflect.Person");
    
        // 【獲取成員變量】
        Field[]fields =  cls.getFields();
        System.out.println(Arrays.toString(fields));// [public int cn.leilei.reflect.Person.height]只獲取了一個權(quán)限是public修飾的成員變量
        Field[]fields2 = cls.getDeclaredFields();
        System.out.println(Arrays.toString(fields2)); // [private java.lang.String cn.leilei.reflect.Person.name, private int cn.leilei.reflect.Person.age, public int cn.leilei.reflect.Person.height, private boolean cn.leilei.reflect.Person.gender] 獲取的所有的成員變量抛人,不受修飾符的影響
        Field field = cls.getField("height");
        System.out.println(field);
        Field field2 = cls.getDeclaredField("age"); // public int cn.leilei.reflect.Person.height  受修飾符的影響
        System.out.println(field2);// private int cn.leilei.reflect.Person.age 不受修飾符的影響
    
        // 【獲取一個成員的值】
        Person p = new Person();
        // 忽略訪問權(quán)限修飾符的安全檢查
        field2.setAccessible(true);
        System.out.println(field2.get(p));  //0
    
        // 【設(shè)置一個成員的值】
        field2.set(p,18);
        System.out.println(p.getAge()); // 18
    
    
      }
    }
    

3. Class對象功能-獲取構(gòu)造方法

  • 獲取方式

    • Constructor<?>[] getConstructors()
    • Constructor<T> getConstructor(類<?>... parameterTypes)
    • Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
  • 構(gòu)造方法相關(guān)操作-創(chuàng)建實例對象

    • T newInstance(Object... initargs)
    • 如果使用空參數(shù)構(gòu)造方法創(chuàng)建對象,操作可以簡化:Class對象的newInstance方法
  • 代碼

    public class Test03 {
      public static void main(String[] args) throws Exception {
        // 【導(dǎo)入本地Person的字節(jié)碼文件脐瑰,獲取對應(yīng)的Class對象】
        Class cls = Class.forName("cn.leilei.reflect.Person");
        // 【獲取構(gòu)造函數(shù)】
        // 獲取Class對象中的所有構(gòu)造函數(shù)
        Constructor[]cs = cls.getConstructors();
        System.out.println(Arrays.toString(cs)); // [public cn.leilei.reflect.Person(java.lang.String,int,boolean), public cn.leilei.reflect.Person()]
        // 獲取Class對象中指定的帶參數(shù)構(gòu)造函數(shù)
        Constructor csParam = cls.getConstructor(String.class,int.class,boolean.class);
        System.out.println(csParam); // public cn.leilei.reflect.Person(java.lang.String,int,boolean)
        // 獲取Class對象中指定的無帶參數(shù)構(gòu)造函數(shù)
        Constructor csNoParam = cls.getConstructor();
        System.out.println(csNoParam); // public cn.leilei.reflect.Person()
        // 【操作構(gòu)造函數(shù)-創(chuàng)建對象】
        Person p = (Person) csParam.newInstance("張三",10,true);
        System.out.println(p); //name:張三,age:10,gender:true
        Person p1 = (Person)csNoParam.newInstance();  // 創(chuàng)建無參數(shù)對象妖枚,和cls.newInstance()等效
        System.out.println(p1); // name:null,age:0,gender:false
      }
    }
    

4. Class對象功能-獲取成員方法

  • 獲取方式

    • Method[] getMethods()
    • Method getMethod(String name, 類<?>... parameterTypes)
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, 類<?>... parameterTypes)
  • 成員方法的相關(guān)操作

    • Object invoke(Object obj, Object... args) 執(zhí)行方法
    • String getName():獲取方法名
  • 代碼

    public class Test04 {
      public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 【獲取Person的Class對象】
        Class<Person> cls = (Class<Person>)Class.forName("cn.leilei.reflect.Person");
        // 【獲取有參的構(gòu)造函數(shù)】
        Constructor cons = cls.getConstructor(String.class,int.class,boolean.class);
        // 【創(chuàng)建實例對象】
        Person p = (Person) cons.newInstance("張三",10,true);
        // 【獲取指定的方法-帶有字符串參數(shù)】
        Method eatMethond = cls.getMethod("eat",String.class);
        //【執(zhí)行方法】
        eatMethond.invoke(p,"雞蛋"); // 吃雞蛋
        // 【獲取指定的方法-無參數(shù)】
        Method eatMethond2 = cls.getMethod("eat");
        //【執(zhí)行方法】
        eatMethond2.invoke(p); // 吃...
        System.out.println(eatMethond.getName()); // eat 獲取方法名
      }
    }
    

5. Class對象功能-獲取全類名

  • 獲取方式

    • String getName()
  • 代碼

    public class Test05 {
      public static void main(String[] args) throws Exception {
        // 【獲取Person的Class對象】
        Class<Person> cls = (Class<Person>)Class.forName("cn.leilei.reflect.Person");
        System.out.println(cls.getName()); // cn.leilei.reflect.Person
      }
    }
    

2.5 反射案例-體驗反射靈活性

  • 需求:寫一個"框架",不能改變該類的任何代碼的前提下苍在,可以幫我們創(chuàng)建任意類的對象绝页,并且執(zhí)行其中

  • 實現(xiàn):配置文件 + 反射

  • 分析

    1. 將需要創(chuàng)建的對象的全類名和需要執(zhí)行的方法定義在配置文件中
    2. 在程序中加載讀取配置文件
    3. 使用反射技術(shù)來加載類文件進內(nèi)存
    4. 創(chuàng)建對象
    5. 執(zhí)行方法
  • 代碼

    • 配置文件

      className=cn.leilei.reflect.Person
      methondName=eat
      
    • 反射代碼

      public class Test06 {
        public static void main(String[] args) throws Exception {
          // 獲取類加載器
          ClassLoader loader = Test06.class.getClassLoader();
          // 使用類加載器獲取配置文件的文件流
          InputStream is = loader.getResourceAsStream("test.properties");
          // 創(chuàng)建Properties集合-存放配置信息
          Properties map = new Properties();
          map.load(is);
          // 根據(jù)配置信息,獲取要使用的類對應(yīng)的字節(jié)碼對象
          Class cs = Class.forName(map.getProperty("className"));
          // 獲取無參數(shù)的構(gòu)造函數(shù)并創(chuàng)建對象
          Object obj = cs.getConstructor().newInstance();
          // 根據(jù)配置信息忌穿,獲取要執(zhí)行的指定的方法
          Method method = cs.getMethod(map.getProperty("methondName"));
          // 執(zhí)行方法
          method.invoke(obj);
      
      
        }
      }
      
  • 好處

    • 不修改“框架”的源碼抒寂,僅更改配置文件滿足不同的對象創(chuàng)建和方法調(diào)用

第三章:注解

3.1 注解介紹

Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制掠剑。

Java 語言中的類、方法郊愧、變量朴译、參數(shù)和包等都可以被標注。Java 標注可以通過反射獲取標注內(nèi)容属铁。在編譯器生成類文件時眠寿,標注可以被嵌入到字節(jié)碼中。Java 虛擬機可以保留標注內(nèi)容焦蘑,在運行時可以獲取到標注內(nèi)容 盯拱。 當然它也支持自定義 Java 標注。

3.2 內(nèi)置的注解

Java 定義了一套注解例嘱,共有 7 個狡逢,3 個在 java.lang 中,剩下 4 個在 java.lang.annotation 中拼卵。

1. 作用在代碼上的注解

  • @Override - 檢查該方法是否是重載方法奢浑。如果發(fā)現(xiàn)其父類,或者是引用的接口中并沒有該方法時腋腮,會報編譯錯誤雀彼。
  • @Deprecated - 標記過時方法。如果使用該方法即寡,會報編譯警告徊哑。
  • @SuppressWarnings - 指示編譯器去忽略注解中聲明的警告。

2. 作用在其他注解的注解(或者說 元注解)

  • @Retention - 標識這個注解怎么保存聪富,是只在代碼中莺丑,還是編入class文件中,或者是在運行時可以通過反射訪問善涨。
  • @Documented - 標記這些注解是否包含在用戶文檔中窒盐。
  • @Target - 標記這個注解應(yīng)該是哪種 Java 成員草则。
  • @Inherited - 標記這個注解是繼承于哪個注解類(默認 注解并沒有繼承于任何子類)

3. 從 Java 7 開始,額外添加了 3 個注解

  • @SafeVarargs - Java 7 開始支持蟹漓,忽略任何使用參數(shù)為泛型變量的方法或構(gòu)造函數(shù)調(diào)用產(chǎn)生的警告炕横。
  • @FunctionalInterface - Java 8 開始支持,標識一個匿名函數(shù)或函數(shù)式接口葡粒。
  • @Repeatable - Java 8 開始支持份殿,標識某注解可以在同一個聲明上使用多次。

3.3 Annotation 架構(gòu)

從中嗽交,我們可以看出:

(01) 1 個 Annotation 和 1 個 RetentionPolicy 關(guān)聯(lián)卿嘲。

可以理解為:每1個Annotation對象,都會有唯一的RetentionPolicy屬性夫壁。

(02) 1 個 Annotation 和 1~n 個 ElementType 關(guān)聯(lián)拾枣。

可以理解為:對于每 1 個 Annotation 對象,可以有若干個 ElementType 屬性盒让。

(03) Annotation 有許多實現(xiàn)類梅肤,包括:Deprecated, Documented, Inherited, Override 等等。

Annotation 的每一個實現(xiàn)類邑茄,都 "和 1 個 RetentionPolicy 關(guān)聯(lián)" 并且 " 和 1~n 個 ElementType 關(guān)聯(lián)"姨蝴。

下面,我先介紹框架圖的左半邊(如下圖)肺缕,即 Annotation, RetentionPolicy, ElementType左医;然后在就 Annotation 的實現(xiàn)類進行舉例說明。

3.4 Annotation組成部分

java Annotation 的組成中同木,有 3 個非常重要的主干類浮梢。它們分別是:

Annotation.java

package java.lang.annotation;
public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}

ElementType.java

package java.lang.annotation;
public enum ElementType {
    TYPE,               /* 類、接口(包括注釋類型)或枚舉聲明  */
    FIELD,              /* 字段聲明(包括枚舉常量)  */
    METHOD,             /* 方法聲明  */
    PARAMETER,          /* 參數(shù)聲明  */
    CONSTRUCTOR,        /* 構(gòu)造方法聲明  */
    LOCAL_VARIABLE,     /* 局部變量聲明  */
    ANNOTATION_TYPE,    /* 注釋類型聲明  */
    PACKAGE             /* 包聲明  */
}

RetentionPolicy.java

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* Annotation信息僅存在于編譯器處理期間泉手,編譯器處理完之后就沒有該Annotation信息了  */
    CLASS,             /* 編譯器將Annotation存儲于類對應(yīng)的.class文件中黔寇。默認行為  */
    RUNTIME            /* 編譯器將Annotation存儲于class文件中,并且可由JVM讀入 */
}

說明

(01) Annotation 就是個接口斩萌。

"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關(guān)聯(lián)缝裤,并且與 "1~n 個 ElementType" 關(guān)聯(lián)〖绽桑可以通俗的理解為:每 1 個 Annotation 對象憋飞,都會有唯一的 RetentionPolicy 屬性;至于 ElementType 屬性姆吭,則有 1~n 個榛做。

(02) ElementType 是 Enum 枚舉類型,它用來指定 Annotation 的類型。

"每 1 個 Annotation" 都與 "1~n 個 ElementType" 關(guān)聯(lián)检眯。當 Annotation 與某個 ElementType 關(guān)聯(lián)時厘擂,就意味著:Annotation有了某種用途。例如锰瘸,若一個 Annotation 對象是 METHOD 類型刽严,則該 Annotation 只能用來修飾方法。

(03) RetentionPolicy 是 Enum 枚舉類型避凝,它用來指定 Annotation 的策略舞萄。通俗點說,就是不同 RetentionPolicy 類型的 Annotation 的作用域不同管削。

"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關(guān)聯(lián)倒脓。

  • a) 若 Annotation 的類型為 SOURCE,則意味著:Annotation 僅存在于編譯器處理期間含思,編譯器處理完之后崎弃,該 Annotation 就沒用了。 例如茸俭," @Override" 標志就是一個 Annotation吊履。當它修飾一個方法的時候,就意味著該方法覆蓋父類的方法调鬓;并且在編譯期間會進行語法檢查!編譯器處理完后酌伊,"@Override" 就沒有任何作用了腾窝。
  • b) 若 Annotation 的類型為 CLASS,則意味著:編譯器將 Annotation 存儲于類對應(yīng)的 .class 文件中居砖,它是 Annotation 的默認行為虹脯。
  • c) 若 Annotation 的類型為 RUNTIME,則意味著:編譯器將 Annotation 存儲于 class 文件中奏候,并且可由JVM讀入循集。

這時,只需要記住"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關(guān)聯(lián)蔗草,并且與 "1~n 個 ElementType" 關(guān)聯(lián)咒彤。學完后面的內(nèi)容之后,再回頭看這些內(nèi)容咒精,會更容易理解镶柱。

3.5 java 自帶的 Annotation

理解了上面的 3 個類的作用之后,我們接下來可以講解 Annotation 實現(xiàn)類的語法定義了模叙。

Annotation 通用定義

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}

說明:

上面的作用是定義一個 Annotation歇拆,它的名字是 MyAnnotation1。定義了 MyAnnotation1 之后,我們可以在代碼中通過 "@MyAnnotation1" 來使用它故觅。 其它的厂庇,@Documented, @Target, @Retention, @interface 都是來修飾 MyAnnotation1 的。下面分別說說它們的含義:

(01) @interface

使用 @interface 定義注解時输吏,意味著它實現(xiàn)了 java.lang.annotation.Annotation 接口权旷,即該注解就是一個Annotation。

定義 Annotation 時评也,@interface 是必須的炼杖。

注意:它和我們通常的 implemented 實現(xiàn)接口的方法不同。Annotation 接口的實現(xiàn)細節(jié)都由編譯器完成盗迟。通過 @interface 定義注解后坤邪,該注解不能繼承其他的注解或接口。

(02) @Documented

類和方法的 Annotation 在缺省情況下是不出現(xiàn)在 javadoc 中的罚缕。如果使用 @Documented 修飾該 Annotation艇纺,則表示它可以出現(xiàn)在 javadoc 中。

定義 Annotation 時邮弹,@Documented 可有可無黔衡;若沒有定義,則 Annotation 不會出現(xiàn)在 javadoc 中腌乡。

(03) @Target(ElementType.TYPE)

前面我們說過盟劫,ElementType 是 Annotation 的類型屬性。而 @Target 的作用与纽,就是來指定 Annotation 的類型屬性侣签。

@Target(ElementType.TYPE) 的意思就是指定該 Annotation 的類型是 ElementType.TYPE。這就意味著急迂,MyAnnotation1 是來修飾"類影所、接口(包括注釋類型)或枚舉聲明"的注解。

定義 Annotation 時僚碎,@Target 可有可無猴娩。若有 @Target,則該 Annotation 只能用于它所指定的地方勺阐;若沒有 @Target卷中,則該 Annotation 可以用于任何地方。

(04) @Retention(RetentionPolicy.RUNTIME)

前面我們說過皆看,RetentionPolicy 是 Annotation 的策略屬性仓坞,而 @Retention 的作用,就是指定 Annotation 的策略屬性腰吟。

@Retention(RetentionPolicy.RUNTIME) 的意思就是指定該 Annotation 的策略是 RetentionPolicy.RUNTIME无埃。這就意味著徙瓶,編譯器會將該 Annotation 信息保留在 .class 文件中,并且能被虛擬機讀取嫉称。

定義 Annotation 時侦镇,@Retention 可有可無。若沒有 @Retention织阅,則默認是 RetentionPolicy.CLASS壳繁。

java自帶的Annotation

通過上面的示例,我們能理解:@interface 用來聲明 Annotation荔棉,@Documented 用來表示該 Annotation 是否會出現(xiàn)在 javadoc 中闹炉, @Target 用來指定 Annotation 的類型,@Retention 用來指定 Annotation 的策略润樱。

理解這一點之后渣触,我們就很容易理解 java 中自帶的 Annotation 的實現(xiàn)類,即 Annotation 架構(gòu)圖的右半邊壹若。如下圖:

java 常用的 Annotation:

@Deprecated  -- @Deprecated 所標注內(nèi)容嗅钻,不再被建議使用。
@Override    -- @Override 只能標注方法店展,表示該方法覆蓋父類中的方法养篓。
@Documented  -- @Documented 所標注內(nèi)容,可以出現(xiàn)在javadoc中赂蕴。
@Inherited   -- @Inherited只能被用來標注“Annotation類型”柳弄,它所標注的Annotation具有繼承性。
@Retention   -- @Retention只能被用來標注“Annotation類型”概说,而且它被用來指定Annotation的RetentionPolicy屬性语御。
@Target      -- @Target只能被用來標注“Annotation類型”,而且它被用來指定Annotation的ElementType屬性席怪。
@SuppressWarnings -- @SuppressWarnings 所標注內(nèi)容產(chǎn)生的警告,編譯器會對這些警告保持靜默纤控。

由于 "@Deprecated 和 @Override" 類似挂捻,"@Documented, @Inherited, @Retention, @Target" 類似;下面船万,我們只對 @Deprecated, @Inherited, @SuppressWarnings 這 3 個 Annotation 進行說明刻撒。

2.1) @Deprecated

@Deprecated 的定義如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

說明:

  • (01) @interface -- 它的用來修飾 Deprecated,意味著 Deprecated 實現(xiàn)了 java.lang.annotation.Annotation 接口耿导;即 Deprecated 就是一個注解声怔。 (02) @Documented -- 它的作用是說明該注解能出現(xiàn)在 javadoc 中。
  • (03) @Retention(RetentionPolicy.RUNTIME) -- 它的作用是指定 Deprecated 的策略是 RetentionPolicy.RUNTIME舱呻。這就意味著醋火,編譯器會將Deprecated 的信息保留在 .class 文件中悠汽,并且能被虛擬機讀取。
  • (04) @Deprecated 所標注內(nèi)容芥驳,不再被建議使用柿冲。

例如,若某個方法被 @Deprecated 標注兆旬,則該方法不再被建議使用假抄。如果有開發(fā)人員試圖使用或重寫被 @Deprecated 標示的方法,編譯器會給相應(yīng)的提示信息丽猬。示例如下:


說明:

上面是 eclipse 中的截圖宿饱,比較類中 "getString1() 和 getString2()" 以及 "testDate() 和 testCalendar()" 。

(01) getString1() 被 @Deprecated 標注脚祟,意味著建議不再使用 getString1(); 所以 getString1() 的定義和調(diào)用時谬以,都會一橫線。這一橫線是eclipse() 對 @Deprecated 方法的處理愚铡。

getString2() 沒有被 @Deprecated 標注蛉签,它的顯示正常。

(02) testDate() 調(diào)用了 Date 的相關(guān)方法沥寥,而 java 已經(jīng)建議不再使用 Date 操作日期/時間碍舍。因此,在調(diào)用 Date的API 時邑雅,會產(chǎn)生警告信息片橡,途中的 warnings。

testCalendar() 調(diào)用了 Calendar 的 API 來操作日期/時間淮野,java 建議用 Calendar 取代 Date捧书。因此,操作 Calendar 不回產(chǎn)生 warning骤星。

2.2) @Inherited

@Inherited 的定義如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

說明:

  • (01) @interface -- 它的用來修飾 Inherited经瓷,意味著 Inherited 實現(xiàn)了 java.lang.annotation.Annotation 接口;即 Inherited 就是一個注解洞难。
  • (02) @Documented -- 它的作用是說明該注解能出現(xiàn)在 javadoc 中舆吮。
  • (03) @Retention(RetentionPolicy.RUNTIME) -- 它的作用是指定 Inherited 的策略是 RetentionPolicy.RUNTIME。這就意味著队贱,編譯器會將 Inherited 的信息保留在 .class 文件中色冀,并且能被虛擬機讀取。
  • (04) @Target(ElementType.ANNOTATION_TYPE) -- 它的作用是指定 Inherited 的類型是 ANNOTATION_TYPE柱嫌。這就意味著锋恬,@Inherited 只能被用來標注 "Annotation 類型"。
  • (05) @Inherited 的含義是编丘,它所標注的Annotation將具有繼承性与学。

假設(shè)彤悔,我們定義了某個 Annotaion,它的名稱是 MyAnnotation癣防,并且 MyAnnotation 被標注為 @Inherited∥锨桑現(xiàn)在,某個類 Base 使用了

MyAnnotation蕾盯,則 Base 具有了"具有了注解 MyAnnotation"幕屹;現(xiàn)在,Sub 繼承了 Base级遭,由于 MyAnnotation 是 @Inherited的(具有繼承性)望拖,所以,Sub 也 "具有了注解 MyAnnotation"挫鸽。

@Inherited 的使用示例:

InheritableSon.java

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
/**
 * 自定義的Annotation说敏。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Inheritable
{
}
@Inheritable
class InheritableFather
{
    public InheritableFather() {
        // InheritableBase是否具有 Inheritable Annotation
        System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
    }
}
/**
 * InheritableSon 類只是繼承于 InheritableFather,
 */
public class InheritableSon extends InheritableFather
{
    public InheritableSon() {
        super();    // 調(diào)用父類的構(gòu)造函數(shù)
        // InheritableSon類是否具有 Inheritable Annotation
        System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
    }
    public static void main(String[] args)
    {
        InheritableSon is = new InheritableSon();
    }
}

運行結(jié)果:

InheritableFather:true
InheritableSon:true

現(xiàn)在丢郊,我們對 InheritableSon.java 進行修改:注釋掉 "Inheritable 的 @Inherited 注解"盔沫。

InheritableSon.java

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
/**
 * 自定義的Annotation。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
@interface Inheritable
{
}
@Inheritable
class InheritableFather
{
    public InheritableFather() {
        // InheritableBase是否具有 Inheritable Annotation
        System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
    }
}
/**
 * InheritableSon 類只是繼承于 InheritableFather枫匾,
 */
public class InheritableSon extends InheritableFather
{
    public InheritableSon() {
        super();    // 調(diào)用父類的構(gòu)造函數(shù)
        // InheritableSon類是否具有 Inheritable Annotation
        System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
    }
    public static void main(String[] args)
    {
        InheritableSon is = new InheritableSon();
    }
}

運行結(jié)果:
InheritableFather:true
InheritableSon:false

對比上面的兩個結(jié)果架诞,我們發(fā)現(xiàn):當注解 Inheritable 被 @Inherited 標注時,它具有繼承性干茉。否則谴忧,沒有繼承性。

2.3) @SuppressWarnings

@SuppressWarnings 的定義如下:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

說明:

(01) @interface -- 它的用來修飾 SuppressWarnings角虫,意味著 SuppressWarnings 實現(xiàn)了 java.lang.annotation.Annotation 接口沾谓;即 SuppressWarnings 就是一個注解。

(02) @Retention(RetentionPolicy.SOURCE) -- 它的作用是指定 SuppressWarnings 的策略是 RetentionPolicy.SOURCE戳鹅。這就意味著唧领,SuppressWarnings 信息僅存在于編譯器處理期間磺芭,編譯器處理完之后 SuppressWarnings 就沒有作用了各淀。

(03) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) -- 它的作用是指定 SuppressWarnings 的類型同時包括TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE惫撰。

  • TYPE 意味著,它能標注"類模软、接口(包括注釋類型)或枚舉聲明"。
  • FIELD 意味著饮潦,它能標注"字段聲明"燃异。
  • METHOD 意味著,它能標注"方法"继蜡。
  • PARAMETER 意味著回俐,它能標注"參數(shù)"逛腿。
  • CONSTRUCTOR 意味著,它能標注"構(gòu)造方法"仅颇。
  • LOCAL_VARIABLE 意味著单默,它能標注"局部變量"。

(04) String[] value(); 意味著忘瓦,SuppressWarnings 能指定參數(shù)

(05) SuppressWarnings 的作用是搁廓,讓編譯器對"它所標注的內(nèi)容"的某些警告保持靜默。例如耕皮,"@SuppressWarnings(value={"deprecation", "unchecked"})" 表示對"它所標注的內(nèi)容"中的 "SuppressWarnings 不再建議使用警告"和"未檢查的轉(zhuǎn)換時的警告"保持沉默境蜕。示例如下:

說明:

(01) 左邊的圖中,沒有使用 @SuppressWarnings(value={"deprecation"}) , 而 Date 屬于 java 不再建議使用的類凌停。因此粱年,調(diào)用 Date 的 API 時,會產(chǎn)生警告罚拟。而右邊的途中台诗,使用了 @SuppressWarnings(value={"deprecation"})。因此赐俗,編譯器對"調(diào)用 Date 的 API 產(chǎn)生的警告"保持沉默拉队。

補充:SuppressWarnings 常用的關(guān)鍵字的表格

deprecation  -- 使用了不贊成使用的類或方法時的警告
unchecked    -- 執(zhí)行了未檢查的轉(zhuǎn)換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合保存的類型秃励。
fallthrough  -- 當 Switch 程序塊直接通往下一種情況而沒有 Break 時的警告氏仗。
path         -- 在類路徑、源文件路徑等中有不存在的路徑時的警告夺鲜。
serial       -- 當在可序列化的類上缺少 serialVersionUID 定義時的警告皆尔。
finally      -- 任何 finally 子句不能正常完成時的警告。
all          -- 關(guān)于以上所有情況的警告币励。

3.6 Annotation 的作用

Annotation 是一個輔助類慷蠕,它在 Junit、Struts食呻、Spring 等工具框架中被廣泛使用流炕。

我們在編程中經(jīng)常會使用到的 Annotation 作用有:

1)編譯檢查

Annotation 具有"讓編譯器進行編譯檢查的作用"。

例如仅胞,@SuppressWarnings, @Deprecated 和 @Override 都具有編譯檢查作用每辟。

(01) 關(guān)于 @SuppressWarnings 和 @Deprecated,已經(jīng)在"第3部分"中詳細介紹過了干旧。這里就不再舉例說明了渠欺。

(02) 若某個方法被 @Override 的標注,則意味著該方法會覆蓋父類中的同名方法椎眯。如果有方法被 @Override 標示挠将,但父類中卻沒有"被 @Override 標注"的同名方法,則編譯器會報錯内贮。示例如下:

上面是該程序在 eclipse 中的截圖产园。從中,我們可以發(fā)現(xiàn) "getString()" 函數(shù)會報錯贺归。這是因為 "getString() 被 @Override 所標注淆两,但在OverrideTest 的任何父類中都沒有定義 getString1() 函數(shù)"。

"將 getString() 上面的 @Override注釋掉"拂酣,即可解決該錯誤婶熬。

2) 在反射中使用 Annotation

在反射的 Class, Method, Field 等函數(shù)中剑勾,有許多于 Annotation 相關(guān)的接口。

一個簡單測試案例:注解 + 反射 實現(xiàn)

/*自定義注解*/
@Documented // 此標注可以生成到JavaDoc中
@Target(ElementType.METHOD)  // 此標注可以定義給方法
@Retention(RetentionPolicy.RUNTIME)  // 此標注可以存儲在class字節(jié)碼文件中赵颅,在程序中可以獲取
// 此標注的目的捂刺,用來標識測試時,被標注的方法是否運行募寨,若有此標注則檢測族展,否則不檢測
public @interface Check {
}
/*自定義類*/
public class Calc {
  @Check
  public void add(){
    System.out.println("1+1="+(1+1));
  }
  @Check
  public void subtract(){
    System.out.println("1-1="+(1-1));
  }
  @Check
  public void multi(){
    String str = null;
    str.toString();
    System.out.println("10*10="+(10*10));
  }
  @Check
  public void div(){
    System.out.println("10/0="+(10/0));
  }
  public void show(){
    System.out.println("永無bug");
  }
}
/*測試類*/
public class Test01 {
  public static void main(String[] args) throws IOException {
    Calc calc = new Calc();
    int count = 0;
    // 創(chuàng)建輸出流,用來寫入異常日志
    BufferedWriter writer = new BufferedWriter(new FileWriter("bug.txt"));
    // 獲取字節(jié)碼對象
    Class cls = calc.getClass();
    // 獲取所有的方法
    Method[]methonds = cls.getDeclaredMethods();
    // 循環(huán)遍歷方法
    for (Method methond : methonds) {
      // 檢測方法是否有被@Check標注
      if(methond.isAnnotationPresent(Check.class)){
        // 有拔鹰,則執(zhí)行
        try {
          methond.invoke(calc);
        } catch (Exception e) {
          count++;
          writer.write("[第"+count+"次出現(xiàn)異常]");
          writer.newLine();
          writer.write(methond.getName() + "方法出現(xiàn)了異常");
          writer.newLine();
          writer.write("異常的名稱" + e.getCause().getClass().getName());
          writer.newLine();
          writer.write("異常的原因" + e.getCause().getMessage());
          writer.newLine();
          writer.write("-------------------------------------");
          writer.newLine();
        }
      }
    }
    writer.write("【本次測試一共出現(xiàn)了"+count+"次異常仪缸!】");
    writer.flush();
    writer.close();
  }
}
/*bug.txt文件結(jié)果*/
[第1次出現(xiàn)異常]
div方法出現(xiàn)了異常
異常的名稱java.lang.ArithmeticException
異常的原因/ by zero
-------------------------------------
[第2次出現(xiàn)異常]
multi方法出現(xiàn)了異常
異常的名稱java.lang.NullPointerException
異常的原因null
-------------------------------------
【本次測試一共出現(xiàn)了2次異常!】

3) 根據(jù) Annotation 生成幫助文檔

通過給 Annotation 注解加上 @Documented 標簽列肢,能使該 Annotation 標簽出現(xiàn)在 javadoc 中恰画。

4) 能夠幫忙查看查看代碼

通過 @Override, @Deprecated 等,我們能很方便的了解程序的大致結(jié)構(gòu)瓷马。

另外拴还,我們也可以通過自定義 Annotation 來實現(xiàn)一些功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欧聘,一起剝皮案震驚了整個濱河市自沧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖拇厢,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晒喷,居然都是意外死亡孝偎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門凉敲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衣盾,“玉大人,你說我怎么就攤上這事爷抓∈凭觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵蓝撇,是天一觀的道長果复。 經(jīng)常有香客問我,道長渤昌,這世上最難降的妖魔是什么虽抄? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮独柑,結(jié)果婚禮上迈窟,老公的妹妹穿的比我還像新娘。我一直安慰自己忌栅,他們只是感情好车酣,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著索绪,像睡著了一般湖员。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上者春,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天破衔,我揣著相機與錄音,去河邊找鬼钱烟。 笑死晰筛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拴袭。 我是一名探鬼主播读第,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拥刻!你這毒婦竟也來了怜瞒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吴汪,沒想到半個月后惠窄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡漾橙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年杆融,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霜运。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡脾歇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淘捡,到底是詐尸還是另有隱情藕各,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布焦除,位于F島的核電站激况,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏踢京。R本人自食惡果不足惜誉碴,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓣距。 院中可真熱鬧黔帕,春花似錦、人聲如沸蹈丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逻杖。三九已至奋岁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荸百,已是汗流浹背闻伶。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留够话,地道東北人蓝翰。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像女嘲,于是被迫代替她去往敵國和親畜份。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,163評論 0 2
  • 前言欣尼,本來只是想研究一下注解的爆雹,不過發(fā)現(xiàn),要懂注解先得懂反射,別問我為什么钙态,你可以自己試試 JAVA反射 主要是指...
    justCode_閱讀 1,222評論 2 9
  • 摘要 Java Annotation是JDK5.0引入的一種注釋機制慧起。 網(wǎng)上很多關(guān)于Java Annotation...
    CarsonCao閱讀 259評論 0 0
  • 1. 學習背景 因為現(xiàn)在很多框架都幾乎用到了注解,而使用傳統(tǒng)xml進行配置的情況已經(jīng)逐漸成為了過去册倒,但是僅僅會使用...
    pr0metheus閱讀 560評論 0 0
  • 前言 現(xiàn)在在我們構(gòu)建自己或公司的項目中完慧,或多或少都會依賴幾個流行比較屌的第三方庫,比如:Butter Knife剩失、...
    戴定康閱讀 3,936評論 0 17