枚舉註解反射整合詳解

一丶枚舉

  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 }

 

二丶註解

2.1 什麼是註解?

什麼是註解?嚴謹的來講,註解提供了一種安全的相似註釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。爲程序的元素(類、方法、成員變量)加上更直觀的說明,這些說明信息是與程序的業務邏輯無關,而且供指定的工具或框架使用。Annontation像一種修飾符同樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。 Java 註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能註解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的做用。註解包含在 java.lang.annotation 包中。java

2.2 註解的做用

  1. 可以讀懂別人寫的代碼(尤爲是框架相關的代碼);web

  2. 實現替代配置文件的功能。好比可能本來須要不少配置文件以及不少邏輯才能實現的內容,若是使用合理的註解,就可使用一個或多個註解來實現相同的功能。這樣就使得代碼更加清晰和整潔;spring

  3. 編譯時進行格式檢查數據庫

    • 如 @Override 註解放在方法前,若是該方法不是覆蓋了某個超類方法,編譯的時候編譯器就能檢查出來。設計模式

  4. 裝逼。數組

    • 作技術的怎麼能夠沒有一點用技術吹牛逼的心理呢?若是會在合適的地方剛好的使用註解或者自定義註解的話,老闆確定會雙手送你 666 的。固然筆者如今只是初學而已,距離用技術吹牛逼的道路還遠。緩存

2.3 元註解

元註解是能夠註解到註解上的註解,或者說元註解是一種基本註解,可是它可以應用到其它的註解上面。或者能夠理解爲:元註解也是一張標籤,可是它是一張特殊的標籤,它的做用和目的就是給其餘普通的標籤進行解釋說明的安全

基本的元標籤有 @Retention, @Documented, @Target, @Inherited 四種(後來到了 Java 8 又加入了 @Repeatable)。app

2.3.1 @Retention

@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) 註解,因此在程序運行時能夠捕獲到它們。

2.3.2 @Target

@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 只能用於修飾類。

2.3.3 @Documented

@Documented 是一個簡單的標記註解,表示是否將註解信息添加在 Java 文檔,即 Javadoc 中。

2.3.4 @Inherited

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 這個註解。能夠這樣理解: 老子很是有錢,因此人們給他貼了一張標籤叫作富豪。 老子的兒子長大後,只要沒有和老子斷絕父子關係,雖然別人沒有給他貼標籤,可是他天然也是富豪。 老子的孫子長大了,天然也是富豪。 這就是人們口中戲稱的富一代,富二代,富三代。雖然叫法不一樣,好像好多個標籤,但其實事情的本質也就是他們有一張共同的標籤,也就是老子身上的那張富豪的標籤。

2.3.5 @Repeatable

@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

2.4 註解的屬性

註解的屬性也叫作成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以無形參的方法形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。如下面的例程爲例:

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;

 

2.5 經常使用註解

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") 一樣的效果。

2.6 自定義註解

自定義註解類編寫的規則:

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號紅富士大廈


三丶反射

 


3.1. 簡介

  • 定義:Java語言中 一種 動態(運行時)訪問、檢測 & 修改它自己的能力

  • 做用:動態(運行時)獲取類的完整結構信息 & 調用對象的方法

1.類的加載過程: 程序通過javac.exe命令之後,會生成一個或多個字節碼文件(.class結尾)。 接着咱們使用java.exe命令對某個字節碼文件進行解釋運行。至關於將某個字節碼文件 加載到內存中。此過程就稱爲類的加載。加載到內存中的類,咱們就稱爲運行時類,此 運行時類,就做爲Class的一個實例。 2.換句話說,Class的實例就對應着一個運行時類。 3.加載到內存中的運行時類,會緩存必定的時間。在此時間以內,咱們能夠經過不一樣的方式 來獲取此運行時類。


3.2. 特色

3.2.1 優勢

靈活性高。由於反射屬於動態編譯,即只有到運行時才動態建立 &獲取對象實例。

