thinking in java學習筆記:14章 類型信息

14.2 Class 對象

https://github.com/zhaojiatao/javasehtml

一、什麼是Class對象,Class對象是用來作什麼的?

Class對象是java程序用來建立類的全部常規對象用的;每一個類都有一個Class對象;java

二、Class對象是如何建立的?

當程序建立第一個對類的靜態成員(static修飾的成員以及構造方法)的引用時,就會加載這個類。類加載器首先檢查這個類的git

Class對象是否已經加載;若是還沒有加載,默認的類加載器就會查找.class文件。在加載字節碼後會執行安全驗證,以後會根程序員

據字節碼在內存中建立這個類的Class對象;github

三、除了jvm的類加載器會獲取某個類的Class對象以外,咱們本身如何獲取某個類的Class對象的引用?

這個例子會介紹第一種方法:使用Class類的forName()方法,獲取類的Class對象的引用;這個方法的反作用是,若是jvmweb

還未加載這個類的Class對象的話就加載這個類;在類加載的過程當中,會初始化static成員;spring

注意,傳遞給forName方法的名字,必須是全限定名;apache

此外,還可使用類字面常量的方法來生成對類Class對象的引用;設計模式

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 
 4 /**
 5  * @author zhaojiatao
 6  * @date 2018/9/9
 7  *
 8  * 一、什麼是Class對象,Class對象是用來作什麼的?
 9  *   Class對象是java程序用來建立類的全部常規對象用的;每一個類都有一個Class對象;
10  * 二、Class對象是如何建立的?
11  *   當程序建立第一個對類的靜態成員(static修飾的成員以及構造方法)的引用時,就會加載這個類。類加載器首先檢查這個類的
12  *   Class對象是否已經加載;若是還沒有加載,默認的類加載器就會查找.class文件。在加載字節碼後會執行安全驗證,以後會根
13  *   據字節碼在內存中建立這個類的Class對象;
14  * 三、除了jvm的類加載器會獲取某個類的Class對象以外,咱們本身如何獲取某個類的Class對象的引用?
15  *    這個例子會介紹第一種方法:使用Class類的forName()方法,獲取類的Class對象的引用;這個方法的反作用是,若是jvm
16  *    還又有加載這個類的Class對象的話就加載這個類;在類加載的過程當中,會初始化static成員;
17  *    注意,傳遞給forName方法的名字,必須是全限定名;
18  *    此外,還可使用類字面常量的方法來生成對類Class對象的引用;
19  *
20  *
21  */
22 interface HasBatteries{}
23 
24 interface Waterproof{}
25 
26 interface Shoots{}
27 
28 class Toy{
29     Toy(){}
30     Toy(int i){}
31 }
32 
33 class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
34     FancyToy(){
35         super(1);
36     }
37 }
38 
39 public class Test2 {
40 
41     //經過Class對象,能夠獲得Class對象的多有信息
42     static void printInfo(Class cc){
43         System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]");
44         System.out.println("Simple name:"+cc.getSimpleName());
45         System.out.println("Canonical name:"+cc.getCanonicalName());
46     }
47 
48 
49     public static void main(String[] args) {
50         Class c=null;
51 
52         try{
53             //注意,必須使用全限定名
54             c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy");
55         }catch (ClassNotFoundException e){
56             System.out.println("Can't find Test2");
57             System.exit(1);
58         }
59 
60         printInfo(c);
61 
62         for(Class face:c.getInterfaces()){
63             printInfo(face);
64         }
65 
66         Class up=c.getSuperclass();
67         Object obj=null;
68         try{
69             //使用newInstance來建立的類,必須帶有默認構造器
70             obj=up.newInstance();
71         }catch(InstantiationException e){
72             System.out.println("Cannot instantiate");
73             System.exit(1);
74         }catch (IllegalAccessException e){
75             System.out.println("Cannot access");
76             System.exit(1);
77         }
78         printInfo(obj.getClass());
79 
80         System.out.println(obj instanceof Toy);
81 
82     }
83 
84 
85 }
View Code

 

14.2.1 字面類常量

除了使用class的forName方法獲取類的Class對象的引用外,還可使用類字面常量;數組

這種方式不只簡單,且更安全,由於會在類編譯期就進行安全檢查

注意,使用.class的方式,獲取類的Class對象的引用,不會像forName()那樣初始化類的Class對象

.class的方式僅僅是獲取了Class對象的引用,只有到了類真正被初始化結束後,Class對象才真正被初始化;即:

