java 反射应用_java反射(二)--反射应用案例
一.反射實(shí)例化對象
經(jīng)過一系列的分析之后發(fā)現(xiàn)雖然可以獲取Class類的實(shí)例化對象,但是依然覺得這個對象的獲取意義不是很大,因此可以通過以下幾個案例去理解反射的核心意義
--反射實(shí)例化對象:獲取Class對象之后最大的意義并不是在于只是一個對象的實(shí)例化操作形式,更重要的是Class類中提供有一個對象的反射實(shí)例化方法,在JDK1.9之前的實(shí)例化:public T newInstance() throw InstantiationException,IllegalAccessException,該方法代替了new 關(guān)鍵字的使用,但是在JDK1.9之后則發(fā)生了變化:class.getDeclaredConstructor().newInstance();
--范例:通過newInstance()方法實(shí)例化對象
1 package反射.認(rèn)識反射機(jī)制.entity;2
3 /**
4 *@author: S K Y5 *@version:0.0.16 */
7 public classPerson {8 public Person() { //任何情況下只要實(shí)例化對象則一定要調(diào)用類中的構(gòu)造方法
9 System.out.println("Person對象實(shí)例化了");10 }11
12 @Override13 publicString toString() {14 return "我是一個好人";15 }16 }
1 public classDemo {2 public static void main(String[] args) throwsClassNotFoundException, IllegalAccessException, InstantiationException {3 Class> aClass = Class.forName("反射.認(rèn)識反射機(jī)制.entity.Person");4 Object o = aClass.newInstance(); //實(shí)例化對象
5 System.out.println(o);6 }7 }
--運(yùn)行結(jié)果
Person對象實(shí)例化了
我是一個好人
Process finished with exit code0
--現(xiàn)在通過反射實(shí)現(xiàn)的對象實(shí)例化處理,依然要調(diào)用類中的無參構(gòu)造方法,其本質(zhì)等價于new 關(guān)鍵字的使用,但是該方法在JDK1.9之后被替代了,因為默認(rèn)的Class類中的newInstance()方法只能夠調(diào)用無參構(gòu)造,所以很多的開發(fā)者認(rèn)為其描述的不準(zhǔn)確,于是將其變換了形式(后續(xù)會說明)
二.反射與工廠設(shè)計模式
如果要想進(jìn)行對象的實(shí)例化處理除了可以使用關(guān)鍵字new 之外,還可以挺過反射機(jī)制來完成.那么思考一個問題:為什么要提供有一個反射的實(shí)例化?到底是使用關(guān)鍵字new還是使用反射進(jìn)行對象實(shí)例化呢?
--如果想要更好的解決此類問題,最好的解釋方案就是通過工廠設(shè)計模式來解決.工廠設(shè)計模式的最大特點(diǎn):客戶端的程序類不直接牽扯到對象的實(shí)例化管理,只與接口發(fā)生關(guān)聯(lián),通過工廠了獲取接口的實(shí)例化對象,傳統(tǒng)的工廠設(shè)計模式:
1 interfaceMessage{2 public void send(); //消息發(fā)送
3 }4 class NetMessage implements Message{ //網(wǎng)絡(luò)消息實(shí)現(xiàn)類
5 @Override6 public voidsend() {7 System.out.println("發(fā)送網(wǎng)絡(luò)消息");8 }9 }10 public classFactoryDemo {11 public static voidmain(String[] args) {12 Message message = new NetMessage(); //如果直接實(shí)例化則一定會有耦合問題
13 }14 }
--在實(shí)際的開發(fā)中,接口的主要作用是為不同的層提供有一個操作的標(biāo)準(zhǔn).但是此時如果直接將一個子類設(shè)置為接口實(shí)例化操作,那么一定會有耦合問題,所以使用了工廠設(shè)計模式來解決此問題.
--范例:傳統(tǒng)的工廠設(shè)計模式
1 interfaceMessage {2 public void send(); //消息發(fā)送
3 }4
5 class NetMessage implements Message { //網(wǎng)絡(luò)消息實(shí)現(xiàn)類
6 @Override7 public voidsend() {8 System.out.println("發(fā)送網(wǎng)絡(luò)消息");9 }10 }11
12 classFactory {13 privateFactory() {14 } //沒有產(chǎn)生實(shí)例化對象的意義
15
16 public staticMessage getInstance(String className) {17 if ("NetMessage".equals(className)) {18 return newNetMessage();19 }20 return null;21 }22 }23
24 public classFactoryDemo {25 public static voidmain(String[] args) {26 Message message = Factory.getInstance("NetMessage");27 message.send();28 }29 }
--此種工廠設(shè)計模式屬于靜態(tài)工廠設(shè)計模式,此時如果追加一個子類,那么工廠類就需要進(jìn)行相應(yīng)的修改(追加相應(yīng)的判斷語句),否則無法獲得新的子類的實(shí)例化對象.工廠設(shè)模式最有效解決的是子類與客戶端的耦合問題,但是解決的核心思想是在于提供有一個工廠類作為過渡端,可是隨著項目的進(jìn)行,Message接口可能會有更多的子類,而且隨著時間的推移,子類會越來越多,因此工廠類永遠(yuǎn)都需要修改,并且永無停止之日.
--此時最好的解決方案就是不使用關(guān)鍵字new來完成對象的實(shí)例化,因為關(guān)鍵字new在使用的時候需要有一個明確的類存在.而newInstance()的方法只需要有一個明確表示類名稱的字符串即可應(yīng)用:
1 interfaceMessage {2 public void send(); //消息發(fā)送
3 }4
5 class NetMessage implements Message { //網(wǎng)絡(luò)消息實(shí)現(xiàn)類
6 @Override7 public voidsend() {8 System.out.println("發(fā)送網(wǎng)絡(luò)消息");9 }10 }11
12 classFactory {13 privateFactory() {14 } //沒有產(chǎn)生實(shí)例化對象的意義
15
16 public static Message getInstance(String className) throwsException {17 return(Message) Class.forName(className).newInstance();18 }19 }20
21 public classFactoryDemo {22 public static void main(String[] args) throwsException {23 Message message = Factory.getInstance("反射.反射應(yīng)用案例.NetMessage");24 message.send();25 }26 }
--此時如果對子類繼續(xù)進(jìn)行擴(kuò)充的話,是沒有必要修改工廠類的.利用反射機(jī)制實(shí)現(xiàn)的工廠設(shè)計模式,最大的優(yōu)勢在于,對于接口的子類的擴(kuò)充,將不再影響到工廠類的定義.但是現(xiàn)在依然需要進(jìn)行思考,在實(shí)際的項目開發(fā)之中,有可能會存在大量的接口,并且這些接口可能都需要通過工廠類來實(shí)例化對象,所以此時的工廠設(shè)計模式不應(yīng)該只為一個Message接口服務(wù),而應(yīng)該變?yōu)闉樗械慕涌诜?wù)(使用泛型實(shí)現(xiàn)開發(fā)需求):
1 interfaceService {2 public voidservice();3 }4
5 class HouseService implementsService {6 @Override7 public voidservice() {8 System.out.println("為您的住房提供服務(wù).");9 }10 }11
12 interfaceMessage {13 public void send(); //消息發(fā)送
14 }15
16 class NetMessage implements Message { //網(wǎng)絡(luò)消息實(shí)現(xiàn)類
17 @Override18 public voidsend() {19 System.out.println("發(fā)送網(wǎng)絡(luò)消息");20 }21 }22
23 classFactory {24 privateFactory() {25 } //沒有產(chǎn)生實(shí)例化對象的意義
26
27 /**
28 * 獲取接口實(shí)例化對象29 *30 *@paramclassName 接口的子類31 *@paramtClass 描述的是一個接口的類型32 *@return如果子類存在則返回指定接口33 *@throwsException34 */
35 public static T getInstance(String className, Class tClass) throwsException {36 returntClass.cast(Class.forName(className).newInstance());37 }38 }39
40 public classFactoryDemo {41 public static void main(String[] args) throwsException {42 Message message = Factory.getInstance("反射.反射應(yīng)用案例.NetMessage",Message.class);43 message.send();44 Service instance = Factory.getInstance("反射.反射應(yīng)用案例.HouseService", Service.class);45 instance.service();46 }47 }
--此時的工廠設(shè)計模式才是所謂的高可用的工廠設(shè)計模式,而這種操作的實(shí)現(xiàn)依賴的就是泛型.此時的工廠設(shè)計模式將不再受限于指定的接口,可以為所有的接口提供實(shí)例化對象.
三.反射與單例設(shè)計模式
單例設(shè)計模式的核心本質(zhì)在于類內(nèi)部的構(gòu)造方法私有化,在類的內(nèi)部產(chǎn)生實(shí)例化對象之后在外部通過static方法獲取到實(shí)例化對象進(jìn)行類中的結(jié)構(gòu)調(diào)用.單例設(shè)計模式一共有兩種,懶漢式和餓漢式(餓漢式的單例是不再本次的討論范圍之內(nèi)的,主要討論懶漢式的單例)
--范例:觀察懶漢式單例的問題
1 classSingleton {2 private static Singleton instance = null;3
4 privateSingleton() {5 }6
7 public staticSingleton getInstance() {8 if (instance == null) {9 instance = newSingleton();10 }11 returninstance;12 }13
14 public voidprint() {15 System.out.println("單例模式加載");16 }17
18 }19
20 public classLazyLoadDemo {21 public static voidmain(String[] args) {22 Singleton singleton =Singleton.getInstance();23 singleton.print();24 }25 }
--此時我們的操作是在單線程的環(huán)境下運(yùn)行的,如果使用多線程
1 classSingleton {2 private static Singleton instance = null;3
4 privateSingleton() {5 System.out.println(Thread.currentThread().getName() + " 實(shí)例化單例對象");6 }7
8 public staticSingleton getInstance() {9 if (instance == null) {10 instance = newSingleton();11 }12 returninstance;13 }14
15 public voidprint() {16 System.out.println("單例模式加載");17 }18
19 }20
21 public classLazyLoadDemo {22 public static voidmain(String[] args) {23 for (int i = 0; i < 3; i++) {24 new Thread(() ->{25 Singleton.getInstance();26 }, "[單例創(chuàng)建者" + (i + 1) + "]").start();27 }28 }29 }
--運(yùn)行結(jié)果
[單例創(chuàng)建者1] 實(shí)例化單例對象
[單例創(chuàng)建者2] 實(shí)例化單例對象
[單例創(chuàng)建者3] 實(shí)例化單例對象
Process finished with exit code0
--單例設(shè)計模式最大的特點(diǎn)是在整體運(yùn)行之中,只允許產(chǎn)生一個實(shí)例化對象,當(dāng)有了若干實(shí)例化對象之后,那么就不是單例設(shè)計模式了,我們可以大致分析單例模式的運(yùn)行流程如下:
1.判斷instance是否為空?
2.如果instance為空,實(shí)例化instance對象
3.返回當(dāng)前的instance
--因此在多線程的設(shè)計中,每一個線程在執(zhí)行步驟1的時候都會認(rèn)為此時的對象為空,那么都會去創(chuàng)建這個對象的實(shí)例,這樣一來單例設(shè)計模式也就失去了意義,如果想要解決這類問題,關(guān)鍵的核心就在于要解決同步處理,而解決同步處理的核心就是使用synchronized關(guān)鍵字
1 classSingleton {2 private static Singleton instance = null;3
4 privateSingleton() {5 System.out.println(Thread.currentThread().getName() + " 實(shí)例化單例對象");6 }7
8 public static synchronizedSingleton getInstance() {9 if (instance == null) {10 instance = newSingleton();11 }12 returninstance;13 }14
15 public voidprint() {16 System.out.println("單例模式加載");17 }18
19 }20
21 public classLazyLoadDemo {22 public static voidmain(String[] args) {23 for (int i = 0; i < 3; i++) {24 new Thread(() ->{25 Singleton.getInstance();26 }, "[單例創(chuàng)建者" + (i + 1) + "]").start();27 }28 }29 }
--運(yùn)行結(jié)果
[單例創(chuàng)建者1] 實(shí)例化單例對象
Process finished with exit code0
--此時卻是進(jìn)行了同步處理,但是這個同步的代價卻是很大的,因為效率會降低.因為整體代碼中實(shí)際上只有一塊區(qū)域需要同步處理,那就是instance對象的實(shí)例化處理部分,在這樣的情況下同步加的未免顯得有些草率,更加合理的進(jìn)行同步處理:
1 classSingleton {2 //在對象實(shí)例化的時候,應(yīng)該立刻與主內(nèi)存中的實(shí)例對象保持同步,而不應(yīng)該存在副本
3 private static volatile Singleton instance = null;4
5 privateSingleton() {6 System.out.println(Thread.currentThread().getName() + " 實(shí)例化單例對象");7 }8
9 public staticSingleton getInstance() {10 synchronized (Singleton.class) { //static方法只能使用Singleton.class
11 if (instance == null) {12 instance = newSingleton();13 }14 }15 returninstance;16 }17
18 public voidprint() {19 System.out.println("單例模式加載");20 }21
22 }23
24 public classLazyLoadDemo {25 public static voidmain(String[] args) {26 for (int i = 0; i < 3; i++) {27 new Thread(() ->{28 Singleton.getInstance();29 }, "[單例創(chuàng)建者" + (i + 1) + "]").start();30 }31 }32 }
總結(jié)
以上是生活随笔為你收集整理的java 反射应用_java反射(二)--反射应用案例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse java 1.8 vm_
- 下一篇: java datainputstream