編譯方式說明:

  1. 靜態編譯:在編譯時肯定類型 & 綁定對象。如常見的使用new關鍵字建立對象

  2. 動態編譯:運行時肯定類型 & 綁定對象。動態編譯體現了Java的靈活性、多態特性 & 下降類之間的藕合性

3.2.2 缺點
  • 執行效率低 由於反射的操做 主要經過JVM執行,因此時間成本會 高於 直接執行相同操做

  1. 由於接口的通用性,Java的invoke方法是傳object和object[]數組的。基本類型參數須要裝箱和拆箱,產生大量額外的對象和內存開銷,頻繁促發GC。

  2. 編譯器難以對動態調用的代碼提早作優化,好比方法內聯。

  3. 反射須要按名檢索類和方法,有必定的時間開銷。

  • 容易破壞類結構 由於反射操做饒過了源碼,容易干擾類原有的內部邏輯


3.3. 應用場景

  • 動態獲取 類文件結構信息(如變量、方法等) & 調用對象的方法

  • 經常使用的需求場景有:動態代理、工廠模式優化、Java JDBC數據庫操做等

下文會用實際例子詳細講解


3.4. 具體使用

3.4.1 Java反射機制提供的功能

 

3.4.2 實現手段

  • 反射機制的實現 主要經過 操做java.lang.Class類

  • 下面將主要講解 java.lang.Class

3.4.2.1 java.lang.Class 類
  • 定義:java.lang.Class類是反射機制的基礎

  • 做用:存放着對應類型對象的 運行時信息

  1. Java程序運行時,Java虛擬機爲全部類型維護一個java.lang.Class對象

  2. Class對象存放着全部關於該對象的 運行時信息

  3. 泛型形式爲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類,分別做用於類的各個組成部分:

 

示意圖

3.4.3 使用步驟

在使用Java反射機制時,主要步驟包括:

  1. 獲取 目標類型的Class對象

  2. 經過 Class 對象分別獲取Constructor類對象、Method類對象 & Field 類對象

  3. 經過 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.TypeJava中全部類型的父接口

  • 這些類型包括:

 

示意圖

  • 之間的關係以下

 

示意圖

步驟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反射機制的步驟說明已經講解完畢。


3.4.4 特別注意:訪問權限問題

  • 背景 反射機制的默認行爲受限於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)   
// 設置對象數組可訪問標誌

3.5. 實例應用講解

3.5.1 基礎應用講解
實例1:利用反射獲取類的屬性 & 賦值
 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));

 

  • 測試結果

 

實例2:利用反射調用類的構造函數
 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");

 

  • 測試結果

 

實例3:利用反射調用類對象的方法
 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");

 


   
  • 測試結果

 


3.5.2 常見需求場景講解
實例1:工廠模式優化
  • 背景 採用簡單工廠模式

  • 衝突

    1. 操做成本高:每增長一個接口的子類,必須修改工廠類的邏輯

    2. 系統複雜性提升:每增長一個接口的子類,都必須向工廠類添加邏輯

關於 簡單工廠模式的介紹 & 使用 請看文章:簡單工廠模式(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 }

 

  • 展現結果

 

如此一來,經過採用反射機制(經過 傳入子類名稱 & 動態建立子類實例),從而使得在增長產品接口子類的狀況下,也不須要修改工廠類的邏輯 & 增長系統複雜度

實例2:應用了反射機制的工廠模式再次優化
  • 背景 在上述方案中,經過調用工廠類的靜態方法(反射原理),從而動態建立產品類實例(該過程當中:需傳入完整的類名 & 包名)

  • 衝突 開發者 沒法提早預知 接口中的子類類型 & 完整類名

  • 解決方案 經過 屬性文件的形式( 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 }

 

  • 測試結果

 

3.6類加載器

 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 }

 

4、整合

4.1Field級別

 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 }

 

4.2Method級別

 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 }

 

4.3Other級別

  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 }

 

4.4Reflection級別

  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 }
相關文章
相關標籤/搜索