只有在對靜態方法或很是數靜態域進行首次引用時才執行初始化;

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 import java.util.Random;
 4 
 5 /**
 6  * @author zhaojiatao
 7  * @date 2018/9/10
 8  *
 9  * 除了使用class的forName方法獲取類的Class對象的引用外,還可使用類字面常量;
10  * 這種方式不只簡單,且更安全,由於會在類編譯期就進行安全檢查;
11  * 注意,使用.class的方式,獲取類的Class對象的引用,不會像forName()那樣初始化類的Class對象;
12  * .class的方式僅僅是獲取了Class對象的引用,只有到了類真正被初始化結束後,Class對象才真正被初始化;即:
13  * 只有在對靜態方法或很是數靜態域進行首次引用時才執行初始化;
14  *
15  */
16 public class Test2_1 {
17     public static Random rand=new Random(47);
18 
19     public static void main(String[] args) throws ClassNotFoundException {
20         Class initable=Initable.class;
21         System.out.println("這個時候僅僅是獲取到了Initable類的Class對象引用,Class對象還沒初始化");
22         //對於static final值是編譯期常量,則該值無需對類初始化就能夠被讀取;
23         System.out.println(Initable.staticFinal);
24         //非編譯期常量,即便被static和final修飾,也必須先初始化類Class對象,才能讀取;
25         System.out.println(Initable.staticFinal2);
26 
27         //若是一個static域不是final的,那麼在對它訪問時,老是要求在它被讀取以前,先進行連接(爲這個域分配存儲空間)
28         //以及初始化(初始化該存儲空間)
29         System.out.println(Initable2.staticNonFinal);
30 
31         //使用forName方法,就會初始化類
32         Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3");
33         System.out.println(Initable3.staticNonFinal);
34 
35 
36 
37     }
38 
39 }
40 
41 
42 class Initable{
43     static final int staticFinal=47;
44     static final int staticFinal2=(int)(1+Math.random()*(10-1+1));
45     static {
46         System.out.println("Initializing Initable");
47     }
48 }
49 
50 class Initable2{
51     static int staticNonFinal=147;
52     static {
53         System.out.println("Initializing Initable2");
54     }
55 }
56 
57 class Initable3{
58     static int staticNonFinal=74;
59     static{
60         System.out.println("Initializing Initable3");
61     }
62 }
View Code

  

14.2.2 泛化

學習範型在Class引用的使用過程的應用

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.junit.Test;
  4 
  5 import java.util.ArrayList;
  6 import java.util.List;
  7 
  8 /**
  9  * @author zhaojiatao
 10  * @date 2018/9/11
 11  *
 12  *
 13  * 學習範型在Class引用的使用過程的應用
 14  *
 15  *
 16  */
 17 public class Test2_2 {
 18 
 19 
 20     @Test
 21     public void Test01() {
 22         Class intClass=int.class;
 23         Class<Integer> genericIntClass=int.class;
 24         genericIntClass=Integer.class;
 25         //若是將genericIntClass這個Class引用賦值給double.class的話,會因爲類型檢查失敗,是沒法編譯;
 26         //genericIntClass=double.class;
 27         //普通類的引用能夠賦值爲任何其餘的Class對象;
 28         intClass = double.class;
 29     }
 30 
 31 
 32     //可使用通配符?代替上例中的<Integer>,
 33     @Test
 34     public void Test02() {
 35         Class<?> intClass=int.class;
 36         intClass = double.class;
 37     }
 38 
 39     //若是我像建立一個Class對象的引用,並指定這個Class對象的類型爲指定類型或其子類型,則須要使用? extend XXX
 40     @Test
 41     public void Test03() {
 42         Class<? extends Number> bounded=int.class;
 43         bounded = double.class;
 44         bounded = Number.class;
 45     }
 46 
 47     //至此,能夠得出結論,使用範型語法的目的是爲了提供編譯期檢查;
 48 
 49     @Test
 50     public void Test04(){
 51         FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class);
 52         System.out.println(fl.create(15));
 53     }
 54 
 55 
 56 
 57     @Test
 58     public void Test05() throws IllegalAccessException, InstantiationException {
 59         Class<A> a=A.class;
 60         A aa=a.newInstance();
 61         //注意,這裏若是不寫成這樣會報錯
 62         Class<? super A> b=a.getSuperclass();
 63 
 64         //注意:b.newInstance返回的不是精確值,而是Object;
 65         Object bb=b.newInstance();
 66 
 67     }
 68 
 69 
 70 
 71 }
 72 
 73 class CountedInteger{
 74     private static long counter;
 75     private final long id=counter++;
 76     public String toString(){
 77         return  Long.toString(id);
 78     }
 79 }
 80 
 81 
 82 //注意,因爲這個類中使用了type.newInstance()方法,因此,必須保證T傳進來的類有默認構造方法;
 83 class FilledList<T>{
 84     private Class<T> type;
 85     public FilledList(Class<T> type){
 86         this.type=type;
 87     }
 88 
 89     public List<T> create(int nElements){
 90         List<T> result=new ArrayList<T>();
 91         try{
 92             for(int i=0;i<nElements;i++){
 93                 //注意,當在type上使用範型,newInstance()方法將產生肯定的類型;
 94                 result.add(type.newInstance());
 95             }
 96         }catch (Exception e){
 97             e.printStackTrace();
 98         }
 99         return result;
100     }
101 
102 }
103 
104 
105 
106 class A{
107 
108 }
109 
110 class B extends A{
111 
112 }
View Code

 

14.3 類型轉換前先作檢查

在類型轉換以前先作檢查,若是貿然強制轉換,可能會拋出ClassCastException異常;

