ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java反射机制-Class类&工厂设计模式

2021-01-27 23:02:29  阅读:138  来源: 互联网

标签:Java 子类 IMessage 工厂 实例 接口 设计模式 Class


Java反射机制-Class类&工厂设计模式

1 如何理解反射?

    可以将反射视为“对象实例化”的反向操作,那么,
    (1)什么是正?
        要使用一个类,经过的流程为:先去导入类所在的包然后去实例化这个类接着通过这个对象去调用类中已经定义好的方法。这就是“正”。
    (2)什么是反?
        反,就是根据实例化的对象去寻根溯源,“反推”出它所在的类的结构,即:包含哪些成员属性和成员方法。
    要想反向获取这些信息,就要用到java.lang.Object类的getClass()方法,该方法的如下所示,
在这里插入图片描述

    例如,定义一个Person类的对象,然后通过这个对象去调用继承自Object类的getClass()方法,获取到运行时类,并将其打印出来,代码示例如下,

public static void main(String[] args) {
    Person p=new Person("Tom",12);
    Class<? extends Person> pClass = p.getClass();
    System.out.println(pClass);//class cn.Bean.Person
}

    输出结果为,
在这里插入图片描述

2 Class类

    注意到,1的案例中,通过getClass()方法的返回值类型为Class,那么,接下来就尝试使用Class类去获取Person类的其它信息。
    Class类位于Java.lang包下,可以说:反射之中所有的核心操作都是通过Class类的对象展开的,即:Class类就是反射操作的核心所在。
    Class的定义如下,

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement 

2.1 Class类的3种实例化方式

    通常的,要使用一个类中定义的成员方法,就要先获取这个类的实例,获取Class类的实例主要有以下3种方式,
    (1)【Object类支持】通过Object类的getClass()方法:获取实例化对象的运行时类——Class的实例;
    特点:①必须要先获取类的实例A,才能去调用继承自Object类的getClass方法获取Class的实例,可能会造成无用对象A的产生;
    例如,

Person p=new Person("Tom",12);
Class<? extends Person> pClass = p.getClass();
System.out.println(pClass);//class cn.Bean.Person
System.out.println(pClass.getName());//cn.Bean.Person

    (2)【JVM直接支持】通过“类名.class”的方式获取Class类的实例;
    例如,

Class<Person> personClass = Person.class;
System.out.println(personClass);//class cn.Bean.Person
System.out.println(personClass.getName());//cn.Bean.Person

    (3)【Class类支持】通过Class类中的static静态方法forName(String className)获取Class类的实例;
在这里插入图片描述
    特点:可以直接采用字符串形式定义要使用的类类型,并且程序中不需要编写任何的import-导包语句。但是,如果要操作的类不存在,就会抛出“ClassNotFountException”异常。
    例如,

try {
    Class<?> aClass = Class.forName("cn.Bean.Person");
    System.out.println(aClass);//class cn.Bean.Person
    System.out.println(aClass.getName());//cn.Bean.Person
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

2.2 利用反射机制获取类的实例

    通过20.3.1种的3种获取Class类的实例clazz之后,就可以使用clazz调用成员方法获取所对应的类的实例。
    这个成员方法在jdk1.9之前是:newInstance();
    在jdk1.9之后变为:getConstructor.newInstance();
在这里插入图片描述
    例如,要通过Class.forName()方法加载Person类,并获取Class类的实例clazz,然后通过clazz获取Person类的实例。示例代码如下,

try {
            //通过Class的newinstance()方法获取Person类的实例
            Class<?> personClass = Class.forName("cn.Bean.Person");
//            Object person = personClass.newInstance();
            Object person = personClass.getDeclaredConstructor().newInstance();
            //打印实例
            System.out.println(person);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

在这里插入图片描述

3 反射&工厂设计模式

    经过2.2的学习,可以得知:想要获取一个类的对象/实例,有两种途径,①直接调用类的构造方法获取;②通过(反射机制)Class实例调用以下两个方法来获取。

【1】newInstance()
【2】getDeclaredConstructor().newInstance()

3.1 工厂模式之简单工厂模式的固有缺陷

    在介绍反射机制与工厂设计模式的关系之前,先对简单工厂模式(也被称为“静态工厂模式”)进行介绍与案例演示,分析其固有的缺陷。
    工厂模式是为了解决程序的耦合问题,其核心思想是:
    下面看一个简单工厂模式的案例:使用工厂模式,根据需要提供IMessage接口不同实现子类的实例,
    (1) IMessage接口只有一个实现子类的情况。

//接口(函数式接口-->只负责发送消息):提供同统一的子类标准
@FunctionalInterface
interface IMessage{
    public abstract void send();//发送消息
}
//接口子类
class NetIMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("www.baidu.com");
    }
}

//简单/静态工厂类
class SimpleFactory{
    private SimpleFactory(){}//工厂类的实例化是毫无意义的,所以可将构造器私有化
    //添加静态的方法
    public static IMessage getIMessageInstance(String messageType){
        if (messageType.equalsIgnoreCase("INetIMessage"))
            return new NetIMessage();
        return null;
    }
}

//测试代码
public class SimpleFactoryModeDemo {
    public static void main(String[] args) {
        //使用工厂类创建INetMessage类的实例
        IMessage netMessage = SimpleFactory.getIMessageInstance("INetIMessage");
        netMessage.send();
    }
}

    (2) 当有多个实现子类的情况。

//接口(函数式接口-->只负责发送消息)
@FunctionalInterface
interface IMessage{
    public abstract void send();//发送消息
}
//接口子类1:NetIMessage
class NetIMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("www.baidu.com");
    }
}
//接口子类2:CloudIMessage
class CloudIMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("cloud message");
    }
}

