java反射(二)--反射應用案例


一.反射實例化對象

  經過一系列的分析之后發現雖然可以獲取Class類的實例化對象,但是依然覺得這個對象的獲取意義不是很大,因此可以通過以下幾個案例去理解反射的核心意義
--反射實例化對象:獲取Class對象之后最大的意義並不是在於只是一個對象的實例化操作形式,更重要的是Class類中提供有一個對象的反射實例化方法,在JDK1.9之前的實例化:public T newInstance() throw InstantiationException,IllegalAccessException,該方法代替了new 關鍵字的使用,但是在JDK1.9之后則發生了變化:class.getDeclaredConstructor().newInstance();
--范例:通過newInstance()方法實例化對象

 1 package 反射.認識反射機制.entity;  2 
 3 /**
 4  * @author : S K Y  5  * @version :0.0.1  6  */
 7 public class Person {  8     public Person() {   //任何情況下只要實例化對象則一定要調用類中的構造方法
 9         System.out.println("Person對象實例化了"); 10  } 11 
12  @Override 13     public String toString() { 14         return "我是一個好人"; 15  } 16 }
1 public class Demo { 2     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { 3         Class<?> aClass = Class.forName("反射.認識反射機制.entity.Person"); 4         Object o = aClass.newInstance();    //實例化對象
5  System.out.println(o); 6  } 7 }

--運行結果

Person對象實例化了 我是一個好人 Process finished with exit code 0

--現在通過反射實現的對象實例化處理,依然要調用類中的無參構造方法,其本質等價於new 關鍵字的使用,但是該方法在JDK1.9之后被替代了,因為默認的Class類中的newInstance()方法只能夠調用無參構造,所以很多的開發者認為其描述的不准確,於是將其變換了形式(后續會說明)

二.反射與工廠設計模式
  如果要想進行對象的實例化處理除了可以使用關鍵字new 之外,還可以挺過反射機制來完成.那么思考一個問題:為什么要提供有一個反射的實例化?到底是使用關鍵字new還是使用反射進行對象實例化呢?
--如果想要更好的解決此類問題,最好的解釋方案就是通過工廠設計模式來解決.工廠設計模式的最大特點:客戶端的程序類不直接牽扯到對象的實例化管理,只與接口發生關聯,通過工廠了獲取接口的實例化對象,傳統的工廠設計模式:

 1 interface Message{  2     public void send();     //消息發送
 3 }  4 class NetMessage implements Message{        //網絡消息實現類
 5  @Override  6     public void send() {  7         System.out.println("發送網絡消息");  8  }  9 } 10 public class FactoryDemo { 11     public static void main(String[] args) { 12         Message message = new NetMessage(); //如果直接實例化則一定會有耦合問題
13  } 14 }

 

 

--在實際的開發中,接口的主要作用是為不同的層提供有一個操作的標准.但是此時如果直接將一個子類設置為接口實例化操作,那么一定會有耦合問題,所以使用了工廠設計模式來解決此問題.
--范例:傳統的工廠設計模式

 1 interface Message {  2     public void send();     //消息發送
 3 }  4 
 5 class NetMessage implements Message {        //網絡消息實現類
 6  @Override  7     public void send() {  8         System.out.println("發送網絡消息");  9  } 10 } 11 
12 class Factory { 13     private Factory() { 14     }     //沒有產生實例化對象的意義
15 
16     public static Message getInstance(String className) { 17         if ("NetMessage".equals(className)) { 18             return new NetMessage(); 19  } 20         return null; 21  } 22 } 23 
24 public class FactoryDemo { 25     public static void main(String[] args) { 26         Message message = Factory.getInstance("NetMessage"); 27  message.send(); 28  } 29 }