學習使用instance of 和isInstance()進行類型檢查;

主要區別就是instance of是編譯器進行類型檢查;

而 isInstance方法是運行期,動態進行類型檢查,可用於反射、泛型中;

 

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 /**
 4  * @author zhaojiatao
 5  * @date 2018/9/11
 6  *
 7  * 在類型轉換以前先作檢查,若是貿然強制轉換,可能會拋出ClassCastException異常;
 8  * 學習使用instance of 和isInstance()進行類型檢查;
 9  * 主要區別就是instance of是編譯器進行類型檢查;
10  * 而 isInstance方法是運行期,動態進行類型檢查,可用於反射、泛型中;
11  *
12  */
13 
14 public class Test3 {
15     public static boolean DynamicEqual(Object fatherObj,Object sonObj){
16          return fatherObj.getClass().isInstance(sonObj); // pass
17         // return sonObj.getClass().isInstance(fatherObj);
18         // return sonObj instanceof Father; // pass
19         // return sonObj instanceof (fatherObj.getClass()); //error
20     }
21 
22     public static void main(String[] args){
23         //instance of 編譯器類型檢查
24         Father father = new Father();
25         Son son = new Son();
26 
27         System.out.println(son instanceof Son); // true
28         System.out.println(son instanceof Father); // true
29         System.out.println(son instanceof Object); // true
30         System.out.println(null instanceof Object); // false
31         System.out.println();
32 
33         //運行時動態類型檢查(括號裏的是子類)
34         System.out.println(Son.class.isInstance(son)); // true
35         //很明顯是錯誤的,但編譯是能夠經過的
36         System.out.println(Integer.class.isInstance(son));//false
37         System.out.println(Father.class.isInstance(son)); // true
38         System.out.println(Object.class.isInstance(son)); // true
39         System.out.println(Object.class.isInstance(null)); // false
40         System.out.println();
41 
42 
43         //different using
44         System.out.println(DynamicEqual(father, son));
45     }
46 
47 
48 }
49 
50 class Father{}
51 
52 class Son extends Father{}
View Code

 

14.4註冊工廠

工廠設計模式:將對象的建立工做交給類本身去完成;工廠方法能夠被多態地調用,從而爲你建立恰當類型的對象。

 

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Collections;
  5 import java.util.List;
  6 import java.util.Random;
  7 
  8 /**
  9  * @author zhaojiatao
 10  * @date 2018/9/12
 11  * 工廠方法設計模式:
 12  * 將對象的建立工做交給類本身去完成;工廠方法能夠被多態地調用,從而爲你建立恰當類型的對象。
 13  */
 14 public class Test4 {
 15 
 16     public static void main(String[] args) {
 17         for(int i=0;i<10;i++){
 18             System.out.println(Part.createRandom());
 19         }
 20     }
 21 
 22 }
 23 
 24 interface Factory<T>{
 25     T create();
 26 }
 27 
 28 
 29 class Part{
 30     public String toString(){
 31         return getClass().getSimpleName();
 32     }
 33 
 34     static List<Factory<? extends Part>> partFactories=new ArrayList<>();
 35 
 36     static {
 37         /*partFactories.add(new FuelFilter.Factory());
 38         partFactories.add(new AirFilter.Factory());
 39         partFactories.add(new CabinAirFilter.Factory());
 40         partFactories.add(new OilFilter.Factory());
 41 
 42         partFactories.add(new FanBelt.Factory());
 43         partFactories.add(new GeneratorBelt.Factory());
 44         partFactories.add(new PowerSteeringBelt.Factory());*/
 45         Collections.addAll(partFactories,new FuelFilter.Factory(),new AirFilter.Factory(),new CabinAirFilter.Factory(),
 46         new OilFilter.Factory(),new FanBelt.Factory(),new GeneratorBelt.Factory(),new PowerSteeringBelt.Factory()
 47         );
 48 
 49     }
 50 
 51     private static Random rand=new Random(47);
 52     public static Part createRandom(){
 53         int n =rand.nextInt(partFactories.size());
 54         return partFactories.get(n).create();
 55     }
 56 
 57 
 58 }
 59 
 60 
 61 class Filter extends Part{}
 62 
 63 class FuelFilter extends Filter{
 64     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FuelFilter>{
 65         @Override
 66         public FuelFilter create() {
 67             return new FuelFilter();
 68         }
 69     }
 70 }
 71 
 72 class AirFilter extends Filter{
 73     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<AirFilter>{
 74         @Override
 75         public AirFilter create() {
 76             return new AirFilter();
 77         }
 78     }
 79 }
 80 
 81 class CabinAirFilter extends Filter{
 82     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<CabinAirFilter>{
 83         @Override
 84         public CabinAirFilter create() {
 85             return new CabinAirFilter();
 86         }
 87     }
 88 }
 89 
 90 class OilFilter extends Filter{
 91     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<OilFilter>{
 92         @Override
 93         public OilFilter create() {
 94             return new OilFilter();
 95         }
 96     }
 97 }
 98 
 99 
