1 ** 2 * 1、枚舉類的使用 3 * 1.枚舉類的理解:類的對象只有有限個,肯定的。咱們稱此類爲枚舉類 4 * 2.當須要定義一組常量時,強烈建議使用枚舉類 5 * 3.若是枚舉類中只有一個對象,則能夠做爲單例模式的實現方式。 6 * 7 * 2、如何定義枚舉類 8 * 方式一:jdk5.0以前,自定義枚舉類 9 * 方式二:jdk5.0,可使用enum關鍵字定義枚舉類 10 * 11 * 3、Enum類中的經常使用方法: 12 * values()方法:返回枚舉類型的對象數組。該方法能夠很方便地遍歷全部的枚舉值。 13 * valueOf(String str):能夠把一個字符串轉爲對應的枚舉類對象。要求字符串必須是枚舉類對象的「名字」。如不是,會有運行時異常:IllegalArgumentException。 14 * toString():返回當前枚舉類對象常量的名稱 15 * 16 * 4、使用enum關鍵字定義的枚舉類實現接口的狀況 17 * 狀況一:實現接口,在enum類中實現抽象方法 18 * 狀況二:讓枚舉類的對象分別實現接口中的抽象方法 19 * 20 * @author shkstart 21 * @create 2019 上午 10:17 22 */ 23 public class SeasonTest { 24 25 public static void main(String[] args) { 26 Season spring = Season.SPRING; 27 System.out.println(spring); 28 29 } 30 31 } 32 //自定義枚舉類 33 class Season{ 34 //1.聲明Season對象的屬性:private final修飾 35 private final String seasonName; 36 private final String seasonDesc; 37 38 //2.私有化類的構造器,並給對象屬性賦值 39 private Season(String seasonName,String seasonDesc){ 40 this.seasonName = seasonName; 41 this.seasonDesc = seasonDesc; 42 } 43 44 //3.提供當前枚舉類的多個對象:public static final的 45 public static final Season SPRING = new Season("春天","春暖花開"); 46 public static final Season SUMMER = new Season("夏天","夏日炎炎"); 47 public static final Season AUTUMN = new Season("秋天","秋高氣爽"); 48 public static final Season WINTER = new Season("冬天","冰天雪地"); 49 50 //4.其餘訴求1:獲取枚舉類對象的屬性 51 public String getSeasonName() { 52 return seasonName; 53 } 54 55 public String getSeasonDesc() { 56 return seasonDesc; 57 } 58 //4.其餘訴求1:提供toString() 59 @Override 60 public String toString() { 61 return "Season{" + 62 "seasonName='" + seasonName + '\'' + 63 ", seasonDesc='" + seasonDesc + '\'' + 64 '}'; 65 } 66 } 67 package com.atguigu.java; 68 69 /** 70 * 使用enum關鍵字定義枚舉類 71 * 說明:定義的枚舉類默認繼承於java.lang.Enum類 72 * 73 * @author shkstart 74 * @create 2019 上午 10:35 75 */ 76 public class SeasonTest1 { 77 public static void main(String[] args) { 78 Season1 summer = Season1.SUMMER; 79 //toString():返回枚舉類對象的名稱 80 System.out.println(summer.toString()); 81 82 // System.out.println(Season1.class.getSuperclass()); 83 System.out.println("****************"); 84 //values():返回全部的枚舉類對象構成的數組 85 Season1[] values = Season1.values(); 86 for(int i = 0;i < values.length;i++){ 87 System.out.println(values[i]); 88 values[i].show(); 89 } 90 System.out.println("****************"); 91 Thread.State[] values1 = Thread.State.values(); 92 for (int i = 0; i < values1.length; i++) { 93 System.out.println(values1[i]); 94 } 95 96 //valueOf(String objName):返回枚舉類中對象名是objName的對象。 97 Season1 winter = Season1.valueOf("WINTER"); 98 //若是沒有objName的枚舉類對象,則拋異常:IllegalArgumentException 99 // Season1 winter = Season1.valueOf("WINTER1"); 100 String seasonDesc = winter.getSeasonDesc(); 101 String seasonName = winter.getSeasonName(); 102 System.out.println(seasonDesc +"----->" + seasonName); 103 System.out.println(winter); 104 winter.show(); 105 } 106 } 107 108 interface Info{ 109 void show(); 110 } 111 112 //使用enum關鍵字枚舉類 113 enum Season1 implements Info{ 114 //1.提供當前枚舉類的對象,多個對象之間用","隔開,末尾對象";"結束 115 SPRING("春天","春暖花開"){ 116 @Override 117 public void show() { 118 System.out.println("春天在哪裏?"); 119 } 120 }, 121 SUMMER("夏天","夏日炎炎"){ 122 @Override 123 public void show() { 124 System.out.println("寧夏"); 125 } 126 }, 127 AUTUMN("秋天","秋高氣爽"){ 128 @Override 129 public void show() { 130 System.out.println("秋天不回來"); 131 } 132 }, 133 WINTER("冬天","冰天雪地"){ 134 @Override 135 public void show() { 136 System.out.println("大約在冬季"); 137 } 138 }; 139 140 //2.聲明Season對象的屬性:private final修飾 141 private final String seasonName; 142 private final String seasonDesc; 143 144 //2.私有化類的構造器,並給對象屬性賦值 145 146 private Season1(String seasonName,String seasonDesc){ 147 this.seasonName = seasonName; 148 this.seasonDesc = seasonDesc; 149 } 150 151 //4.其餘訴求1:獲取枚舉類對象的屬性 152 public String getSeasonName() { 153 return seasonName; 154 } 155 156 public String getSeasonDesc() { 157 return seasonDesc; 158 } 159 // //4.其餘訴求1:提供toString() 160 // 161 // @Override 162 // public String toString() { 163 // return "Season1{" + 164 // "seasonName='" + seasonName + '\'' + 165 // ", seasonDesc='" + seasonDesc + '\'' + 166 // '}'; 167 // } 168 169 170 // @Override 171 // public void show() { 172 // System.out.println("這是一個季節"); 173 // } 174 }
什麼是註解?嚴謹的來講,註解提供了一種安全的相似註釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。爲程序的元素(類、方法、成員變量)加上更直觀的說明,這些說明信息是與程序的業務邏輯無關,而且供指定的工具或框架使用。Annontation像一種修飾符同樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。 Java 註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。註解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的做用。註解包含在 java.lang.annotation 包中。java
可以讀懂別人寫的代碼(尤爲是框架相關的代碼);web
實現替代配置文件的功能。好比可能本來須要不少配置文件以及不少邏輯才能實現的內容,若是使用合理的註解,就可使用一個或多個註解來實現相同的功能。這樣就使得代碼更加清晰和整潔;spring
編譯時進行格式檢查數據庫
如 @Override 註解放在方法前,若是該方法不是覆蓋了某個超類方法,編譯的時候編譯器就能檢查出來。設計模式
裝逼。數組
作技術的怎麼能夠沒有一點用技術吹牛逼的心理呢?若是會在合適的地方剛好的使用註解或者自定義註解的話,老闆確定會雙手送你 666 的。固然筆者如今只是初學而已,距離用技術吹牛逼的道路還遠。緩存
元註解是能夠註解到註解上的註解,或者說元註解是一種基本註解,可是它可以應用到其它的註解上面。或者能夠理解爲:元註解也是一張標籤,可是它是一張特殊的標籤,它的做用和目的就是給其餘普通的標籤進行解釋說明的。安全
基本的元標籤有 @Retention, @Documented, @Target, @Inherited 四種(後來到了 Java 8 又加入了 @Repeatable)。app
@Retention 定義了該註解的生命週期。當 @Retention 應用到一個註解上的時候,做用就是說明這個註解的存活時間。框架
RetentionPolicy.SOURCE
: 註解只在源碼階段保留,在編譯器完整編譯以後,它將被丟棄忽視;
例:@Override, @SuppressWarnings
RetentionPolicy.CLASS: 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中;
RetentionPolicy.RUNTIME: 註解能夠保留到程序運行的時候,它會被加載進入到 JVM 中,因此在程序運行時能夠獲取到它們;筆者接觸到大部分的註解都是 RUNTIME 的生命週期。
以 SpringMVC 中的 @Service 的源碼爲例:
1 package org.springframework.stereotype; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 @Target({ElementType.TYPE}) 10 @Retention(RetentionPolicy.RUNTIME) 11 @Documented 12 @Component 13 public @interface Service { 14 String value() default ""; 15 }
這裏 @Service 擁有 @Retention(RetentionPolicy.RUNTIME) 註解,因此在程序運行時能夠捕獲到它們。
@Target 表示該註解用於什麼地方,能夠理解爲:當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。可使用的 ElementType 參數:
ElementType.CONSTRUCTOR: 對構造方法進行註解;
ElementType.ANNOTATION_TYPE: 對註解進行註解;
ElementType.FIELD: 對屬性、成員變量、成員對象(包括 enum 實例)進行註解;
ElementType.LOCAL_VARIABLE: 對局部變量進行註解;
ElementType.METHOD: 對方法進行註解;
ElementType.PACKAGE: 對包進行註解;
ElementType.PARAMETER: 對描述參數進行註解;
ElementType.TYPE: 對類、接口、枚舉進行註解;
如上面的 @Service 所示,@Service 的 @Target 註解值爲 ElementType.TYPE,即 @Service 只能用於修飾類。
@Documented 是一個簡單的標記註解,表示是否將註解信息添加在 Java 文檔,即 Javadoc 中。
Inherited 是指繼承,@Inherited 定義了一個註釋與子類的關係。若是一個超類帶有 @Inherited 註解,那麼對於該超類,它的子類若是沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public class A {} public class B extends A {}
註解 Test 被 @Inherited 修飾,以後類 A 被 Test 註解,類 B 繼承 A,類 B 也擁有 Test 這個註解。能夠這樣理解: 老子很是有錢,因此人們給他貼了一張標籤叫作富豪。 老子的兒子長大後,只要沒有和老子斷絕父子關係,雖然別人沒有給他貼標籤,可是他天然也是富豪。 老子的孫子長大了,天然也是富豪。 這就是人們口中戲稱的富一代,富二代,富三代。雖然叫法不一樣,好像好多個標籤,但其實事情的本質也就是他們有一張共同的標籤,也就是老子身上的那張富豪的標籤。
@Repeatable 是 Java 8 中加入的,是指可重複的意思。一般使用 @Repeatable 的時候指註解的值能夠同時取多個。
1 @interface Persons { 2 Person[] value(); 3 } 4 5 @Repeatable(Persons.class) 6 @interface Person { 7 String role default ""; 8 } 9 10 @Person(role="artist") 11 @Person(role="coder") 12 @Person(role="PM") 13 public class SuperMan { 14 ... 15 }
上面的代碼經過 @Repeatable 定義了 Person,而 @Repeatable 後面括號的類至關於一個容器註解。容器註解就是用來存放其它註解的地方,它自己也是一個註解。
1 @Documented 2 @Retention(RetentionPolicy.RUNTIME) 3 @Target(ElementType.ANNOTATION_TYPE) 4 public @interface Repeatable { 5 Class<? extends Annotation> value(); 6 }
上面是 @Repeatable 的源碼。按照規定,若是使前面的 Persons 裏面能夠重複調用某個註解,則 Persons 必須有一個 value 的屬性,且屬性類型必須爲被 @Repeatable 註解的 Person。
註解的屬性也叫作成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以無形參的方法形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。如下面的例程爲例:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Coder { 4 int id(); 5 String name(); 6 String language(); 7 String company(); 8 }
上面假設定義了一個名爲 @Coder 的註解,該註解有 id, name, language, company 三個屬性。使用的時候,咱們應該對其賦值。賦值的方式相似於 key="value" 的方式進行,屬性之間用 "," 隔開:
1 @Coder(id = 10086, name = "GRQ", language = "JAVA", company = "cetc") 2 public class coderGRQ() { 3 4 }
此外,註解能夠有默認值,須要用 default 關鍵字指定。例如上例:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Coder { 4 public int id() default -1; 5 public String name() default "GRQ"; 6 public String language() default "C++"; 7 public String company() default "China_Company"; 8 }
若是:
@Coder public class coderXX {}
因爲在 @Coder 註解中設置了默認值,因此就不須要再 @Coder 後面的括號裏進行賦值了。
此外,若是註解內只有一個名爲 value 的屬性時,應用該屬性時能夠將值直接寫到括號內,不用寫 value = "..."。例如:
public @interface language { String value(); }
那麼下面兩種聲明是相同的:
// 第一種聲明 @language("JAVA") int coderA; // 第二種聲明 @language(value = "JAVA") int coderA;
Java 中自帶且經常使用的幾種註解有 @Override, @Deprecated, @SuppresWarninngs, @SafeVarargs。
@Override 是一個標記類型註解,用於提示子類要複寫父類中被 @Override 修飾的方法,它說明了被標註的方法重載了父類的方法,起到了斷言的做用。若是咱們使用了這種註解在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。
@Deprecated 也是一個標記類型註解,用於標記過期的元素。好比若是開發人員正在調用一個過期的方法、類或成員變量時,能夠用該註解進行標註。
@SuppressWarnings 並非一個標記類型註解,它能夠阻止警告的提示。它有一個類型爲 String[] 的成員,其值爲被禁止的警告名。
@SafeVarargs 是一個參數安全類型註解。它的目的是提醒開發人員,不要用參數作一些不安全的操做。它的存在會阻止編譯器產生 unchecked 的警告。例如對於可變長度參數,若是和泛型一塊兒使用,會產生比較多的編譯器警告。以下面的方法:
public static <T> T useVarargs(T... args) { return args.length > 0 ? args[0] : null; }
若是參數傳遞的是不可具體化的類型(相似於 List<String> 的泛型類型),每調用一次該方法,都會產生警告信息。若是但願禁止這個警告信息,可使用 @SuppressWarnings("unchecked") 註解進行聲明。同時在 Java 7 版本以後的 @SafeVarargs 註解針對 "unchecked" 警告進行了屏蔽,咱們也能夠用 @SafeVarargs 得到 @SuppressWarnings("unchecked") 一樣的效果。
自定義註解類編寫的規則:
1. 註解類型定義爲 @interface,全部的註解會自動繼承 java.lang.Annotation 這一接口,並且不能再去繼承其餘的類或接口; 2. 參數成員只能用 public 或 default 兩個關鍵字修飾; 3. 參數成員只能用基本類型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等數據類型,以及這些類型的數組; 4. 要獲取類方法和字段的註解信息,必須經過 Java 的反射技術; 5. 註解也能夠不定義成員變量,但這樣的註解沒有什麼卵用; 6. 自定義註解須要使用元註解進行編寫;
水果名稱註解 FruitName.java:
1 package com.grq.FruitAnnotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.Target; 6 7 import static java.lang.annotation.ElementType.FIELD; 8 import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 10 /** 11 * 水果名稱註解 12 */ 13 @Target(FIELD) 14 @Retention(RUNTIME) 15 @Documented 16 public @interface FruitName { 17 String value() default ""; 18 }
水果顏色註解 FruitColor.java:
1 package com.grq.FruitAnnotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.Target; 6 7 import static java.lang.annotation.ElementType.FIELD; 8 import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 10 /** 11 * 水果顏色註解 12 */ 13 @Target(FIELD) 14 @Retention(RUNTIME) 15 @Documented 16 public @interface FruitColor { 17 /** 18 * 顏色枚舉 19 */ 20 public enum Color{ BLUE,RED,GREEN}; 21 22 /** 23 * 顏色屬性 24 */ 25 Color fruitColor() default Color.GREEN; 26 27 }
水果供應者註解 FruitProvider.java:
1 package com.grq.FruitAnnotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.Target; 6 7 import static java.lang.annotation.ElementType.FIELD; 8 import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 10 /** 11 * 水果供應者註解 12 */ 13 @Target(FIELD) 14 @Retention(RUNTIME) 15 @Documented 16 public @interface FruitProvider { 17 /** 18 * 供應商編號 19 */ 20 public int id() default -1; 21 22 /** 23 * 供應商名稱 24 */ 25 public String name() default ""; 26 27 /** 28 * 供應商地址 29 */ 30 public String address() default ""; 31 }
註解處理器 FruitInfoUtil.java:
1 package com.grq.FruitAnnotation; 2 3 import java.lang.reflect.Field; 4 5 /** 6 * 註解處理器 7 */ 8 public class FruitInfoUtil { 9 public static void getFruitInfo(Class<?> clazz){ 10 11 String strFruitName=" 水果名稱:"; 12 String strFruitColor=" 水果顏色:"; 13 String strFruitProvicer="供應商信息:"; 14 15 Field[] fields = clazz.getDeclaredFields(); 16 17 for(Field field :fields){ 18 if(field.isAnnotationPresent(FruitName.class)){ 19 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class); 20 strFruitName=strFruitName+fruitName.value(); 21 System.out.println(strFruitName); 22 } 23 else if(field.isAnnotationPresent(FruitColor.class)){ 24 FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class); 25 strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); 26 System.out.println(strFruitColor); 27 } 28 else if(field.isAnnotationPresent(FruitProvider.class)){ 29 FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class); 30 strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address(); 31 System.out.println(strFruitProvicer); 32 } 33 } 34 } 35 }
蘋果 Apple.java:
1 package com.grq.FruitAnnotation; 2 3 /** 4 * 註解使用 5 */ 6 public class Apple { 7 8 @FruitName("Apple") 9 private String appleName; 10 11 @FruitColor(fruitColor = FruitColor.Color.RED) 12 private String appleColor; 13 14 @FruitProvider(id=1,name="陝西紅富士集團",address="陝西省西安市延安路89號紅富士大廈") 15 private String appleProvider; 16 17 public void setAppleColor(String appleColor) { 18 this.appleColor = appleColor; 19 } 20 public String getAppleColor() { 21 return appleColor; 22 } 23 24 public void setAppleName(String appleName) { 25 this.appleName = appleName; 26 } 27 public String getAppleName() { 28 return appleName; 29 } 30 31 public void setAppleProvider(String appleProvider) { 32 this.appleProvider = appleProvider; 33 } 34 public String getAppleProvider() { 35 return appleProvider; 36 } 37 38 public void displayName(){ 39 System.out.println("水果的名字是:蘋果"); 40 } 41 }
測試輸出水果信息 FruitTestAnnotation:
1 package com.grq.FruitAnnotation; 2 3 public class TestFruitAnnotation { 4 public static void main(String[] args) { 5 FruitInfoUtil.getFruitInfo(Apple.class); 6 } 7 }
運行後的測試結果爲:
水果名稱:Apple 水果顏色:RED 供應商編號:1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延安路89號紅富士大廈
定義:Java
語言中 一種 動態(運行時)訪問、檢測 & 修改它自己的能力
做用:動態(運行時)獲取類的完整結構信息 & 調用對象的方法
1.類的加載過程: 程序通過javac.exe命令之後,會生成一個或多個字節碼文件(.class結尾)。 接着咱們使用java.exe命令對某個字節碼文件進行解釋運行。至關於將某個字節碼文件 加載到內存中。此過程就稱爲類的加載。加載到內存中的類,咱們就稱爲運行時類,此 運行時類,就做爲Class的一個實例。 2.換句話說,Class的實例就對應着一個運行時類。 3.加載到內存中的運行時類,會緩存必定的時間。在此時間以內,咱們能夠經過不一樣的方式 來獲取此運行時類。
靈活性高。由於反射屬於動態編譯,即只有到運行時才動態建立 &獲取對象實例。
編譯方式說明:
靜態編譯:在編譯時肯定類型 & 綁定對象。如常見的使用
new
關鍵字建立對象動態編譯:運行時肯定類型 & 綁定對象。動態編譯體現了
Java
的靈活性、多態特性 & 下降類之間的藕合性
執行效率低 由於反射的操做 主要經過JVM
執行,因此時間成本會 高於 直接執行相同操做
由於接口的通用性,Java的invoke方法是傳object和object[]數組的。基本類型參數須要裝箱和拆箱,產生大量額外的對象和內存開銷,頻繁促發GC。
編譯器難以對動態調用的代碼提早作優化,好比方法內聯。
反射須要按名檢索類和方法,有必定的時間開銷。
容易破壞類結構 由於反射操做饒過了源碼,容易干擾類原有的內部邏輯
動態獲取 類文件結構信息(如變量、方法等) & 調用對象的方法
經常使用的需求場景有:動態代理、工廠模式優化、Java JDBC
數據庫操做等
下文會用實際例子詳細講解
Java
反射機制提供的功能
反射機制的實現 主要經過 操做java.lang.Class類
下面將主要講解 java.lang.Class
類
定義:java.lang.Class
類是反射機制的基礎
做用:存放着對應類型對象的 運行時信息
在
Java
程序運行時,Java
虛擬機爲全部類型維護一個java.lang.Class
對象該
Class
對象存放着全部關於該對象的 運行時信息泛型形式爲
Class<T>
每種類型的Class
對象只有1個 = 地址只有1個
// 對於2個String類型對象,它們的Class對象相同 Class c1 = "Carson".getClass(); Class c2 = Class.forName("java.lang.String"); // 用==運算符實現兩個類對象地址的比較 System.out.println(c1 ==c2); // 輸出結果:true
Java
反射機制的實現除了依靠Java.lang.Class
類,還須要依靠:Constructor
類、Field
類、Method
類,分別做用於類的各個組成部分:
示意圖
在使用Java
反射機制時,主要步驟包括:
獲取 目標類型的Class
對象
經過 Class
對象分別獲取Constructor
類對象、Method
類對象 & Field
類對象
經過 Constructor
類對象、Method
類對象 & Field
類對象分別獲取類的構造函數、方法&屬性的具體信息,並進行後續操做
下面,我將詳細講解每一個步驟中的使用方法。
步驟1:獲取 目標類型的Class對象
// 獲取 目標類型的`Class`對象的方式主要有4種
1 <-- 方式1:Object.getClass() --> 2 // Object類中的getClass()返回一個Class類型的實例 3 Boolean carson = true; 4 Class<?> classType = carson.getClass(); 5 System.out.println(classType); 6 // 輸出結果:class java.lang.Boolean 7 8 <-- 方式2:T.class 語法 --> 9 // T = 任意Java類型 10 Class<?> classType = Boolean.class; 11 System.out.println(classType); 12 // 輸出結果:class java.lang.Boolean 13 // 注:Class對象表示的是一個類型,而這個類型未必必定是類 14 // 如,int不是類,但int.class是一個Class類型的對象 15 16 <-- 方式3:static method Class.forName --> 17 Class<?> classType = Class.forName("java.lang.Boolean"); 18 // 使用時應提供異常處理器 19 System.out.println(classType); 20 // 輸出結果:class java.lang.Boolean 21 22 <-- 方式4:TYPE語法 --> 23 24 Class<?> classType = Boolean.TYPE; 25 System.out.println(classType); 26 // 輸出結果:boolean
此處額外講一下java.lang.reflect.Type
類
java.lang.reflect.Type
是 Java
中全部類型的父接口
這些類型包括:
示意圖
之間的關係以下
示意圖
步驟2:經過 Class 對象分別獲取Constructor類對象、Method類對象 & Field 類對象
1 // 即如下方法都屬於`Class` 類的方法。 2 3 <-- 1. 獲取類的構造函數(傳入構造函數的參數類型)->> 4 // a. 獲取指定的構造函數 (公共 / 繼承) 5 Constructor<T> getConstructor(Class<?>... parameterTypes) 6 // b. 獲取全部的構造函數(公共 / 繼承) 7 Constructor<?>[] getConstructors(); 8 // c. 獲取指定的構造函數 ( 不包括繼承) 9 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 10 // d. 獲取全部的構造函數( 不包括繼承) 11 Constructor<?>[] getDeclaredConstructors(); 12 // 最終都是得到一個Constructor類對象 13 14 // 特別注意: 15 // 1. 不帶 "Declared"的方法支持取出包括繼承、公有(Public) & 不包括有(Private)的構造函數 16 // 2. 帶 "Declared"的方法是支持取出包括公共(Public)、保護(Protected)、默認(包)訪問和私有(Private)的構造方法,但不包括繼承的構造函數 17 // 下面同理 18 19 <-- 2. 獲取類的屬性(傳入屬性名) --> 20 // a. 獲取指定的屬性(公共 / 繼承) 21 Field getField(String name) ; 22 // b. 獲取全部的屬性(公共 / 繼承) 23 Field[] getFields() ; 24 // c. 獲取指定的全部屬性 (不包括繼承) 25 Field getDeclaredField(String name) ; 26 // d. 獲取全部的全部屬性 (不包括繼承) 27 Field[] getDeclaredFields() ; 28 // 最終都是得到一個Field類對象 29 30 <-- 3. 獲取類的方法(傳入方法名 & 參數類型)--> 31 // a. 獲取指定的方法(公共 / 繼承) 32 Method getMethod(String name, Class<?>... parameterTypes) ; 33 // b. 獲取全部的方法(公共 / 繼承) 34 Method[] getMethods() ; 35 // c. 獲取指定的方法 ( 不包括繼承) 36 Method getDeclaredMethod(String name, Class<?>... parameterTypes) ; 37 // d. 獲取全部的方法( 不包括繼承) 38 Method[] getDeclaredMethods() ; 39 // 最終都是得到一個Method類對象 40 41 <-- 4. Class類的其餘經常使用方法 --> 42 getSuperclass(); 43 // 返回父類 44 45 String getName(); 46 // 做用:返回完整的類名(含包名,如java.lang.String ) 47 48 Object newInstance(); 49 // 做用:快速地建立一個類的實例 50 // 具體過程:調用默認構造器(若該類無默認構造器,則拋出異常 51 // 注:若須要爲構造器提供參數需使用java.lang.reflect.Constructor中的newInstance()
步驟3:經過 Constructor類對象、Method類對象 & Field類對象分別獲取類的構造函數、方法 & 屬性的具體信息 & 進行操做
1 // 即如下方法都分別屬於`Constructor`類、`Method`類 & `Field`類的方法。 2 3 <-- 1. 經過Constructor 類對象獲取類構造函數信息 --> 4 String getName();// 獲取構造器名 5 Class getDeclaringClass();// 獲取一個用於描述類中定義的構造器的Class對象 6 int getModifiers();// 返回整型數值,用不一樣的位開關描述訪問修飾符的使用情況 7 Class[] getExceptionTypes();// 獲取描述方法拋出的異常類型的Class對象數組 8 Class[] getParameterTypes();// 獲取一個用於描述參數類型的Class對象數組 9 10 <-- 2. 經過Field類對象獲取類屬性信息 --> 11 String getName();// 返回屬性的名稱 12 Class getDeclaringClass(); // 獲取屬性類型的Class類型對象 13 Class getType();// 獲取屬性類型的Class類型對象 14 int getModifiers(); // 返回整型數值,用不一樣的位開關描述訪問修飾符的使用情況 15 Object get(Object obj) ;// 返回指定對象上 此屬性的值 16 void set(Object obj, Object value) // 設置 指定對象上此屬性的值爲value 17 18 <-- 3. 經過Method 類對象獲取類方法信息 --> 19 String getName();// 獲取方法名 20 Class getDeclaringClass();// 獲取方法的Class對象 21 int getModifiers();// 返回整型數值,用不一樣的位開關描述訪問修飾符的使用情況 22 Class[] getExceptionTypes();// 獲取用於描述方法拋出的異常類型的Class對象數組 23 Class[] getParameterTypes();// 獲取一個用於描述參數類型的Class對象數組 24 25 <--額外:java.lang.reflect.Modifier類 --> 26 // 做用:獲取訪問修飾符 27 28 static String toString(int modifiers) 29 // 獲取對應modifiers位設置的修飾符的字符串表示 30 31 static boolean isXXX(int modifiers) 32 // 檢測方法名中對應的修飾符在modifiers中的值
至此,關於Java
反射機制的步驟說明已經講解完畢。
背景 反射機制的默認行爲受限於Java
的訪問控制
如,沒法訪問(
private
)私有的方法、字段
衝突 Java安全機制只容許查看任意對象有哪些域,而不容許讀它們的值
若強制讀取,將拋出異常
解決方案 脫離Java
程序中安全管理器的控制、屏蔽Java語言的訪問檢查,從而脫離訪問控制
具體實現手段:使用Field類
、Method類
& Constructor
類對象的setAccessible()
void setAccessible(boolean flag) // 做用:爲反射對象設置可訪問標誌 // 規則:flag = true時 ,表示已屏蔽Java語言的訪問檢查,使得能夠訪問 & 修改對象的私有屬性 boolean isAccessible() // 返回反射對象的可訪問標誌的值 static void setAccessible(AccessibleObject[] array, boolean flag) // 設置對象數組可訪問標誌
1 <-- 測試類定義--> 2 public class Student { 3 4 public Student() { 5 System.out.println("建立了一個Student實例"); 6 } 7 private String name; 8 } 9 10 <-- 利用反射獲取屬性 & 賦值 --> 11 // 1. 獲取Student類的Class對象 12 Class studentClass = Student.class; 13 14 // 2. 經過Class對象建立Student類的對象 15 Object mStudent = studentClass.newInstance(); 16 17 // 3. 經過Class對象獲取Student類的name屬性 18 Field f = studentClass.getDeclaredField("name"); 19 20 // 4. 設置私有訪問權限 21 f.setAccessible(true); 22 23 // 5. 對新建立的Student對象設置name值 24 f.set(mStudent, "Carson_Ho"); 25 26 // 6. 獲取新建立Student對象的的name屬性 & 輸出 27 System.out.println(f.get(mStudent));
測試結果
1 <-- 測試類定義--> 2 3 public class Student { 4 5 // 無參構造函數 6 public Student() { 7 System.out.println("調用了無參構造函數"); 8 } 9 10 // 有參構造函數 11 public Student(String str) { 12 System.out.println("調用了有參構造函數"); 13 } 14 15 private String name; 16 } 17 18 <-- 利用反射調用構造函數 --> 19 // 1. 獲取Student類的Class對象 20 Class studentClass studentClass = Student.class; 21 22 // 2.1 經過Class對象獲取Constructor類對象,從而調用無參構造方法 23 // 注:構造函數的調用其實是在newInstance(),而不是在getConstructor()中調用 24 Object mObj1 = studentClass.getConstructor().newInstance(); 25 26 // 2.2 經過Class對象獲取Constructor類對象(傳入參數類型),從而調用有參構造方法 27 Object mObj2 = studentClass.getConstructor(String.class).newInstance("Carson");
測試結果
1 <-- 測試類定義--> 2 public class Student { 3 4 public Student() { 5 System.out.println("建立了一個Student實例"); 6 } 7 8 // 無參數方法 9 public void setName1 (){ 10 System.out.println("調用了無參方法:setName1()"); 11 } 12 13 // 有參數方法 14 public void setName2 (String str){ 15 System.out.println("調用了有參方法setName2(String str):" + str); 16 } 17 } 18 19 <-- 利用反射調用方法 --> 20 // 1. 獲取Student類的Class對象 21 Class studentClass = Student.class; 22 23 // 2. 經過Class對象建立Student類的對象 24 Object mStudent = studentClass.newInstance(); 25 26 // 3.1 經過Class對象獲取方法setName1()的Method對象:需傳入方法名 27 // 由於該方法 = 無參,因此不須要傳入參數 28 Method msetName1 = studentClass.getMethod("setName1"); 29 30 // 經過Method對象調用setName1():需傳入建立的實例 31 msetName1.invoke(mStudent); 32 33 // 3.2 經過Class對象獲取方法setName2()的Method對象:需傳入方法名 & 參數類型 34 Method msetName2 = studentClass.getMethod("setName2",String.class); 35 36 // 經過Method對象調用setName2():需傳入建立的實例 & 參數值 37 msetName2.invoke(mStudent,"Carson_Ho");
測試結果
背景 採用簡單工廠模式
衝突
操做成本高:每增長一個接口的子類,必須修改工廠類的邏輯
系統複雜性提升:每增長一個接口的子類,都必須向工廠類添加邏輯
關於 簡單工廠模式的介紹 & 使用 請看文章:簡單工廠模式(SimpleFactoryPattern)- 最易懂的設計模式解析
解決方案 採用反射機制: 經過 傳入子類名稱 & 動態建立子類實例,從而使得在增長產品接口子類的狀況下,也不須要修改工廠類的邏輯
實例演示
步驟1. 建立抽象產品類的公共接口
Product.java abstract class Product{ public abstract void show(); }
步驟2. 建立具體產品類(繼承抽象產品類),定義生產的具體產品
1 <-- 具體產品類A:ProductA.java --> 2 public class ProductA extends Product{ 3 4 @Override 5 public void show() { 6 System.out.println("生產出了產品A"); 7 } 8 } 9 10 <-- 具體產品類B:ProductB.java --> 11 public class ProductB extends Product{ 12 13 @Override 14 public void show() { 15 System.out.println("生產出了產品B"); 16 } 17 }
步驟3. 建立工廠類
Factory.java
1 public class Factory { 2 3 // 定義方法:經過反射動態建立產品類實例 4 public static Product getInstance(String ClassName) { 5 6 Product concreteProduct = null; 7 8 try { 9 10 // 1. 根據 傳入的產品類名 獲取 產品類類型的Class對象 11 Class product_Class = Class.forName(ClassName); 12 // 2. 經過Class對象動態建立該產品類的實例 13 concreteProduct = (Product) product_Class.newInstance(); 14 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 19 // 3. 返回該產品類實例 20 return concreteProduct; 21 } 22 23 }
步驟4:外界經過調用工廠類的靜態方法(反射原理),傳入不一樣參數從而建立不一樣具體產品類的實例
TestReflect.java
1 public class TestReflect { 2 public static void main(String[] args) throws Exception { 3 4 // 1. 經過調用工廠類的靜態方法(反射原理),從而動態建立產品類實例 5 // 需傳入完整的類名 & 包名 6 Product concreteProduct = Factory.getInstance("scut.carson_ho.reflection_factory.ProductA"); 7 8 // 2. 調用該產品類對象的方法,從而生產產品 9 concreteProduct.show(); 10 } 11 }
展現結果
如此一來,經過採用反射機制(經過 傳入子類名稱 & 動態建立子類實例),從而使得在增長產品接口子類的狀況下,也不須要修改工廠類的邏輯 & 增長系統複雜度
背景 在上述方案中,經過調用工廠類的靜態方法(反射原理),從而動態建立產品類實例(該過程當中:需傳入完整的類名 & 包名)
衝突 開發者 沒法提早預知 接口中的子類類型 & 完整類名
解決方案 經過 屬性文件的形式( Properties) 配置所要的子類信息,在使用時直接讀取屬性配置文件從而獲取子類信息(完整類名)
具體實現
步驟1:建立抽象產品類的公共接口
Product.java
abstract class Product{ public abstract void show(); }
步驟2. 建立具體產品類(繼承抽象產品類),定義生產的具體產品
1 <-- 具體產品類A:ProductA.java --> 2 public class ProductA extends Product{ 3 4 @Override 5 public void show() { 6 System.out.println("生產出了產品A"); 7 } 8 } 9 10 <-- 具體產品類B:ProductB.java --> 11 public class ProductB extends Product{ 12 13 @Override 14 public void show() { 15 System.out.println("生產出了產品B"); 16 } 17 }
步驟3. 建立工廠類
Factory.java
1 public class Factory { 2 3 // 定義方法:經過反射動態建立產品類實例 4 public static Product getInstance(String ClassName) { 5 6 Product concreteProduct = null; 7 8 try { 9 10 // 1. 根據 傳入的產品類名 獲取 產品類類型的Class對象 11 Class product_Class = Class.forName(ClassName); 12 // 2. 經過Class對象動態建立該產品類的實例 13 concreteProduct = (Product) product_Class.newInstance(); 14 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 19 // 3. 返回該產品類實例 20 return concreteProduct; 21 } 22 23 }
步驟4:建立屬性配置文件 Product.properties
// 寫入抽象產品接口類的子類信息(完整類名)
ProductA = scut.carson_ho.reflection_factory.ProductA
ProductB = scut.carson_ho.reflection_factory.ProductB
步驟5:將屬性配置文件 放到src/main/assets文件夾中
若沒
assets
文件夾,則自行建立
步驟6:在動態建立產品類對象時,動態讀取屬性配置文件從而獲取子類完整類名 TestReflect.java
1 public class TestReflect { 2 public static void main(String[] args) throws Exception { 3 4 // 1. 讀取屬性配置文件 5 Properties pro = new Properties() ; 6 pro.load(this.getAssets().open("Product.properties")); 7 8 // 2. 獲取屬性配置文件中的產品類名 9 String Classname = pro.getProperty("ProductA"); 10 11 // 3. 動態生成產品類實例 12 Product concreteProduct = Factory.getInstance(Classname); 13 14 // 4. 調用該產品類對象的方法,從而生產產品 15 concreteProduct.show(); 16 17 }
1 /** 2 * 瞭解類的加載器 3 * @author shkstart 4 * @create 2019 下午 2:16 5 */ 6 public class ClassLoaderTest { 7 8 @Test 9 public void test1(){ 10 //對於自定義類,使用系統類加載器進行加載 11 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); 12 System.out.println(classLoader); 13 //調用系統類加載器的getParent():獲取擴展類加載器 14 ClassLoader classLoader1 = classLoader.getParent(); 15 System.out.println(classLoader1); 16 //調用擴展類加載器的getParent():沒法獲取引導類加載器 17 //引導類加載器主要負責加載java的核心類庫,沒法加載自定義類的。 18 ClassLoader classLoader2 = classLoader1.getParent(); 19 System.out.println(classLoader2); 20 21 ClassLoader classLoader3 = String.class.getClassLoader(); 22 System.out.println(classLoader3); 23 24 } 25 /* 26 Properties:用來讀取配置文件。 27 28 */ 29 @Test 30 public void test2() throws Exception { 31 32 Properties pros = new Properties(); 33 //此時的文件默認在當前的module下。 34 //讀取配置文件的方式一: 35 // FileInputStream fis = new FileInputStream("jdbc.properties"); 36 // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); 37 // pros.load(fis); 38 39 //讀取配置文件的方式二:使用ClassLoader 40 //配置文件默認識別爲:當前module的src下 41 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); 42 InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); 43 pros.load(is); 44 45 46 String user = pros.getProperty("user"); 47 String password = pros.getProperty("password"); 48 System.out.println("user = " + user + ",password = " + password); 49 50 51 52 } 53 54 }
1 public class FieldTest { 2 3 @Test 4 public void test1(){ 5 6 Class clazz = Person.class; 7 8 //獲取屬性結構 9 //getFields():獲取當前運行時類及其父類中聲明爲public訪問權限的屬性 10 Field[] fields = clazz.getFields(); 11 for(Field f : fields){ 12 System.out.println(f); 13 } 14 System.out.println(); 15 16 //getDeclaredFields():獲取當前運行時類中聲明的全部屬性。(不包含父類中聲明的屬性) 17 Field[] declaredFields = clazz.getDeclaredFields(); 18 for(Field f : declaredFields){ 19 System.out.println(f); 20 } 21 } 22 23 //權限修飾符 數據類型 變量名 24 @Test 25 public void test2(){ 26 Class clazz = Person.class; 27 Field[] declaredFields = clazz.getDeclaredFields(); 28 for(Field f : declaredFields){ 29 //1.權限修飾符 30 int modifier = f.getModifiers(); 31 System.out.print(Modifier.toString(modifier) + "\t"); 32 33 //2.數據類型 34 Class type = f.getType(); 35 System.out.print(type.getName() + "\t"); 36 37 //3.變量名 38 String fName = f.getName(); 39 System.out.print(fName); 40 41 System.out.println(); 42 } 43 } 44 45 }
1 public class MethodTest { 2 3 @Test 4 public void test1(){ 5 6 Class clazz = Person.class; 7 8 //getMethods():獲取當前運行時類及其全部父類中聲明爲public權限的方法 9 Method[] methods = clazz.getMethods(); 10 for(Method m : methods){ 11 System.out.println(m); 12 } 13 System.out.println(); 14 //getDeclaredMethods():獲取當前運行時類中聲明的全部方法。(不包含父類中聲明的方法) 15 Method[] declaredMethods = clazz.getDeclaredMethods(); 16 for(Method m : declaredMethods){ 17 MyAnnotation a = m.getAnnotation(MyAnnotation.class); 18 System.out.println(m); 19 } 20 } 21 22 /* 23 @Xxxx 24 權限修飾符 返回值類型 方法名(參數類型1 形參名1,...) throws XxxException{} 25 */ 26 @Test 27 public void test2(){ 28 Class clazz = Person.class; 29 Method[] declaredMethods = clazz.getDeclaredMethods(); 30 for(Method m : declaredMethods){ 31 //1.獲取方法聲明的註解 32 Annotation[] annos = m.getAnnotations(); 33 for(Annotation a : annos){ 34 System.out.println(a); 35 } 36 37 //2.權限修飾符 38 System.out.print(Modifier.toString(m.getModifiers()) + "\t"); 39 40 //3.返回值類型 41 System.out.print(m.getReturnType().getName() + "\t"); 42 43 //4.方法名 44 System.out.print(m.getName()); 45 System.out.print("("); 46 //5.形參列表 47 Class[] parameterTypes = m.getParameterTypes(); 48 if(!(parameterTypes == null && parameterTypes.length == 0)){ 49 for(int i = 0;i < parameterTypes.length;i++){ 50 51 if(i == parameterTypes.length - 1){ 52 System.out.print(parameterTypes[i].getName() + " args_" + i); 53 break; 54 } 55 56 System.out.print(parameterTypes[i].getName() + " args_" + i + ","); 57 } 58 } 59 60 System.out.print(")"); 61 62 //6.拋出的異常 63 Class[] exceptionTypes = m.getExceptionTypes(); 64 if(exceptionTypes.length > 0){ 65 System.out.print("throws "); 66 for(int i = 0;i < exceptionTypes.length;i++){ 67 if(i == exceptionTypes.length - 1){ 68 System.out.print(exceptionTypes[i].getName()); 69 break; 70 } 71 72 System.out.print(exceptionTypes[i].getName() + ","); 73 } 74 } 75 76 System.out.println(); 77 } 78 } 79 }
1 /** 2 * @author shkstart 3 * @create 2019 下午 4:19 4 */ 5 public class OtherTest { 6 7 /* 8 獲取構造器結構 9 10 */ 11 @Test 12 public void test1(){ 13 14 Class clazz = Person.class; 15 //getConstructors():獲取當前運行時類中聲明爲public的構造器 16 Constructor[] constructors = clazz.getConstructors(); 17 for(Constructor c : constructors){ 18 System.out.println(c); 19 } 20 21 System.out.println(); 22 //getDeclaredConstructors():獲取當前運行時類中聲明的全部的構造器 23 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); 24 for(Constructor c : declaredConstructors){ 25 System.out.println(c); 26 } 27 28 } 29 30 /* 31 獲取運行時類的父類 32 33 */ 34 @Test 35 public void test2(){ 36 Class clazz = Person.class; 37 38 Class superclass = clazz.getSuperclass(); 39 System.out.println(superclass); 40 } 41 42 /* 43 獲取運行時類的帶泛型的父類 44 45 */ 46 @Test 47 public void test3(){ 48 Class clazz = Person.class; 49 50 Type genericSuperclass = clazz.getGenericSuperclass(); 51 System.out.println(genericSuperclass); 52 } 53 54 /* 55 獲取運行時類的帶泛型的父類的泛型 56 57 58 代碼:邏輯性代碼 vs 功能性代碼 59 */ 60 @Test 61 public void test4(){ 62 Class clazz = Person.class; 63 64 Type genericSuperclass = clazz.getGenericSuperclass(); 65 ParameterizedType paramType = (ParameterizedType) genericSuperclass; 66 //獲取泛型類型 67 Type[] actualTypeArguments = paramType.getActualTypeArguments(); 68 // System.out.println(actualTypeArguments[0].getTypeName()); 69 System.out.println(((Class)actualTypeArguments[0]).getName()); 70 } 71 72 /* 73 獲取運行時類實現的接口 74 */ 75 @Test 76 public void test5(){ 77 Class clazz = Person.class; 78 79 Class[] interfaces = clazz.getInterfaces(); 80 for(Class c : interfaces){ 81 System.out.println(c); 82 } 83 84 System.out.println(); 85 //獲取運行時類的父類實現的接口 86 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); 87 for(Class c : interfaces1){ 88 System.out.println(c); 89 } 90 91 } 92 /* 93 獲取運行時類所在的包 94 95 */ 96 @Test 97 public void test6(){ 98 Class clazz = Person.class; 99 100 Package pack = clazz.getPackage(); 101 System.out.println(pack); 102 } 103 104 /* 105 獲取運行時類聲明的註解 106 107 */ 108 @Test 109 public void test7(){ 110 Class clazz = Person.class; 111 112 Annotation[] annotations = clazz.getAnnotations(); 113 for(Annotation annos : annotations){ 114 115 System.out.println( annos.toString()); 116 } 117 } 118 119 }
1 /** 2 * 調用運行時類中指定的結構:屬性、方法、構造器 3 * 4 * @author shkstart 5 * @create 2019 下午 4:46 6 */ 7 public class ReflectionTest { 8 9 /* 10 11 不須要掌握 12 */ 13 @Test 14 public void testField() throws Exception { 15 Class clazz = Person.class; 16 17 //建立運行時類的對象 18 Person p = (Person) clazz.newInstance(); 19 20 21 //獲取指定的屬性:要求運行時類中屬性聲明爲public 22 //一般不採用此方法 23 Field id = clazz.getField("id"); 24 25 /* 26 設置當前屬性的值 27 28 set():參數1:指明設置哪一個對象的屬性 參數2:將此屬性值設置爲多少 29 */ 30 31 id.set(p,1001); 32 33 /* 34 獲取當前屬性的值 35 get():參數1:獲取哪一個對象的當前屬性值 36 */ 37 int pId = (int) id.get(p); 38 System.out.println(pId); 39 40 41 } 42 /* 43 如何操做運行時類中的指定的屬性 -- 須要掌握 44 */ 45 @Test 46 public void testField1() throws Exception { 47 Class clazz = Person.class; 48 49 //建立運行時類的對象 50 Person p = (Person) clazz.newInstance(); 51 52 //1. getDeclaredField(String fieldName):獲取運行時類中指定變量名的屬性 53 Field name = clazz.getDeclaredField("name"); 54 55 //2.保證當前屬性是可訪問的 56 name.setAccessible(true); 57 //3.獲取、設置指定對象的此屬性值 58 name.set(p,"Tom"); 59 60 System.out.println(name.get(p)); 61 } 62 63 /* 64 如何操做運行時類中的指定的方法 -- 須要掌握 65 */ 66 @Test 67 public void testMethod() throws Exception { 68 69 Class clazz = Person.class; 70 71 //建立運行時類的對象 72 Person p = (Person) clazz.newInstance(); 73 74 /* 75 1.獲取指定的某個方法 76 getDeclaredMethod():參數1 :指明獲取的方法的名稱 參數2:指明獲取的方法的形參列表 77 */ 78 Method show = clazz.getDeclaredMethod("show", String.class); 79 //2.保證當前方法是可訪問的 80 show.setAccessible(true); 81 82 /* 83 3. 調用方法的invoke():參數1:方法的調用者 參數2:給方法形參賦值的實參 84 invoke()的返回值即爲對應類中調用的方法的返回值。 85 */ 86 Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); 87 System.out.println(returnValue); 88 89 System.out.println("*************如何調用靜態方法*****************"); 90 91 // private static void showDesc() 92 93 Method showDesc = clazz.getDeclaredMethod("showDesc" ,String.class); 94 showDesc.setAccessible(true); 95 //若是調用的運行時類中的方法沒有返回值,則此invoke()返回null 96 // Object returnVal = showDesc.invoke(null); 97 Object returnVal = showDesc.invoke(Person.class,"ER"); 98 System.out.println(returnVal);//null 99 100 } 101 102 /* 103 如何調用運行時類中的指定的構造器 104 */ 105 @Test 106 public void testConstructor() throws Exception { 107 Class clazz = Person.class; 108 109 //private Person(String name) 110 /* 111 1.獲取指定的構造器 112 getDeclaredConstructor():參數:指明構造器的參數列表 113 */ 114 Constructor constructor = clazz.getDeclaredConstructor(String.class); 115 116 //2.保證此構造器是可訪問的 117 constructor.setAccessible(true); 118 119 //3.調用此構造器建立運行時類的對象 120 Person per = (Person) constructor.newInstance("Tom"); 121 System.out.println(per); 122 123 } 124 125 }