--此種工廠設計模式屬於靜態工廠設計模式,此時如果追加一個子類,那么工廠類就需要進行相應的修改(追加相應的判斷語句),否則無法獲得新的子類的實例化對象.工廠設模式最有效解決的是子類與客戶端的耦合問題,但是解決的核心思想是在於提供有一個工廠類作為過渡端,可是隨着項目的進行,Message接口可能會有更多的子類,而且隨着時間的推移,子類會越來越多,因此工廠類永遠都需要修改,並且永無停止之日.
--此時最好的解決方案就是不使用關鍵字new來完成對象的實例化,因為關鍵字new在使用的時候需要有一個明確的類存在.而newInstance()的方法只需要有一個明確表示類名稱的字符串即可應用:

 1 interface Message {  2     public void send();     //消息發送
 3 }  4 
 5 class NetMessage implements Message {        //網絡消息實現類
 6  @Override  7     public void send() {  8         System.out.println("發送網絡消息");  9  } 10 } 11 
12 class Factory { 13     private Factory() { 14     }     //沒有產生實例化對象的意義
15 
16     public static Message getInstance(String className) throws Exception { 17         return (Message) Class.forName(className).newInstance(); 18  } 19 } 20 
21 public class FactoryDemo { 22     public static void main(String[] args) throws Exception { 23         Message message = Factory.getInstance("反射.反射應用案例.NetMessage"); 24  message.send(); 25  } 26 }

--此時如果對子類繼續進行擴充的話,是沒有必要修改工廠類的.利用反射機制實現的工廠設計模式,最大的優勢在於,對於接口的子類的擴充,將不再影響到工廠類的定義.但是現在依然需要進行思考,在實際的項目開發之中,有可能會存在大量的接口,並且這些接口可能都需要通過工廠類來實例化對象,所以此時的工廠設計模式不應該只為一個Message接口服務,而應該變為為所有的接口服務(使用泛型實現開發需求): 

 1 interface Service {  2     public void service();  3 }  4 
 5 class HouseService implements Service {  6  @Override  7     public void service() {  8         System.out.println("為您的住房提供服務.");  9  } 10 } 11 
12 interface Message { 13     public void send();     //消息發送
14 } 15 
16 class NetMessage implements Message {        //網絡消息實現類
17  @Override 18     public void send() { 19         System.out.println("發送網絡消息"); 20  } 21 } 22 
23 class Factory { 24     private Factory() { 25     }     //沒有產生實例化對象的意義
26 
27     /**
28  * 獲取接口實例化對象 29  * 30  * @param className 接口的子類 31  * @param tClass 描述的是一個接口的類型 32  * @return 如果子類存在則返回指定接口 33  * @throws Exception 34      */
35     public static <T> T getInstance(String className, Class<T> tClass) throws Exception { 36         return tClass.cast(Class.forName(className).newInstance()); 37  } 38 } 39 
40 public class FactoryDemo { 41     public static void main(String[] args) throws Exception { 42         Message message = Factory.getInstance("反射.反射應用案例.NetMessage",Message.class); 43  message.send(); 44         Service instance = Factory.getInstance("反射.反射應用案例.HouseService", Service.class); 45  instance.service(); 46  } 47 }

--此時的工廠設計模式才是所謂的高可用的工廠設計模式,而這種操作的實現依賴的就是泛型.此時的工廠設計模式將不再受限於指定的接口,可以為所有的接口提供實例化對象.

三.反射與單例設計模式
  單例設計模式的核心本質在於類內部的構造方法私有化,在類的內部產生實例化對象之后在外部通過static方法獲取到實例化對象進行類中的結構調用.單例設計模式一共有兩種,懶漢式和餓漢式(餓漢式的單例是不再本次的討論范圍之內的,主要討論懶漢式的單例)
--范例:觀察懶漢式單例的問題

 1 class Singleton {  2     private static Singleton instance = null;  3 
 4     private Singleton() {  5  }  6 
 7     public static Singleton getInstance() {  8         if (instance == null) {  9             instance = new Singleton(); 10  } 11         return instance; 12  } 13 
14     public void print() { 15         System.out.println("單例模式加載"); 16  } 17 
18 } 19 
20 public class LazyLoadDemo { 21     public static void main(String[] args) { 22         Singleton singleton = Singleton.getInstance(); 23  singleton.print(); 24  } 25 }