100 class Belt extends Part{
101 
102 }
103 
104 class FanBelt extends Belt{
105     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FanBelt>{
106         @Override
107         public FanBelt create() {
108             return new FanBelt();
109         }
110     }
111 }
112 
113 class GeneratorBelt extends Belt{
114     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<GeneratorBelt>{
115         @Override
116         public GeneratorBelt create() {
117             return new GeneratorBelt();
118         }
119     }
120 }
121 
122 class PowerSteeringBelt extends Belt{
123     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<PowerSteeringBelt>{
124         @Override
125         public PowerSteeringBelt create() {
126             return new PowerSteeringBelt();
127         }
128     }
129 }
View Code

 

 14.5 instanceof 與 Class的等價性

  這一章節,其實主要就是講解instanceof、isInstance()、class對象== 三者的不一樣;

 instanceof和isInstance()是一組,結果是相同的,區別在前文已經說過,前者須要在編譯器進行類型檢查,後者只在運行時進行類型檢查;

   而比較class對象的==和equals一組,結果也是相同的,應爲equals是比較兩個class對象的內存地址是否一致;

  但綜合起來看,這兩組的結果是不同的。instance和isInstance()考慮了繼承的狀況,然後一組沒有;

 

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 /**
 4  * @author zhaojiatao
 5  * @date 2018/9/12
 6  */
 7 public class Test5 {
 8     public static void main(String[] args) {
 9         FamilyVsExactType.test(new Base());
10         FamilyVsExactType.test(new Derived());
11     }
12 }
13 
14 
15 class Base{}
16 class Derived extends Base{}
17 
18 class FamilyVsExactType {
19     static void test(Object x){
20         System.out.println("Testing x of type "+x.getClass());
21         System.out.println("x instanceof Base "+(x instanceof Base));
22         System.out.println("x instanceof Derived "+Derived.class.isInstance(x));
23         System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
24         System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
25         System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class));
26         System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class));
27         System.out.println("x.getClass().equals(Base.class) "+x.getClass().equals(Base.class));
28         System.out.println("x.getClass().equals(Derived.class) "+x.getClass().equals(Derived.class));
29     }
30 }
View Code

 

14.6 反射與內省 

14.6.1Java反射機制的適用場景及其利與弊(引用:https://blog.csdn.net/zolalad/article/details/29370565)

1、反射的適用場景是什麼?

1).Java的反射機制在作基礎框架的時候很是有用,有一句話這麼說來着:反射機制是不少Java框架的基石。而通常應用層面不多用,不過這種東西,如今不少開源框架基本都已經給你封裝好了,本身基本用不着寫。典型的除了Hibernate以外,還有Spring也用到不少反射機制。經典的就是在xml文件或者properties裏面寫好了配置,而後在Java類裏面解析xml或properties裏面的內容,獲得一個字符串,而後用反射機制,根據這個字符串得到某個類的Class實例,這樣就能夠動態配置一些東西,不用每一次都要在代碼裏面去new或者作其餘的事情,之後要改的話直接改配置文件,代碼維護起來就很方便了,同時有時候要適應某些需求,Java類裏面不必定能直接調用另外的方法,這時候也能夠經過反射機制來實現。
總的來講,本身寫的不多,具體何時要用那要看需求,反射機制無非就是根據一個String來獲得你要的實體對象,而後調用它原來的東西。可是若是是要本身寫框架的話,那就會用得比較多了。

2)當你作一個軟件能夠安裝插件的功能,你連插件的類型名稱都不知道,你怎麼實例化這個對象呢?由於程序是支持插件的(第三方的),在開發的時候並不知道 。因此沒法在代碼中 New出來 ,但反射能夠,經過反射,動態加載程序集,而後讀出類,檢查標記以後再實例化對象,就能夠得到正確的類實例。

3)在編碼階段不知道那個類名,要在運行期從配置文件讀取類名, 這時候就沒有辦法硬編碼new ClassName(),而必須用到反射才能建立這個對象.反射的目的就是爲了擴展未知的應用。好比你寫了一個程序,這個程序定義了一些接口,只要實現了這些接口的dll均可以做爲插件來插入到這個程序中。那麼怎麼實現呢?就能夠經過反射來實現。就是把dll加載進內存,而後經過反射的方式來調用dll中的方法。不少工廠模式就是使用的反射。 

2、程序員在本身的業務開發中應該儘可能的遠離反射

反射:在流行的庫如Spring和Hibernate中,反射天然有其用武之地。不過內省業務代碼在不少時候都不是一件好事,緣由有不少,通常狀況下我老是建議你們不要使用反射。

首先是代碼可讀性與工具支持。打開熟悉的IDE,尋找你的Java代碼的內部依賴,很容易吧。如今,使用反射來替換掉你的代碼而後再試一下,結果如何呢?若是經過反射來修改已經封裝好的對象狀態,那麼結果將會變得更加不可控。請看看以下示例代碼:

 

若是這樣作就沒法獲得編譯期的安全保證。就像上面這個示例同樣,你會發現若是getDeclaredField()方法調用的參數輸錯了,那麼只有在運行期才能發現。要知道的是,尋找運行期Bug的難度要遠遠超過編譯期的Bug。

