进阶Java编程(8)反射应用案例
1,反射实例化对象
经过一系列分析之后可以发现虽然获得了Class类的实例化对象但是依然觉得这个对象获取的意义不是很大,所以为了进一步的帮助大家理解反射的核心意义所在,下面将通过几个案例进行说明(都是实际开发之中一定会使用到)。
·反射实例化对象
获取Class对象之后最大的意义实际上并不是在于只是一个对象实例化操作的形式,更是重要的是Class类里面提供有一个对象的反射实例化方法(代替关键字new):
·在JDK1.9以前的实例化:public T newInstance() throws InstantiationException,IllegalAccessException
·JDK1.9之后:class.getDeclaredConstructor().newInstance()
·范例:通过newInstance()方法实例化Person类对象
1 package cn.mufasa.demo;
2 public class Person1 {
3 //任何情况下如果要进行实例化一定要调用类中的构造方法
4 //为类中的属性初始化
5 public Person1(){
6 System.out.println("***无参构造方法***");
7 }
8 @Override
9 public String toString(){
10 return "一个Person类";
11 }
12 }
1 package cn.mufasa.demo;
2 public class JavaAPIDemo6 {
3 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
4 Class<?> cls=Class.forName("cn.mufasa.demo.Person1");
5 Object obj=cls.newInstance();//实例化对象,JDK1.9之后非常Deprecated【弃用】
6 System.out.println(obj);//输出对象调用toString
7 }
8 }
9 /*输出
10 【cls.newInstance()】***无参构造方法***
11 【System.out.println(obj)】一个Person类
12 */
现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于【new 对象=new 类()】,相当于隐藏了关键字new,而直接使用字符串进行了替代。
·范例:从JDK1.9之后newInstance()被替代
1 package cn.mufasa.demo;
2 public class JavaAPIDemo7 {
3 public static void main(String[] args) throws Exception {
4 Class<?> cls=Class.forName("cn.mufasa.demo.Person1");
5 Object obj=cls.getDeclaredConstructor().newInstance();//JDK1.9之后,构造方法的实例化
6 System.out.println(obj);
7 }
8 }
9 /*输出
10 ***无参构造方法***
11 一个Person类
12 */
原因:Class类中的newInstance只能够调用无参构造,很多开发者认为其描述的不准确,于是将其变换了形式(构造方法会讲解)。
2,反射与工厂设计模式
如果想要进行对象的实例化处理除了使用关键字new之外,还可以使用反射机制来完成,于是此时一定会思考一个问题:①为什么要提供一个反射的实例化?②到底是使用关键字new还是反射呢?
如果想要更好的理解此类问题,最后的解释方案就是通过工厂设计模式来解决。工厂设计模式最大的特定:客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象。
·范例:传统工厂设计模式
1 package cn.mufasa.demo;
2 public class JavaAPIDemo8 {
3 public static void main(String[] args) throws Exception {
4 IMessage msg=new NetMessage();//如果直接实例化一定会有耦合问题
5 msg.send();
6 }
7 }
8 /*输出
9 【网络消息发送】www.cnblogs.com
10 */
在实际的开发之中,接口主要的作用是为不同的层提供有一个操作的标准。但是如果此时直接将一个子类设置为实例化接口对象操作,那么一定会有耦合问题,所以使用工厂设计模式来解决此类问题。
·范例:利用工厂模式解决此类问题
1 package cn.mufasa.demo;
2 public class Factory {
3 private Factory(){}//没有产生实例化对象的意义,所以构造方法私有化
4 public static IMessage getInstance(String className){
5 if("netmessage".equalsIgnoreCase(className)){
6 return new NetMessage();
7 }
8 return null;
9 }
10 }
1 package cn.mufasa.demo;
2 public class JavaAPIDemo9 {
3 public static void main(String[] args) throws Exception {
4 IMessage msg=Factory.getInstance("NetMessage");//标准的工厂设计模式
5 msg.send();
6 }
7 }
8 /*输出
9 【网络消息发送】www.cnblogs.com
10 */
此种工厂设计模式属于静态设计模式,也就是说如果现在要追加一个子类,则意味着工厂类一定要做出修改,因为如果不追加这种判断是无法获取指定接口对象的。
·范例:为IMessage追加一个子类
1 package cn.mufasa.demo;
2 public class Factory1 {
3 private Factory1(){}//没有产生实例化对象的意义,所以构造方法私有化
4 public static IMessage getInstance(String className){
5 if("netmessage".equalsIgnoreCase(className)){
6 return new NetMessage();
7 }else if("cloudmessage".equalsIgnoreCase(className)){
8 return new CloudMessage();
9 }
10 return null;
11 }
12 }
1 package cn.mufasa.demo;
2 public class JavaAPIDemo10 {
3 public static void main(String[] args) throws Exception {
4 IMessage msg=Factory1.getInstance("cloudmessage");//标准的工厂设计模式
5 msg.send();
6 }
7 }
8 /*输出
9 【云消息】www.cnblogs.com
10 */
工厂设计模式最有效解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供有一个工厂类作为过渡端,但是随着项目的进行,你的IMessage类可能会有更多的子类,而且随着时间的推移子类产生的可能越来越多,那么此时就意味着,你的工厂类永远都要进行修改,并且永无停止之日!!!这种静态工厂在实际开发中不被采用,如果想要编码一次工厂类满足所有子类,那么其中的判断【if】语句是不允许增加的。
那么这个时候最好的解决方案就是不使用关键字new来完成,因为关键字new在使用的时候需要有一个明确的类存在。而newInstance()方法只需要有一个明确表示类名称的字符串即可应用。
1 package cn.mufasa.demo;
2 public class Factory2 {
3 private Factory2(){}
4 public static IMessage getInstance(String className) throws Exception {
5 IMessage instance=null;
6 try{//动态工厂类
7 instance=(IMessage) Class.forName(className).getDeclaredConstructor().newInstance();
8 }catch (Exception e){
9 e.printStackTrace();
10 }
11 return instance;
12 }
13 }
1 package cn.mufasa.demo;
2 public class JavaAPIDemo11 {
3 public static void main(String[] args) throws Exception {
4 IMessage msg=Factory2.getInstance("cn.mufasa.demo.CloudMessage");
5 msg.send();
6 }
7 }
8 /*输出
9 【云消息】www.cnblogs.com
10 */
这个时候发现利用工厂类设计模式,最大的优势在于接口子类的扩充将不再影响到工厂类的定义。
但是现在依然需要进一步思考,因为在实际的项目开发过程中有可能会有大量的接口,并且这些接口都可能需要通过工厂类实例化,所有此时的工厂设计模式不应该只为一个IMessage接口服务,而应该为所有的接口服务。
·范例:终极的工厂设计模式【没有耦合-无视接口、子类等等】
1 package cn.mufasa.demo;
2 public interface IService {
3 public void service();
4 }
1 package cn.mufasa.demo;
2 class HouseService implements IService{
3 @Override
4 public void service() {
5 System.out.println("【服务】住宿服务");
6 }
7 }
1 package cn.mufasa.demo;
2 public class JavaAPIDemo12 {
3 public static void main(String[] args) throws Exception {
4 IMessage msg=Factory3.getInstance("cn.mufasa.demo.CloudMessage",IMessage.class);
5 msg.send();
6 IService service=Factory3.getInstance("cn.mufasa.demo.HouseService",IService.class);
7 service.service();
8 }
9 }
10 /*输出
11 【云消息】www.cnblogs.com
12 【服务】住宿服务
13 */
1 package com.cnblogs.mufasa.Demo2; 2 3 public class Factory3 { 4 private Factory3(){} 5 6 /** 7 * 获取接口实例化对象 8 * @param className 接口的子类 9 * @param tClass 描述的是一个接口的类型 10 * @param <T> 接口的类型【泛型】 11 * @return 如果子类存在则返回指定接口的实例化对象 12 * @throws Exception 13 */ 14 public static <T> T getInstance(String className,Class<T> tClass) throws Exception { 15 T instance=null; 16 try{//动态工厂类 17 instance=(T) Class.forName(className).getDeclaredConstructor().newInstance(); 18 }catch (Exception e){ 19 e.printStackTrace(); 20 } 21 return instance; 22 } 23 }
此时的工厂设计模式将不再受限于指定的接口,可以为所有的接口提供实例化操作,大大提高了可重用性。