单元测试、注解、枚举、反射(5)JavaSE
1 JUnit單元測試
1.1 Junit的介紹
Junit屬于白盒測試(白盒測試是對軟件的過程性細(xì)節(jié)做細(xì)致的檢查。這種方法是把測試對象看做一個打開的盒子,它允許測試人員利用程序內(nèi)部的邏輯結(jié)構(gòu)及有關(guān)信息,設(shè)計(jì)或選擇測試用例,對程序的所有邏輯路徑進(jìn)行測試,通過在不同點(diǎn)檢查程序狀態(tài),確定實(shí)際狀態(tài)是否與預(yù)期的狀態(tài)一致。因此白盒測試又稱為結(jié)構(gòu)測試)。
使用JUnit的原因是因?yàn)樵跊]有使用Junit的時候,有以下缺點(diǎn):
(1)測試一定走main方法,是程序的入口,main方法的格式必須不能寫錯。
(2)要是在同一個main方法中測試的話,那么不需要測試的東西必須注釋掉。
(3)測試邏輯如果分開的話,需要定義多個測試類,麻煩。
(4)業(yè)務(wù)邏輯和測試代碼,都混淆了。
而使用Junit就可以在同一個類中定義多個方法,并且增加更多強(qiáng)大的功能。
1.2 Junit的使用
(1)一般測試和業(yè)務(wù)做一個分離,分離為不同的包,以后測試類就單獨(dú)放在這個包下。(建議起名:公司域名倒著寫+test)
(2)測試類的名字:****Test (見名知意)
(3)測試方法的定義:這個方法可以獨(dú)立運(yùn)行,不依托于main方法。(建議:名字見名知意:testAdd() ,testSub()。參數(shù):無參。返回值:void)。
(4)測試方法定義完成后,還不能直接獨(dú)立運(yùn)行,必須在方法上加一個注解:@Test,并且導(dǎo)入JUnit包后,才可以獨(dú)立運(yùn)行。
(5)判定結(jié)果是綠色:正常結(jié)果。紅色:出現(xiàn)異常。
(6)即使出現(xiàn)綠色效果,也不意味著你的測試就通過了,因?yàn)榇a中邏輯也可能出現(xiàn)問題,這種情況怎么解決呢?加入斷言。同理綠色通過,紅色不通過。
EG:做一個對兩個方法(加法和減法)的測試類
(7)如果需要對一個類中的很多方法進(jìn)行測試的過程中,這些方法都需要寫一些同樣的代碼,我們?nèi)绻恳粋€方法中都寫入這些代碼,就會造成很大的冗余。遇到這種情況就可以使用JUnit中兩個注解@Before和@After。
某一個方法中,加入@Before注解以后,那么這個方法中的功能會在測試方法執(zhí)行前先執(zhí)行。一般會在@Beforer修飾的那個方法中加入:加入一些申請資源的代碼:申請數(shù)據(jù)庫資源,申請IO資源,申請網(wǎng)絡(luò)資源等等。
加入了@After注解以后,那么這個方法中的功能會在測試方法執(zhí)行后先執(zhí)行。一般會在@After修飾的那個方法中加入:加入釋放資源的代碼:釋放數(shù)據(jù)庫資源,釋放IO資源,釋放網(wǎng)絡(luò)資源等等。
2 注解
2.1 注解的介紹
- 注解(Annotation,也稱元數(shù)據(jù))是在JDK5.0新增的。
- 它是代碼里面的特殊標(biāo)記。使用注解時要在其前面增加@符號,并把該注解當(dāng)成一個修飾符使用。用于修飾它支持的程序元素。這些標(biāo)記可以在編譯,類加載,運(yùn)行時被讀取,并執(zhí)行相應(yīng)的處理。通過使用注解,程序員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補(bǔ)充信息。代碼分析工具、開發(fā)工具和部署工具可以通過這些補(bǔ)充信息進(jìn)行驗(yàn)證或者進(jìn)行部署。
- Annotation 可用于修飾包,類,構(gòu)造器,方法,成員變量,參數(shù),局部變量,這些信息被保存在Annotation的"name=value"對中。在JavaSE中,注解的使用目的比較簡單,例如標(biāo)記過時的功能,忽略警告等。在JavaEE/ArIdroid中注解占據(jù)了更重要的角色,例如用來配置應(yīng)用程序的任何切面,代替JavaEE舊版中所遺留的繁冗代碼和XML配置等。未來的開發(fā)模式都是基于注解的,JPA(java的持久化API)是基于注解的,Spring2.5以. E都是基于注解的,Hibernate3.x以后也是基于注解的,現(xiàn)在的Struts2有一部分也是基于注解的了,注解是一種趨勢,一定程度上可以說 :框架=注解+反射+設(shè)計(jì)模式。
2.2 注解的使用
2.2.1 Junit的注解
@Test @Before @After 見1.2
2.2.2 文檔相關(guān)的注解
文檔注釋(/** … **/)允許你在程序中嵌入關(guān)于程序的信息。需要配合javadoc工具軟件來生成信息,并且顯示在HTML文件中。文檔注解(@author)一般就使用在文檔注釋之中。
javadoc工具一般識別的標(biāo)簽有
注意:
? @param @return和@exception這三個標(biāo)記都是只用于方法的。
? @param的格式要求: @param 形參名 形參類型 形參說明
? @return的格式要求: @return 返回值類型返回值說明,如果方法的返回值類型是void就不能寫
? @exception的格式要求: @exception 異常類型異常說明
? @param和@exception可以并列多個
IDEA中javadoc的使用:
在指定的目錄輸出后會有很多html文件,index.html就是我們要查看的注解文檔。
注意:防止亂碼:
2.2.3 JDK內(nèi)置的3個注解
- @Override: 限定重寫父類方法,該注解只能用于方法,只要重寫方法在父類中有問題,就會有錯誤提示。
- @Deprecated: 用于表示所修飾的元素(類,方法,構(gòu)造器,屬性等)已過時。通常是因?yàn)樗揎椀慕Y(jié)構(gòu)危險(xiǎn)或存在更好的選擇。
- @SuppressWarnnings: 抑制編譯器警告
EG:
class Person{public void eat(){System.out.println("父類eat...");} }public class Student extends Person {/*@Override的作用:限定重寫的方法,只要重寫方法有問題,就有錯誤提示。*/@Overridepublic void eat(){System.out.println("子類eat..");}/*在方法前加入@Deprecated,這個方法就會變成一個廢棄方法/過期方法/過時方法*/@Deprecatedpublic void study(){System.out.println("學(xué)習(xí)。。");} }class Test{public static void main(String[] args){Student stu = new Student();/*過期方法的用畫橫線提示*/stu.study();@SuppressWarnings("unused")int age = 10; //原本是暗色,變成亮色int num = 10;System.out.println(num); //只要被使用過就會變亮//rwatypes:泛型@SuppressWarnings({"unused","rwatypes"})ArrayList al = new ArrayList();} }2.2.4 代替配置文件的注解
在servlet3.0之前的配置:
需要專門用一個xml配置文件來設(shè)置該Servlet所對應(yīng)的訪問地址
在servlet3.0之后使用注解:替代配置文件。
只需要一個@WebServlet注解指定地址即可。
2.2.5 自定義注解
1.自定義注解使用很少,一般情況下都是用現(xiàn)成的注解。
EG:接口的定義 public @interface MyAnnotation {String[] value(); }2.發(fā)現(xiàn)定義的注解的聲明使用的關(guān)鍵字:@interface,跟接口沒有一點(diǎn)關(guān)系。
3.這value是屬性看上去是無參數(shù)方法,實(shí)際上理解為一個成員變量,一個屬性
無參數(shù)方法名字看成是成員變量的名字,無參數(shù)方法的返回值看成是成員變量的類型。這個參數(shù)叫 配置參數(shù)
4.無參數(shù)方法的類型:基本數(shù)據(jù)類型(八種),String,枚舉,注解類型,還可以是以上類型對應(yīng)的數(shù)組。
1)使用注解的話,如果你定義了配置參數(shù),就必須給配置參數(shù)進(jìn)行賦值操作:
@MyAnnotation(value={"abc","ccc"}) public class Test { }2)如果只有一個參數(shù),并且這個參數(shù)的名字為value的話,那么value=可以省略不寫。
@MyAnnotation({"abc","ccc"}) public class Test { }3)如果你給配置參數(shù)設(shè)置默認(rèn)的值了,那么使用的時候可以無需傳值:
public @interface MyAnnotation {String[] value() default {"abc","hello"}; } @MyAnnotation() public class Test { }4)注解可以疊加使用
@MyAnnotation() @MyAnnotation2 public class Test { }5)一個注解的內(nèi)部是可以不定義配置參數(shù)的。內(nèi)部沒有定義配置參數(shù)的注解叫做標(biāo)記。內(nèi)部定義配置參數(shù)的注解叫做元數(shù)據(jù)
public @interface MyAnnotation2 { }2.2.6 元注解
元注解是用于修飾其它注解的注解。
JDK5.0提供了四種元注解:Retention, Target, Documented, Inherited
1)@Retention
@Retention:用于修飾注解,用于指定被修飾注解的生命周期,@Rentention包含一個RetentionPolicy枚舉類型的成員變量,使用@Rentention時必須為該value成員變量指定值。
- RetentionPolicy.SOURCE: 只在源文件中有效(即源文件保留),編譯器直接丟棄這種策略的注釋,在.class文件中不會保留注解信息(具體表現(xiàn),反編譯查看字節(jié)碼文件:發(fā)現(xiàn)字節(jié)碼文件中沒有MyAnnotation這個注解。)
- RetentionPolicy.CLASS: 在class文件中有效(即class保留),保留在.class文件中,但是當(dāng)運(yùn)行Java程序時,他就不會繼續(xù)加載了,不會保留在內(nèi)存中,JVM不會保留注解。如果注解沒有加Retention元注解,那么相當(dāng)于默認(rèn)的注解就是這種狀態(tài)。(反編譯看字節(jié)碼文件,字節(jié)碼文件中帶有MyAnnotation注解。)
- RetentionPolicy.RUNTIME:在運(yùn)行時有效(即運(yùn)行時保留),當(dāng)運(yùn)行 Java程序時,JVM會保留注釋,加載在內(nèi)存中了,那么程序可以通過反射獲取該注釋。
2)@Target
用于修飾注解的注解,用于指定被修飾的注解能用于修飾哪些程序元素。@Target也包含一個名為value的成員變量。
ElementType變量是一個枚舉數(shù)組,里面包含數(shù)據(jù)類型,方法,類等。如果要使用@Target標(biāo)記過的注解@test,那么@test首先就要指定@Target指定的范圍如@Target({TYPE,METHOD}),然后再使用@test注解后就只能再指定類型上使用這個注解
public enum ElementType {TYPE,//類FIELD,//屬性METHOD,//方法PARAMETER,//參數(shù)CONSTRUCTOR,//構(gòu)造器LOCAL_VARIABLE,ANNOTATION_TYPE,//注解類型PACKAGE,//包TYPE_PARAMETER,TYPE_USE } EG: @Target({TYPE}) public @interface MyAnnotation { } @MyAnnotation() //因?yàn)?#64;Target指定了TYPE,所以可以作用再類Class上 public class Test {@MyAnnotation() //不能使用int a;@MyAnnotation() //不能使用public static void testOne(){} }3)@Documented
用于指定被該元注解修飾的注解類將被javadoc工具提取成文檔。默認(rèn)情況下,javadoc是 不包括注解的,但是加上了這個注解生成的文檔中就會帶著注解了。
4)@Inherited
被它修飾的Annotation將具有繼承性。如果某個類使用了被@Inherited修飾的Annotation,則其子類將自動具有該注解。
3 枚舉
在java中,類的對象是有限個,確定的。這個類我們可以定義為枚舉類。
舉例:
星期:一二三四五六日
性別:男女
季節(jié):春夏秋冬
構(gòu)造枚舉類的特點(diǎn):
1.屬性是私有的,而且不可改變
2.構(gòu)造器也是私有的,不可以隨意構(gòu)造枚舉類
3.在類中定義枚舉,提供給外界的構(gòu)造方法是有限的。
3.2 枚舉的構(gòu)造
在JDK1.5之前,得自定義枚舉類。EG: public class Season {//屬性是私有的,而且不可改變private final String seasonName;private final String seasonDesc;//構(gòu)造器也是私有的,不可以隨意構(gòu)造Season類private Season(String seasonName,String seasonDesc){this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//枚舉,提供給外界的構(gòu)造方法是有限的。public static final Season SPRING = new Season("春天","春暖花開");public static final Season SUMMER = new Season("夏天","烈日炎炎");public static final Season AUTUMN = new Season("秋天","碩果累累");public static final Season WINTER = new Season("冬天","冰天雪地");//額外:public String getSeasonName(){return this.seasonName;}public String getSeasonDesc(){return this.seasonDesc;}@Overridepublic String toString() {return "Season{" +"seasonName='" + seasonName + '\'' +", seasonDesc='" + seasonDesc + '\'' +'}';} } class TestSeason {//這是一個main方法,是程序的入口:public static void main(String[] args) {Season summer = Season.SUMMER;System.out.println(summer/*.toString()*/);System.out.println(summer.getSeasonName());} } 在JDK1.5之后,枚舉類enum出現(xiàn),可以簡化很多,對應(yīng)自定義的修改。EG: public enum Season {//定義枚舉,必須放在最前面//提供枚舉類的有限的 確定的對象:--->enum枚舉類要求對象(常量)必須放在最開始位置//多個對象之間用逗號,進(jìn)行連接,最后一個對象后面用分號;結(jié)束SPRING ("春天","春暖花開"),SUMMER ("夏天","烈日炎炎"),AUTUMN("秋天","碩果累累"),WINTER("冬天","冰天雪地");//屬性是私有的,而且不可改變private final String seasonName;private final String seasonDesc;//構(gòu)造器也是私有的,不可以隨意構(gòu)造Season類private Season(String seasonName,String seasonDesc){this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//額外:public String getSeasonName(){return this.seasonName;}public String getSeasonDesc(){return this.seasonDesc;}@Overridepublic String toString() {return "Season{" +"seasonName='" + seasonName + '\'' +", seasonDesc='" + seasonDesc + '\'' +'}';}3.2 枚舉的使用以及常用的方法:
public class TestSeason {public static void main(String[] args) {Season winter = Season.WINTER;System.out.println(winter);//enum關(guān)鍵字對應(yīng)的枚舉類的上層父類是 :java.lang.Enum//但是我們自定義的枚舉類的上層父類:ObjectSystem.out.println(Season.class.getSuperclass().getName());//java.lang.Enum} } public class TestSeason {public static void main(String[] args) {//enum關(guān)鍵字對應(yīng)的枚舉類的上層父類是 :java.lang.Enum//但是我們自定義的枚舉類的上層父類:ObjectSeason winter = Season.WINTER;System.out.println(winter);System.out.println(Season.class.getSuperclass().getName());//java.lang.Enum//用enum關(guān)鍵字創(chuàng)建的Season枚舉類上面的父類是:java.lang.Enum,常用方法子類Season可以直接拿過來使用://toString();--->獲取對象的名字Season autumn = Season.AUTUMN;System.out.println(autumn/*.toString()*/);//AUTUMNSystem.out.println("--------------------");//values:返回枚舉類對象的數(shù)組Season[] values = Season.values();for(Season s:values){System.out.println(s/*.toString()*/);}System.out.println("--------------------");//valueOf:通過對象名字獲取這個枚舉對象//注意:對象的名字必須傳正確,否則拋出異常Season autumn1 = Season.valueOf("AUTUMN");System.out.println(autumn1);} }注意:
1.為什么在很多源碼中看到別人定義的枚舉類形態(tài)特別簡單:因?yàn)檫@個枚舉類底層沒有屬性,構(gòu)造器,toString,get方法都刪掉不寫了,然后案例應(yīng)該寫為:SPRING() 現(xiàn)在連括號()也可以省略,就變成SPRING。看到的形態(tài)就剩:常量名(對象名)
3.3 枚舉的應(yīng)用
定義一個Person類,類中又一個屬性是性別Sex,那么這個sex屬性由于只有男和女兩種選擇而不能隨意,那么就可以用枚舉類來限定Person的set方法。
class Person {//屬性:private int age;private String name;private Gender sex;public Gender getSex() {return sex;}public void setSex(Gender sex) {this.sex = sex;} } enum Gender {男,女; } public class Test {//這是一個main方法,是程序的入口:public static void main(String[] args) {Person p = new Person();p.setAge(19);p.setName("lili");p.setSex(Gender.男);//傳入枚舉類Gender的對象:-->在入口處對參數(shù)進(jìn)行了限制System.out.println(p);//還可以通過枚舉結(jié)合switch處理:Gender sex = Gender.男;//switch后面的()中可以傳入枚舉類型//switch后面的():int,short,byte,char,String ,枚舉switch (sex){case 女:System.out.println("是個女孩");break;case 男:System.out.println("是個男孩");break;}} }4 反射
4.1 為什么要用反射
案例引入:美團(tuán)外賣付款實(shí)現(xiàn)(要么用微信支付 要么用支付寶支付 )
//接口的制定方:美團(tuán)外賣 public interface Mtwm {//在線支付功能:void payOnline(); } class WeChat implements Mtwm{@Overridepublic void payOnline() {//具體實(shí)現(xiàn)微信支付的功能:System.out.println("我已經(jīng)點(diǎn)了外賣,正在使用微信支付");} } class AliPay implements Mtwm {@Overridepublic void payOnline() {//具體的支付寶支付:System.out.println("我已經(jīng)點(diǎn)了外賣,我正在使用支付寶進(jìn)行支付");} }最一般的實(shí)現(xiàn)
public class Test {public static void main(String[] args) {//定義一個字符串,用來模擬前臺的支付方式:String str = "微信";if("微信".equals(str)){//str.equals("微信")---?避免空指針異常//微信支付://new WeChat().payOnline();pay(new WeChat());}if("支付寶".equals(str)){//支付寶支付://new AliPay().payOnline();pay(new AliPay());}if("招商銀行".equals(str)){pay(new BankCard());}}//微信支付public static void pay(WeChat wc){wc.payOnline();}//支付寶支付public static void pay(AliPay ap){ap.payOnline();}//招商銀行支付public static void pay(BankCard bc){bc.payOnline();} }發(fā)現(xiàn)要寫的方法很多,代碼冗余。這個時候可以用多態(tài)來簡化
public class Test {public static void main(String[] args) {//定義一個字符串,用來模擬前臺的支付方式:String str = "微信";if("微信".equals(str)){//str.equals("微信")---?避免空指針異常//微信支付:pay(new WeChat());}if("支付寶".equals(str)){//支付寶支付:pay(new AliPay());}if("招商銀行".equals(str)){pay(new BankCard());}}//方法形參是接口,具體傳入的是接口的實(shí)現(xiàn)類的對象--->多態(tài)的一種形式public static void pay(Mtwm m){m.payOnline();} }多態(tài)確實(shí)可以提高代碼的擴(kuò)展性,但是:擴(kuò)展性沒有達(dá)到最好。
怎么沒有達(dá)到最好:上面的if分支,還是需要手動的刪除或者添加,如果假如同時來了50個付款方式,那就要加50個if。
解決辦法:反射機(jī)制
利用反射實(shí)現(xiàn)上述功能:
上述代碼不管又多少家銀行來了,這個類中的邏輯代碼都不用變了,要變的只有一個字符串接口。
4.2 反射的概念
- JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機(jī)制。
- 在編譯后產(chǎn)生字節(jié)碼文件的時候,類加載器子系統(tǒng)通過二進(jìn)制字節(jié)流,負(fù)責(zé)從文件系統(tǒng)加載class文件。在執(zhí)行程序(java.exe)時候,將字節(jié)碼class文件讀入JVM中(即類的加載)。這個時候JVM會在內(nèi)存中對應(yīng)創(chuàng)建一個java.lang.Class對象,這個對象也會被放入字節(jié)碼信息中,這個Class對象,就對應(yīng)加載那個字節(jié)碼信息,這個對象將被作為程序訪問方法區(qū)中的這個類的各種數(shù)據(jù)的外部接口。
所以:我們可以通過這個對象看到類的結(jié)構(gòu),這個對象就好像是一面鏡子,透過鏡子看到類的各種信息,我們形象的稱之為反射。 - 這種“看透”class的能力(the ability of the program to examine itself)被稱為introspection(內(nèi)省、內(nèi)觀、反省)。Reflection和introspection是常被并提的兩個術(shù)語。
說明:在運(yùn)行期間,如果我們要產(chǎn)生某個類的對象,Java虛擬機(jī)(JVM)會檢查該類型的Class對象是否已被加載。
如果沒有被加載,JVM會根據(jù)類的名稱找到.class文件并加載它。一旦某個類型的Class對象已被加載到內(nèi)存,就可以用它來產(chǎn)生該類型的所有對象。
補(bǔ)充:動態(tài)語膏vs靜態(tài)語言
1、動態(tài)語言
是一類在運(yùn)行時可以改變其結(jié)構(gòu)的語言:例如新的函數(shù)、對象、甚至代碼可以被引進(jìn),已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化。通俗點(diǎn)說就是在運(yùn)行時代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)。
主要動態(tài)語言: Object-C、 C#、JavaScript、 PHP、 Python、 Erlang 。
2、靜態(tài)語言
與動態(tài)語言相對應(yīng)的,運(yùn)行時結(jié)構(gòu)不可變的語言就是靜態(tài)語言。如Java、C、C++。
所以Java不是動態(tài)語言,但Java可以稱之為“準(zhǔn)動態(tài)語言”。即Java有一定的動態(tài)性,我們可以利用反射機(jī)制、字節(jié)碼操作獲得類似動態(tài)語言的特性。Java的動態(tài)性讓編程的時候更加靈活!
4.3 獲取字節(jié)碼信息的4種方式
//舉例要用的類Person class Person {//屬性private int age;public String name;//方法private void eat(){System.out.println("Person---eat");}public void sleep(){System.out.println("Person---sleep");} } public class Test {public static void main(String[] args) throws ClassNotFoundException {//案例:以Person的字節(jié)碼信息為案例//方式1:通過getClass()方法獲取Person p = new Person();Class c1 = p.getClass();System.out.println(c1);//方式2:通過內(nèi)置class屬性:Class c2 = Person.class;System.out.println(c2);System.out.println(c1==c2);//注意:方式1和方式2不常用,因?yàn)榧热欢家呀?jīng)又Person類了,就可以直接用Person類的對象來使用里面的屬性和方法,沒有必要多此一舉。//方式3:用的最多:調(diào)用Class類提供的靜態(tài)方法forNameClass c3 = Class.forName("com.zhaoss.test02.Person");//方式4:利用類的加載器(了解技能點(diǎn))ClassLoader loader = Test.class.getClassLoader();Class c4 = loader.loadClass("com.zhaoss.test02.Person");} }4.4 Class類的具體的實(shí)例
- Calss類的具體實(shí)例可以有:外部類,內(nèi)部類、接口、注解、數(shù)組、基本數(shù)據(jù)類型、void。
4.5 通過Class類來獲取運(yùn)行時類的完整對象
舉例要用的類:
//作為一個父類 public class Person implements Serializable {//屬性private int age;public String name;//方法private void eat(){System.out.println("Person---eat");}public void sleep(){System.out.println("Person---sleep");} } //Student作為子類 @MyAnnotation(value="hello") public class Student extends Person implements MyInterface{//屬性:private int sno;//學(xué)號double height;//身高protected double weight;//體重public double score;//成績//方法:@MyAnnotation(value="himethod")public String showInfo(){return "我是一名三好學(xué)生";}public String showInfo(int a,int b){return "重載方法====我是一名三好學(xué)生";}private void work(){System.out.println("我以后會找工作--》成為碼農(nóng) 程序員 程序猿 程序媛");}void happy(){System.out.println("做人最重要的就是開心每一天");}protected int getSno(){return sno;}//構(gòu)造器public Student(){System.out.println("空參構(gòu)造器");}private Student(int sno){this.sno = sno;}Student(int sno,double weight){this.sno = sno;this.weight = weight;}protected Student(int sno,double height,double weight){this.sno = sno;}@Override@MyAnnotation(value="hellomyMethod")public void myMethod() {System.out.println("我重寫了myMethod方法。。");}@Overridepublic String toString() {return "Student{" +"sno=" + sno +", height=" + height +", weight=" + weight +", score=" + score +'}';} }/* @Target:定義當(dāng)前注解能夠修飾程序中的哪些元素 @Retention:定義注解的聲明周期*/ @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation {String value();//屬性 }public interface MyInterface {//自定義的接口//隨便定義一個抽象方法:void myMethod(); }1)獲取構(gòu)造器和創(chuàng)建對象
public class Test {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//獲取字節(jié)碼信息:Class cls = Student.class;//通過字節(jié)碼信息可以獲取構(gòu)造器://getConstructors只能獲取當(dāng)前運(yùn)行時類的被public修飾的構(gòu)造器Constructor[] c1 = cls.getConstructors();for(Constructor c:c1){System.out.println(c);}System.out.println("-------------------");//getDeclaredConstructors:獲取運(yùn)行時類的全部修飾符的構(gòu)造器Constructor[] c2 = cls.getDeclaredConstructors();for(Constructor c:c2){System.out.println(c);}System.out.println("-------------------");//獲取指定的構(gòu)造器://得到空構(gòu)造器Constructor con1 = cls.getConstructor();System.out.println(con1);//得到兩個參數(shù)的有參構(gòu)造器:Constructor con2 = cls.getConstructor(double.class, double.class);System.out.println(con2);//得到一個參數(shù)的有參構(gòu)造器:并且是private修飾的Constructor con3 = cls.getDeclaredConstructor(int.class);System.out.println(con3);//有了構(gòu)造器以后我就可以創(chuàng)建對象:Object o1 = con1.newInstance();System.out.println(o1);Object o2 = con2.newInstance(180.5, 170.6);System.out.println(o2);} }2)獲取屬性和對屬性進(jìn)行賦值
public class Test {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {//獲取運(yùn)行時類的字節(jié)碼信息:Class cls = Student.class;//獲取屬性://getFields:獲取運(yùn)行時類和父類中被public修飾的屬性Field[] fields = cls.getFields();for(Field f:fields){System.out.println(f);}System.out.println("---------------------");//getDeclaredFields:獲取運(yùn)行時類中的所有屬性Field[] declaredFields = cls.getDeclaredFields();for(Field f:declaredFields){System.out.println(f);}System.out.println("---------------------");//獲取指定的屬性:Field score = cls.getField("score");System.out.println(score);Field sno = cls.getDeclaredField("sno");System.out.println(sno);System.out.println("---------------------");//屬性的具體結(jié)構(gòu)://獲取修飾符,返回的是一個int類型的數(shù),打開modifier源碼后可以看到數(shù)字對應(yīng)的字符串,如private就是2,static是8,那么修飾為private static就是10。然后它的ToString方法可以將這個數(shù)字轉(zhuǎn)化為對應(yīng)修飾符,所以我們可以利用這個方法獲得對應(yīng)屬性的修飾符/*int modifiers = sno.getModifiers();System.out.println(modifiers);System.out.println(Modifier.toString(modifiers));*/System.out.println(Modifier.toString(sno.getModifiers()));//獲取屬性的數(shù)據(jù)類型:Class clazz = sno.getType();System.out.println(clazz.getName());//獲取屬性的名字:String name = sno.getName();System.out.println(name);System.out.println("-------------------------------");//給屬性賦值:(給屬性設(shè)置值,必須要有對象)Field sco = cls.getField("score");Object obj = cls.newInstance();sco.set(obj,98);//給obj這個對象的score屬性設(shè)置具體的值,這個值為98System.out.println(obj);} }3)獲取方法和調(diào)用方法
public class Test {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {//獲取字節(jié)碼信息:Class cls = Student.class;//獲取方法://getMethods:獲取運(yùn)行時類的方法還有所有父類中的方法(被public修飾)Method[] methods = cls.getMethods();for(Method m:methods){System.out.println(m);}System.out.println("-----------------------");//getDeclaredMethods:獲取運(yùn)行時類中的所有方法:Method[] declaredMethods = cls.getDeclaredMethods();for(Method m:declaredMethods){System.out.println(m);}System.out.println("-----------------------");//獲取指定的方法:Method showInfo1 = cls.getMethod("showInfo");System.out.println(showInfo1);Method showInfo2 = cls.getMethod("showInfo", int.class, int.class);System.out.println(showInfo2);Method work = cls.getDeclaredMethod("work",int.class);System.out.println(work);System.out.println("-----------------------");//獲取方法的具體結(jié)構(gòu):/*@注解修飾符 返回值類型 方法名(參數(shù)列表) throws XXXXX{}*///名字:System.out.println(work.getName());//修飾符:int modifiers = work.getModifiers();System.out.println(Modifier.toString(modifiers));//返回值:System.out.println(work.getReturnType());//參數(shù)列表:Class[] parameterTypes = work.getParameterTypes();for(Class c:parameterTypes){System.out.println(c);}//獲取注解:Method myMethod = cls.getMethod("myMethod");Annotation[] annotations = myMethod.getAnnotations();for(Annotation a:annotations){System.out.println(a);}//獲取異常:Class[] exceptionTypes = myMethod.getExceptionTypes();for(Class c:exceptionTypes){System.out.println(c);}//調(diào)用方法:Object o = cls.newInstance();myMethod.invoke(o);//調(diào)用o對象的mymethod方法System.out.println(showInfo2.invoke(o,12,45));;} }4)獲取類的接口,所在包,注解
public class Test {public static void main(String[] args) {//獲取字節(jié)碼信息:Class cls = Student.class;//獲取運(yùn)行時類的接口:Class[] interfaces = cls.getInterfaces();for(Class c:interfaces){System.out.println(c);}//得到父類的接口://先得到父類的字節(jié)碼信息:Class superclass = cls.getSuperclass();//得到接口:Class[] interfaces1 = superclass.getInterfaces();for(Class c:interfaces1){System.out.println(c);}//獲取運(yùn)行時類所在的包:Package aPackage = cls.getPackage();System.out.println(aPackage);System.out.println(aPackage.getName());//獲取運(yùn)行類的注解:Annotation[] annotations = cls.getAnnotations();for(Annotation a:annotations){System.out.println(a);}} }4.6 反射面試題
- 創(chuàng)建Person的對象,以后用new Person()創(chuàng)建,還是用反射創(chuàng)建?
如果某個對象,只有在程序運(yùn)行時才可以動態(tài)的知道具體時哪一個對象時,這個時候就用反射。 - 反射是否破壞了面向?qū)ο蟮姆庋b性?
封裝性和反射各自解決的是不同的問題,事實(shí)上,反射確實(shí)是具有可以訪問private修飾的屬性的,但是這僅僅是提供了這種方法。不建議使用。舉個例子:在廁所上有男生女生的標(biāo)識,但是如果真有變態(tài),那也就是可以進(jìn)入與自己性別不符的門。但不能因?yàn)橛凶儜B(tài)的存在,直接不在廁所上貼男生女生的標(biāo)識。
總結(jié)
以上是生活随笔為你收集整理的单元测试、注解、枚举、反射(5)JavaSE的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中的Base64详解
- 下一篇: Java爬虫爬取360doc个人图书馆