最後還要談談代價問題。JIT對反射的優化程度是不一樣的,有些優化時間會更長一些,而有些甚至是沒法應用優化。所以,有時反射的性能損失能夠達到幾個數量級的差異。不過在典型的業務應用中,你可能不會注意到這個代價。

總結一下,我以爲在業務代碼中惟一合理(直接)使用反射的場景是經過AOP。除此以外,你最好遠離反射這一特性。

3、性能分析

反射機制是一種程序自我分析的能力。用於獲取一個類的類變量,構造函數,方法,修飾符。

優勢:運行期類型的判斷,動態類加載,動態代理使用反射。

缺點:性能是一個問題,反射至關於一系列解釋操做,通知jvm要作的事情,性能比直接的java代碼要慢不少。

 

14.6.2反射的一些經常使用操做:參考:https://blog.csdn.net/sinat_38259539/article/details/71799078

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.junit.Test;
  4 
  5 import java.io.InputStream;
  6 import java.lang.reflect.Constructor;
  7 import java.lang.reflect.Field;
  8 import java.lang.reflect.InvocationTargetException;
  9 import java.lang.reflect.Method;
 10 import java.util.ArrayList;
 11 import java.util.Properties;
 12 
 13 /**
 14  * @author zhaojiatao
 15  * @date 2018/9/13
 16  *
 17  * 反射的應用場景:https://blog.csdn.net/zolalad/article/details/29370565
 18  * 反射的一些經常使用操做:參考:https://blog.csdn.net/sinat_38259539/article/details/71799078
 19  *
 20  */
 21 class Student {
 22 
 23     //---------------構造方法-------------------
 24     //(默認的構造方法)
 25     Student(String str){
 26         System.out.println("(默認)的構造方法 s = " + str);
 27     }
 28 
 29     //無參構造方法
 30     public Student(){
 31         System.out.println("調用了公有、無參構造方法執行了。。。");
 32     }
 33 
 34     //有一個參數的構造方法
 35     public Student(char name){
 36         System.out.println("姓名:" + name);
 37     }
 38 
 39     //有多個參數的構造方法
 40     public Student(String name ,int age){
 41         System.out.println("姓名:"+name+"年齡:"+ age);//這的執行效率有問題,之後解決。
 42     }
 43 
 44     //受保護的構造方法
 45     protected Student(boolean n){
 46         System.out.println("受保護的構造方法 n = " + n);
 47     }
 48 
 49     //私有構造方法
 50     private Student(int age){
 51         System.out.println("私有的構造方法   年齡:"+ age);
 52     }
 53 
 54 
 55     //**********字段*************//
 56     public String name;
 57     protected int age;
 58     char sex;
 59     private String phoneNum;
 60 
 61 
 62 
 63 
 64     //**************成員方法***************//
 65     public void show1(String s){
 66         System.out.println("調用了:公有的,String參數的show1(): s = " + s);
 67     }
 68     protected void show2(){
 69         System.out.println("調用了:受保護的,無參的show2()");
 70     }
 71     void show3(){
 72         System.out.println("調用了:默認的,無參的show3()");
 73     }
 74     private String show4(int age){
 75         System.out.println("調用了,私有的,而且有返回值的,int參數的show4(): age = " + age);
 76         return "abcd";
 77     }
 78 
 79 
 80 
 81     @Override
 82     public String toString() {
 83         return "Student [name=" + name + ", age=" + age + ", sex=" + sex
 84                 + ", phoneNum=" + phoneNum + "]";
 85     }
 86 
 87 
 88     public static void main(String[] args) {
 89         System.out.println("main方法執行了。。。");
 90     }
 91 
 92     public void show(){
 93         System.out.println("is show()");
 94     }
 95 
 96 
 97 
 98 
 99 }