//简单/静态工厂类
class SimpleFactory{
    private SimpleFactory(){}//工厂类的实例化是毫无意义的,所以可将构造器私有化
    //添加静态的方法
    public static IMessage getIMessageInstance(String messageType){
        if (messageType.equalsIgnoreCase("INetIMessage"))
            return new NetIMessage();
        else if (messageType.equalsIgnoreCase("CloudIMessage"))
            return new CloudIMessage();
        return null;
    }

    可以发现, 每当要为接口IMessage追加一个新的子类时,对于简单工厂模式,工厂类内部都要去修改创建子类的代码,从而保证程序的正常运行 。这意味着对于工厂类的修改将是永无止境的,因此,这种简单工厂模式对于实际项目开发中是不会被采用的。如下图所示,
在这里插入图片描述

3.2 利用反射机制优化简单工厂模式

    对于简单工厂模式存在的缺陷,解决之道就是:不使用new关键字去创建类的实例【因为new关键字在使用时需要有一个明确的构造器出现】,而是根据表示类的名称的字符串,调用Class.forName(String className)方法去创建子类对象,这样就避免了多个if语句的持续添加
    按照上述思想,来对先前工厂类中的代码进行修改,如下所示,

//利用反射机制实现的工厂类
class SimpleFactory{
    private SimpleFactory(){}//工厂类的实例化是毫无意义的,所以可将构造器私有化
    //添加静态的方法
    public static IMessage getIMessageInstance(String messageType){
        //提供接口对象
        IMessage message=null;
        //根据表示类名称的字符串创建新的实例
        try {
            message = (IMessage) Class.forName(messageType).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return message;
    }
}

    在main方法中测试结果如下,

public class SimpleFactoryModeDemo {
    public static void main(String[] args) {
        //使用工厂类创建IMessage接口的实例
        IMessage netMessage = SimpleFactory.getIMessageInstance("cn.demo12.ClientIMessage");
        netMessage.send();
    }
}

在这里插入图片描述

    如此,利用反射机制实现的工厂模式,就可以对简单工厂模式/静态工厂模式弊端进行修补,其最大优势在于:对于接口子类的扩充,将不再影响到工厂类的定义

3.3 反射机制&工厂模式的关系

    按照3.2中的思路,反射机制与工厂模式之间的关系就如下图所示,
在这里插入图片描述
    这样,工厂类Factory就不需要再去关注接口IMessage的实现子类,而只需要通过反射机制,利用接口子类字符串形式的名称去创建对应子类的对象即可。

3.4 利用反射机优化工厂模式存在的新问题

    按照3.3的思路,现在工厂类的方法已经与接口的实现子类无关了,而只需要去关注接口的类型与表示子类名称的字符串即可。
    那么,现在假设有一个新的接口IServer,该接口也有子类:GISIServer,那么,就可以让这个工厂类继续为IServer接口服务,实现代码如下,

//利用反射机制实现的工厂类
class SimpleFactory{
    private SimpleFactory(){}//工厂类的实例化是毫无意义的,所以可将构造器私有化
    //添加静态的方法
    //提供IMessage接口的子类对象
    public static IMessage getIMessageInstance(String messageType){
        //提供接口对象
        IMessage message=null;
        //根据表示类名称的字符串创建新的实例
        try {
            message = (IMessage) Class.forName(messageType).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return message;
    }

    //提供IServer接口的子类对象
    public static IServer getServiceInstance(String serviceType){
        IServer server=null;
        try {
            server = (IServer) Class.forName(serviceType).getDeclaredConstructor().newInstance();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return server;
    }
}

    但是,当这个工厂类的服务对象逐渐增多时,就要编写大量的重复性代码。因为:创建不同接口的子类对象时,除了接口类型不同之外,接口子类的创建过程是相同的——利用反射机制调用Class.forName()方法创建对象,这样,就会出现大量的冗余代码。
在这里插入图片描述

3.5 利用泛型继续优化工厂模式

    那么,能否对20.3.3.4存在的这个问题再次进行优化呢?
答案是可以的,解决之道就是:泛型。实现代码如下,

//利用反射机制实现的工厂类
class SimpleFactory{
    private SimpleFactory(){}//工厂类的实例化是毫无意义的,所以可将构造器私有化
    //添加静态的方法
    //提供指定泛型类型的实例
    /***
     * 特点:在方法外指定泛型的类型,在方法内根据泛型的类型进行实例类型的转换
     * @param className 表示类的名称
     * @param clazz 表示泛型对应的接口类型
     * @return 如果子类存在就返回指定接口的实例化对象
     */
    public static<T> T getInstance(String className,Class<T> clazz){
        //声明对象实例
        T instance=null;
        try {
            //利用反射机制创建T类型的实例
            instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }
}

    在main方法中的测试代码如下,

public static void main(String[] args) {
    //使用工厂类创建IMessage接口的实例
    IMessage netMessage = SimpleFactory.getInstance("cn.demo12.ClientIMessage",IMessage.class);
    netMessage.send();

    IServer server=SimpleFactory.getInstance("cn.demo12.GISIServer",IServer.class);
    server.service();
}

    输出结果为,
在这里插入图片描述
    总结:通过“在工厂类的getInstance()方法外部指定接口类型,在方法内部根据接口类型对利用反射机制创建的接口实例执行强制类型转换,并将其返回”的方式,使得工厂类中getInstance()方法,不再受限于指定的接口,可以为所有的接口提供实例化对象。这也是工厂设计模式的最终版本,提高了代码的重用性。

标签:Java,子类,IMessage,工厂,实例,接口,设计模式,Class
来源: https://blog.csdn.net/weixin_43524214/article/details/113278401

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有