--此時我們的操作是在單線程的環境下運行的,如果使用多線程

 1 class Singleton {  2     private static Singleton instance = null;  3 
 4     private Singleton() {  5         System.out.println(Thread.currentThread().getName() + " 實例化單例對象");  6  }  7 
 8     public static Singleton getInstance() {  9         if (instance == null) { 10             instance = new Singleton(); 11  } 12         return instance; 13  } 14 
15     public void print() { 16         System.out.println("單例模式加載"); 17  } 18 
19 } 20 
21 public class LazyLoadDemo { 22     public static void main(String[] args) { 23         for (int i = 0; i < 3; i++) { 24             new Thread(() -> { 25  Singleton.getInstance(); 26             }, "[單例創建者" + (i + 1) + "]").start(); 27  } 28  } 29 }

--運行結果

[單例創建者1] 實例化單例對象 [單例創建者2] 實例化單例對象 [單例創建者3] 實例化單例對象 Process finished with exit code 0

--單例設計模式最大的特點是在整體運行之中,只允許產生一個實例化對象,當有了若干實例化對象之后,那么就不是單例設計模式了,我們可以大致分析單例模式的運行流程如下:
  1.判斷instance是否為空?
  2.如果instance為空,實例化instance對象
  3.返回當前的instance
--因此在多線程的設計中,每一個線程在執行步驟1的時候都會認為此時的對象為空,那么都會去創建這個對象的實例,這樣一來單例設計模式也就失去了意義,如果想要解決這類問題,關鍵的核心就在於要解決同步處理,而解決同步處理的核心就是使用synchronized關鍵字

 1 class Singleton {  2     private static Singleton instance = null;  3 
 4     private Singleton() {  5         System.out.println(Thread.currentThread().getName() + " 實例化單例對象");  6  }  7 
 8     public static synchronized Singleton getInstance() {  9         if (instance == null) { 10             instance = new Singleton(); 11  } 12         return instance; 13  } 14 
15     public void print() { 16         System.out.println("單例模式加載"); 17  } 18 
19 } 20 
21 public class LazyLoadDemo { 22     public static void main(String[] args) { 23         for (int i = 0; i < 3; i++) { 24             new Thread(() -> { 25  Singleton.getInstance(); 26             }, "[單例創建者" + (i + 1) + "]").start(); 27  } 28  } 29 }

--運行結果

[單例創建者1] 實例化單例對象 Process finished with exit code 0

--此時卻是進行了同步處理,但是這個同步的代價卻是很大的,因為效率會降低.因為整體代碼中實際上只有一塊區域需要同步處理,那就是instance對象的實例化處理部分,在這樣的情況下同步加的未免顯得有些草率,更加合理的進行同步處理:

 1 class Singleton {  2     //在對象實例化的時候,應該立刻與主內存中的實例對象保持同步,而不應該存在副本
 3     private static volatile Singleton instance = null;  4 
 5     private Singleton() {  6         System.out.println(Thread.currentThread().getName() + " 實例化單例對象");  7  }  8 
 9     public static Singleton getInstance() { 10         synchronized (Singleton.class) { //static方法只能使用Singleton.class
11             if (instance == null) { 12                 instance = new Singleton(); 13  } 14  } 15         return instance; 16  } 17 
18     public void print() { 19         System.out.println("單例模式加載"); 20  } 21 
22 } 23 
24 public class LazyLoadDemo { 25     public static void main(String[] args) { 26         for (int i = 0; i < 3; i++) { 27             new Thread(() -> { 28  Singleton.getInstance(); 29             }, "[單例創建者" + (i + 1) + "]").start(); 30  } 31  } 32 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM