[TOC]
第一章:單元測試
1.1 測試分類
- 黑盒測試衷旅,不需要寫代碼捐腿,給輸入值,看程序是否能夠輸出期望的值柿顶。
- 白盒測試茄袖,需要寫代碼的。關(guān)注程序具體的執(zhí)行流程嘁锯。
- 利用java中的junit依賴環(huán)境是白盒測試的一種方式
1.2 Junit的使用步驟
- 定義一個測試類(測試用例)
- 定義測試方法:可以獨立運行
- 給方法加@Test
- 導(dǎo)入junit依賴環(huán)境
- 判定結(jié)果:
- 紅色:失敗
- 綠色:成功
- 一般我們會使用斷言操作來處理結(jié)果
-
Assert.assertEquals(期望的結(jié)果,運算的結(jié)果)
;
-
- Before和After
- @Before:
- 修飾的方法會在測試方法之前被自動執(zhí)行
- @After:
- 修飾的方法會在測試方法執(zhí)行之后自動被執(zhí)行
- @Before:
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對象的方式
-
獲取方式
-
Class.forName("全類名")
,將字節(jié)碼文件加載進內(nèi)存映胁,返回Class對象 -
類名.class
木羹,通過類名的屬性class獲取 -
對象.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)操作
- 設(shè)置值
- void set(Object obj, Object value)
- 獲取值
- get(Object obj)
- 忽略訪問權(quán)限修飾符的安全檢查
- setAccessible(true):暴力反射
- 設(shè)置值
-
代碼
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):配置文件 + 反射
-
分析
- 將需要創(chuàng)建的對象的全類名和需要執(zhí)行的方法定義在配置文件中
- 在程序中加載讀取配置文件
- 使用反射技術(shù)來加載類文件進內(nèi)存
- 創(chuàng)建對象
- 執(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)一些功能。