100 
101 
102 
103 
104 public class Test6 {
105     /*
106      * 經過Class對象能夠獲取某個類中的:構造方法、成員變量、成員方法;並訪問成員;
107      *
108      * 1.獲取構造方法:
109      *         1).批量的方法:
110      *             public Constructor[] getConstructors():全部"公有的"構造方法
111                 public Constructor[] getDeclaredConstructors():獲取全部的構造方法(包括私有、受保護、默認、公有)
112 
113      *         2).獲取單個的方法,並調用:
114      *             public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
115      *             public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"能夠是私有的,或受保護、默認、公有;
116      *
117      *             調用構造方法:
118      *             Constructor-->newInstance(Object... initargs)
119      *
120      * 2.設置字段的值:
121      *         Field --> public void set(Object obj,Object value):
122      *         參數說明:
123      *         1.obj:要設置的字段所在的對象;
124      *         2.value:要爲字段設置的值;
125      *
126      *
127      */
128 
129 
130 
131     //經過反射獲取構造方法並使用
132     @Test
133     public void test01() {
134 
135         //1.加載Class對象
136         Class clazz = null;
137         try {
138             clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
139         } catch (ClassNotFoundException e) {
140             e.printStackTrace();
141         }
142 
143         //2.獲取全部公有構造方法
144             System.out.println("**********************全部公有構造方法*********************************");
145             //Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
146             Constructor[] conArray = clazz.getConstructors();
147             for(Constructor c : conArray){
148                 System.out.println(c);
149             }
150 
151             System.out.println("************全部的構造方法(包括:私有、受保護、默認、公有)***************");
152             //Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.
153             conArray = clazz.getDeclaredConstructors();
154             for(Constructor c : conArray){
155                 System.out.println(c);
156             }
157 
158             System.out.println("*****************獲取公有、無參的構造方法*******************************");
159             Constructor con = null;
160             try {
161                 con = clazz.getConstructor(null);
162             } catch (NoSuchMethodException e) {
163                 e.printStackTrace();
164             }
165             //1>、由於是無參的構造方法因此類型是一個null,不寫也能夠:這裏須要的是一個參數的類型,切記是類型
166             //2>、返回的是描述這個無參構造函數的類對象。
167 
168             System.out.println("con = " + con);
169             //調用構造方法
170             Object obj = null;
171             try {
172                 obj = con.newInstance();
173             } catch (InstantiationException e) {
174                 e.printStackTrace();
175             } catch (IllegalAccessException e) {
176                 e.printStackTrace();
177             } catch (InvocationTargetException e) {
178                 e.printStackTrace();
179             }
180             System.out.println("obj = " + obj);
181             Student stu = (Student)obj;
182             System.out.println("使用無參構造方法建立的Student對象的實例:stu="+stu.toString());
183 
184             System.out.println("******************獲取私有構造方法,並調用*******************************");
185             try {
186                 con = clazz.getDeclaredConstructor(char.class);
187             } catch (NoSuchMethodException e) {
188                 e.printStackTrace();
189             }
190             System.out.println(con);
191             //調用構造方法
192             con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
193             try {
194                 obj = con.newInstance('男');
195             } catch (InstantiationException e) {
196                 e.printStackTrace();
197             } catch (IllegalAccessException e) {
198                 e.printStackTrace();
199             } catch (InvocationTargetException e) {
200                 e.printStackTrace();
201             }
202 
203             System.out.println("obj = " + obj);
204             stu = (Student)obj;
205             System.out.println("使用私有構造方法建立的Student對象的實例:stu="+stu.toString());
206 
207     }
208 
209 
210 
211 
212     //獲取成員變量並調用
213     @Test
214     public void test02() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
215             InvocationTargetException, InstantiationException {
216         //1.獲取Class對象
217         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
218         //2.獲取字段
219         System.out.println("************獲取全部公有的字段********************");
220         Field[] fieldArray = stuClass.getFields();
221         for(Field f : fieldArray){
222             System.out.println(f);
223         }
224         System.out.println("************獲取全部的字段(包括私有、受保護、默認的)********************");
225         fieldArray = stuClass.getDeclaredFields();
226         for(Field f : fieldArray){
227             System.out.println(f);
228         }
229         System.out.println("*************獲取公有字段xx並調用***********************************");
230         Field f = stuClass.getField("name");
231         System.out.println(f);
232         //獲取一個對象
233         Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
234         //爲字段設置值
235         f.set(obj, "劉德華");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
236         //驗證
237         Student stu = (Student)obj;
238         System.out.println("驗證姓名:" + stu.name);
239 
240 
241         System.out.println("**************獲取私有字段****並調用********************************");
242         f = stuClass.getDeclaredField("phoneNum");
243         System.out.println(f);
244         f.setAccessible(true);//暴力反射,解除私有限定
245         f.set(obj, "18888889999");
246         System.out.println("驗證電話:" + stu);
247 
248     }
249 
250 
251 
252     //獲取成員方法並調用
253     @Test
254     public void test03() throws Exception {
255 
256         //1.獲取Class對象
257         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
258         //2.獲取全部公有方法
259         System.out.println("***************獲取全部的」公有「方法*******************");
260         stuClass.getMethods();
261         Method[] methodArray = stuClass.getMethods();
262         for(Method m : methodArray){
263             System.out.println(m);
264         }
265         System.out.println("***************獲取全部的方法,包括私有的*******************");
266         methodArray = stuClass.getDeclaredMethods();
267         for(Method m : methodArray){
268             System.out.println(m);
269         }
270         System.out.println("***************獲取公有的show1()方法*******************");
271         Method m = stuClass.getMethod("show1", String.class);
272         System.out.println(m);
273         //實例化一個Student對象
274         Object obj = stuClass.getConstructor().newInstance();
275         m.invoke(obj, "劉德華");
276 
277         System.out.println("***************獲取私有的show4()方法******************");
278         m = stuClass.getDeclaredMethod("show4", int.class);
279         System.out.println(m);
280         m.setAccessible(true);//解除私有限定
281         Object result = m.invoke(obj, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
282         System.out.println("返回值:" + result);
283 
284 
285 
286 
287     }
288 
289 
290     //反射main方法
291     @Test
292     public void test04(){
293         try {
294             //一、獲取Student對象的字節碼
295             Class clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
296 
297             //二、獲取main方法
298             Method methodMain = clazz.getMethod("main", String[].class);//第一個參數:方法名稱,第二個參數:方法形參的類型,
299             //三、調用main方法
300             // methodMain.invoke(null, new String[]{"a","b","c"});
301             //第一個參數,對象類型,由於方法是static靜態的,因此爲null能夠,第二個參數是String數組,這裏要注意在jdk1.4時是數組,jdk1.5以後是可變參數
302             //這裏拆的時候將  new String[]{"a","b","c"} 拆成3個對象。。。因此須要將它強轉。
303             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
304             //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
305 
306         } catch (Exception e) {
307             e.printStackTrace();
308         }
309 
310     }
311 
312 
313 
314     //反射方法的其它使用之---經過反射運行配置文件內容
315     @Test
316     public void test05() throws Exception {
317         Properties pro = new Properties();
318         {//此方式要求   配置文件在 src 文件夾 內
319 
320             //類名.class.getClassLoader().getResourceAsStream("文件名")
321             InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("pro.properties");
322             pro.load(inStream);
323             inStream.close();
324         }
325 
326         //經過反射獲取Class對象
327         Class stuClass = Class.forName(pro.getProperty("className"));//"cn.fanshe.Student"
328         //2獲取show()方法
329         Method m = stuClass.getMethod(pro.getProperty("methodName"));//show
330         //3.調用show()方法
331         m.invoke(stuClass.getConstructor().newInstance());
332 
333     }
334 
335 
336 
337     //反射方法的其它使用之---經過反射越過泛型檢查
338     @Test
339     public void test06() throws Exception{
340         ArrayList<String> strList = new ArrayList<String>();
341         strList.add("aaa");
342         strList.add("bbb");
343 
344         //    strList.add(100);
345         //獲取ArrayList的Class對象,反向的調用add()方法,添加數據
346         Class listClass = strList.getClass(); //獲得 strList 對象的字節碼 對象
347         //獲取add()方法
348         Method m = listClass.getMethod("add", Object.class);
349         //調用add()方法
350         m.invoke(strList, 100);
351 
352         //遍歷集合
353         for(Object obj : strList){
354             System.out.println(obj);
355         }
356 
357     }
358 
359 
360 
361 
362 
363 }
View Code

 

 

 

14.6.3 內省(參考:http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html)

內省(Introspector) 是Java 語言對 JavaBean 類屬性、事件的一種缺省處理方法。

  JavaBean是一種特殊的類,主要用於傳遞數據信息,這種類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則。若是在兩個模塊之間傳遞信息,能夠將信息封裝進JavaBean中,這種對象稱爲「值對象」(Value Object),或「VO」。方法比較少。這些信息儲存在類的私有變量中,經過set()、get()得到。

何時會用到內省?

在框架設計的時候使用比較多,像mybatis中類與表的映射關係的類的屬性與表的列一一對應,框架在賦值和獲取值得時候會調用對應的setter和getter方法;

例如spring 中初始化bean的時候取藥用到反射的方式實例化bean,在set值的時候會根據變量名得到settter方法,把配置好的值set進去;

 

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.apache.commons.beanutils.BeanUtils;
  4 import org.apache.commons.beanutils.PropertyUtils;
  5 import org.junit.Test;
  6 
  7 import java.beans.BeanInfo;
  8 import java.beans.IntrospectionException;
  9 import java.beans.Introspector;
 10 import java.beans.PropertyDescriptor;
 11 import java.lang.reflect.InvocationTargetException;
 12 import java.lang.reflect.Method;
 13 
 14 /**
 15  * @author zhaojiatao
 16  * @date 2018/9/13
 17  *
 18  * 內省:
 19  * http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html
 20  * https://blog.csdn.net/u010445297/article/details/60967146
 21  *
 22  */
 23 public class Test6_Introspector {
 24 
 25     public static void main(String[] args) {
 26         UserInfo userInfo=new UserInfo();
 27         userInfo.setUserName("peida");
 28         try {
 29             BeanInfoUtil.getProperty(userInfo, "userName");
 30 
 31             BeanInfoUtil.setProperty(userInfo, "userName");
 32 
 33             BeanInfoUtil.getProperty(userInfo, "userName");
 34 
 35             BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");
 36 
 37             BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
 38 
 39             BeanInfoUtil.setProperty(userInfo, "age");
 40             //說明:BeanInfoUtil.setProperty(userInfo, "age");報錯是應爲age屬性是int數據類型,
 41             //而setProperty方法裏面默認給age屬性賦的值是String類型。因此會爆出argument type mismatch參數類型不匹配的錯誤信息。
 42 
 43         } catch (Exception e) {
 44             // TODO Auto-generated catch block
 45             e.printStackTrace();
 46         }
 47 
 48     }
 49 
 50 
 51 
 52     @Test
 53     public void testBeanUtils(){
 54         UserInfo userInfo=new UserInfo();
 55         try {
 56             BeanUtils.setProperty(userInfo, "userName", "peida");
 57 
 58             System.out.println("set userName:"+userInfo.getUserName());
 59 
 60             System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
 61 
 62             BeanUtils.setProperty(userInfo, "age", 18);
 63             System.out.println("set age:"+userInfo.getAge());
 64 
 65             System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
 66 
 67             System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
 68             System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
 69 
 70             PropertyUtils.setProperty(userInfo, "age", 8);
 71             System.out.println(PropertyUtils.getProperty(userInfo, "age"));
 72 
 73             System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
 74 
 75             PropertyUtils.setProperty(userInfo, "age", "8");
 76         }
 77         catch (IllegalAccessException e) {
 78             e.printStackTrace();
 79         }
 80         catch (InvocationTargetException e) {
 81             e.printStackTrace();
 82         }
 83         catch (NoSuchMethodException e) {
 84             e.printStackTrace();
 85         }
 86     }
 87 
 88 
 89 
 90 }
 91 
 92 
 93 class BeanInfoUtil {
 94 
 95     public static void setProperty(UserInfo userInfo,String userName){
 96         PropertyDescriptor propDesc= null;
 97         try {
 98             propDesc = new PropertyDescriptor(userName,UserInfo.class);
 99         } catch (IntrospectionException e) {
100             e.printStackTrace();
101         }
102         Method methodSetUserName=propDesc.getWriteMethod();
103         try {
104             methodSetUserName.invoke(userInfo, "wong");
105         } catch (IllegalAccessException e) {
106             e.printStackTrace();
107         } catch (InvocationTargetException e) {
108             e.printStackTrace();
109         }
110         System.out.println("set userName:"+userInfo.getUserName());
111     }
112 
113     public static void getProperty(UserInfo userInfo,String userName){
114         PropertyDescriptor proDescriptor = null;
115         try {
116             proDescriptor = new PropertyDescriptor(userName,UserInfo.class);
117         } catch (IntrospectionException e) {
118             e.printStackTrace();
119         }
120         Method methodGetUserName=proDescriptor.getReadMethod();
121         Object objUserName= null;
122         try {
123             objUserName = methodGetUserName.invoke(userInfo);
124         } catch (IllegalAccessException e) {
125             e.printStackTrace();
126         } catch (InvocationTargetException e) {
127             e.printStackTrace();
128         }
129         System.out.println("get userName:"+objUserName.toString());
130     }
131 
132     public static void setPropertyByIntrospector(UserInfo userInfo,String userName){
133         BeanInfo beanInfo= null;
134         try {
135             beanInfo = Introspector.getBeanInfo(UserInfo.class);
136         } catch (IntrospectionException e) {
137             e.printStackTrace();
138         }
139         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
140         if(proDescrtptors!=null&&proDescrtptors.length>0){
141             for(PropertyDescriptor propDesc:proDescrtptors){
142                 if(propDesc.getName().equals(userName)){
143                     Method methodSetUserName=propDesc.getWriteMethod();
144                     try {
145                         methodSetUserName.invoke(userInfo, "alan");
146                     } catch (IllegalAccessException e) {
147                         e.printStackTrace();
148                     } catch (InvocationTargetException e) {
149                         e.printStackTrace();
150                     }
151                     System.out.println("set userName:"+userInfo.getUserName());
152                     break;
153                 }
154             }
155         }
156     }
157 
158     public static void getPropertyByIntrospector(UserInfo userInfo,String userName){
159         BeanInfo beanInfo= null;
160         try {
161             beanInfo = Introspector.getBeanInfo(UserInfo.class);
162         } catch (IntrospectionException e) {
163             e.printStackTrace();
164         }
165         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
166         if(proDescrtptors!=null&&proDescrtptors.length>0){
167             for(PropertyDescriptor propDesc:proDescrtptors){
168                 if(propDesc.getName().equals(userName)){
169                     Method methodGetUserName=propDesc.getReadMethod();
170                     Object objUserName= null;
171                     try {
172                         objUserName = methodGetUserName.invoke(userInfo);
173                     } catch (IllegalAccessException e) {
174                         e.printStackTrace();
175                     } catch (InvocationTargetException e) {
176                         e.printStackTrace();
177                     }
178                     System.out.println("get userName:"+objUserName.toString());
179                     break;
180                 }
181             }
182         }
183     }
184 }
185 
186 
187 
188 class UserInfo {
189 
190     private long userId;
191     private String userName;
192     private int age;
193     private String emailAddress;
194 
195     public long getUserId() {
196         return userId;
197     }
198     public void setUserId(long userId) {
199         this.userId = userId;
200     }
201     public String getUserName() {
202         return userName;
203     }
204     public void setUserName(String userName) {
205         this.userName = userName;
206     }
207     public int getAge() {
208         return age;
209     }
210     public void setAge(int age) {
211         this.age = age;
212     }
213     public String getEmailAddress() {
214         return emailAddress;
215     }
216     public void setEmailAddress(String emailAddress) {
217         this.emailAddress = emailAddress;
218     }
219 
220 }
View Code

 

14.7 jdk動態代理(參考:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html)

jdk的動態代理依賴接口,有侷限性;底層是經過反射,執行method;

相關文章
相關標籤/搜索