反射应用案例

作者: 江湖非良人 | 来源:发表于2019-07-31 19:20 被阅读3次

  经过一系列分析之后可以发现虽然获得了Class类的实例化对象,但是依然觉得这个对象获取的意义不是很大,所以为了进一步理解反射的核心意义,下面将通过几个案例进行程序的说明(都是实际开发中一定会使用到的)。

反射实例化对象

  获取Class对象后最大意义实际上并不是在于只是一个对象的实例化操作形式,更重要的是Class中提供了一个对象的反射实例化方法(代替了关键字new):

  • 在JDK1.8及以前的实例化:
@Deprecated(since="9")
public T newInstance() throws InstantiationException, IllegalAccessException
  • 在JDK1.9+的实例化:
clazz.getDeclaredConstructor().newInstance()

范例:通过newInstance()方法实例化Person类对象

package com.mldn.demo.ref;
public class Person {
    //任何情况下如果要实例化对象,一定要调用类中的构造方法
    public Person(){
        System.out.println("*********** Person类构造方法 ************");
    }
    @Override
    public String toString() {
        return "我是一个中国人";
    }
}
package com.mldn.demo;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls=Class.forName("com.mldn.demo.ref.Person");
        Object obj= cls.newInstance();//实例化对象,JDK1.9废除了
        System.out.println(obj.toString());
        /**
         * *********** Person类构造方法 ************
         * 我是一个中国人
         */
    }
}

现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于“类 对象 = new 类()”,相当于隐含了关键字new,而使用字符串进行了替代。

范例:使用getDeclaredConstructor().newInstance()进行对象实例化

package com.mldn.demo;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls=Class.forName("com.mldn.demo.ref.Person");
        Object obj= cls.getDeclaredConstructor().newInstance();//实例化对象,JDK1.9废除了
        System.out.println(obj.toString());
        /**
         * *********** Person类构造方法 ************
         * 我是一个中国人
         */
    }
}

从JDK1.9后,newInstance()被替代了,因为默认的Class类中的newInstance()方法只能调用无参构造,所以很多开发者会认为其描述的不准确,于是将其变换了形式(构造方法会提到)。

反射与工厂设计模式

  如果要想进行对象的实例化处理,除了可以使用关键字new之外,还可以使用反射机制来完成,于是此时一定会思考一个问题:为什么要提供有一个反射的实例化?那么什么情况下用关键字new,什么情况下用反射呢?
  如果要想更好的理解此类问题,最好的解决方案就是通过工厂设计模式来解决。
  工厂模式的特点:客户端的程序类不涉及对象的实例化管理,只和接口有关联,通过工厂类获取指定接口的实例化对象。
范例:传统的工厂设计模式

interface IMessage{
    void send();//消息发送
}
class NetMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【网络消息发送】www.baidu.com");
    }
}
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
            IMessage msg=new NetMessage();//若直接实例化则一定会有耦合问题
    }
}

在实际的开发中,接口的主要作用是为不同层提供有一个操作的标准。但是如果此时直接将一个子类设置为接口实例化操作,那么一定会有耦合问题,所以需要使用了工厂设计模式来解决此问题。

范例:利用工厂设计模式解决

public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        IMessage msg=Factory.getInstance("netmessage");
        msg.send();//【网络消息发送】www.baidu.com
    }
}
interface IMessage{
    void send();//消息发送
}
class NetMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【网络消息发送】www.baidu.com");
    }
}
class Factory{
    private Factory(){}
    public static IMessage getInstance(String className){
        if("netmessage".equalsIgnoreCase(className)){
            return new NetMessage();
        }
        return null;
    }
}
静态工厂设计模式

此种工厂设计模式属于静态工厂设计模式,也就是如果现在要追加一个子类,则意味着工厂类一定要做出修改,因为如果不追加判断是无法获取指定接口对象的。

范例:为IMessage追加一个子类

public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        IMessage msg=Factory.getInstance("netmessage");
        msg.send();//【网络消息发送】www.baidu.com
        msg=Factory.getInstance("cloudmessage");
        msg.send();//【云消息发送】www.baidu.com
    }
}
interface IMessage{
    void send();//消息发送
}
class NetMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【网络消息发送】www.baidu.com");
    }
}
class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【云消息发送】www.baidu.com");
    }
}
class Factory{
    private Factory(){}
    public static IMessage getInstance(String className){
        if("netmessage".equalsIgnoreCase(className)){
            return new NetMessage();
        }
        if("cloudmessage".equalsIgnoreCase(className)){
            return new CloudMessage();
        }
        return null;
    }
}

工厂设计模式最有效的解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供了一个工厂类作为过渡端,但是随着项目的开发,IMessage接口下可能会拥有更多的子类,而且随着时间的推移,子类产生的可能会越来越多,那么此时就意味着,工厂类永远都要进行修改。

  那么这时候最好的解决方案就是不使用关键字new来完成,因为关键字在new在使用时,需要有一个明确的类存在。而newInstance()方法只需要有一个只需要有一个明确表示类名称的字符串即可。

public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        IMessage msg=Factory.getInstance("com.mldn.demo.NetMessage");
        msg.send();//【网络消息发送】www.baidu.com
        msg=Factory.getInstance("com.mldn.demo.CloudMessage");
        msg.send();//【云消息发送】www.baidu.com
    }
}
interface IMessage{
    void send();//消息发送
}
class NetMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【网络消息发送】www.baidu.com");
    }
}
class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【云消息发送】www.baidu.com");
    }
}
class Factory{
    private Factory(){}
    public static IMessage getInstance(String className){
        IMessage instance=null;
        try {
            instance=(IMessage) Class.forName(className).getDeclaredConstructor().newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}

这时可以发现,利用反射机制实现的工厂设计模式,最大的优势:对于接口子类的扩充将不再影响到工厂类的定义。

反射工厂设计模式

但现在依然需要进一步思考,因为在实际的项目开发过程中,有可能会存在有大量的接口,并且这些接口都可能需要通过工厂类实例化,所以此时的工厂设计模式不应该只为IMessage接口服务,应该为所有接口服务。

public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        IMessage msg=Factory.getInstance("com.mldn.demo.NetMessage",IMessage.class);
        msg.send();//【网络消息发送】www.baidu.com
        msg=Factory.getInstance("com.mldn.demo.CloudMessage",IMessage.class);
        msg.send();//【云消息发送】www.baidu.com
        IService service=Factory.getInstance("com.mldn.demo.HouseService",IService.class);
        service.service();
    }
}
class Factory{
    private Factory(){}
    /**
     * 获取接实例化对象
     * @param className 接口的子类
     * @param clazz 描述的是一个接口的类型
     * @return 如果子类存在则返回指定接口的实例化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getInstance(String className,Class<T> clazz){
        T instance=null;
        try {
            instance=(T) Class.forName(className).getDeclaredConstructor().newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}
interface IService{
    void service();
}
class HouseService implements IService{
    @Override
    public void service() {
        System.out.println("【服务】为您的住宿提供服务");
    }
}
interface IMessage{
    void send();//消息发送
}
class NetMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【网络消息发送】www.baidu.com");
    }
}
class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【云消息发送】www.baidu.com");
    }
}

此时的工厂模式将不再受限于指定的接口,可为所有的接口提供实例化服务。

反射与单例设计模式

  单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象后通过static方法获取实例化对象进行类中的机构调用,单例设计模式一共有两类:懒汉式、饿汉式,饿汉式的单例是不在本次讨论范围内的,主要来讨论懒汉式的单例设计模式。
范例:观察懒汉式单例设计模式的问题

public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 3; i++) {
            new Thread(()->{
                Singleton.getInstance().print();
            },"单例消费端-"+i).start();
        }
        /**
         * ****** 【单例消费端-1】实例化Singleton类对象 *******
         * ****** 【单例消费端-0】实例化Singleton类对象 *******
         * www.baidu.com
         * ****** 【单例消费端-2】实例化Singleton类对象 *******
         * www.baidu.com
         * www.baidu.com
         */
    }
}
class Singleton {
    private static Singleton instance = null;
    private Singleton() {
        System.out.printf("****** 【%s】实例化Singleton类对象 *******\n",Thread.currentThread().getName());
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    public void print() {
        System.out.println("www.baidu.com");
    }
}

单例设计模式的最大特点是在整体的运行程序中只允许产生一个实例化对象,但当有了若干个线程之后,实际上当前的程序就会产生多个实例化对象了,此时就不是单例设计模式了。此时问题造成的关键在于代码本身出现了不同步的情况,而要想解决的关键核心就在于需要使用synchronized关键字进行同步处理。

单例设计模式问题

范例:使用synchronized同步方法进行同步处理

class Singleton {
    private static Singleton instance = null;
    private Singleton() {
        System.out.printf("****** 【%s】实例化Singleton类对象 *******\n",Thread.currentThread().getName());
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    public void print() {
        System.out.println("www.baidu.com");
    }
}

这个时候的确是进行了同步处理,但这个同步处理的代价有些大,因为效率会低。因为整体代码中实际上只有一块部分需要进行同步处理,instance对象的实例化处理部分。
范例:使用synchronized同步代码块进行同步处理

class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {
        System.out.printf("****** 【%s】实例化Singleton类对象 *******\n",Thread.currentThread().getName());
    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    public void print() {
        System.out.println("www.baidu.com");
    }
}

相关文章

  • 反射应用案例

      经过一系列分析之后可以发现虽然获得了Class类的实例化对象,但是依然觉得这个对象获取的意义不是很大,所以为了...

  • Python 严格的类型检测

    简单的反射使用案例

  • PHP Reflection 反射

    反射 ReflectionClass 类 PHP反射机制 反射在 PHP 中的应用

  • reflect.go包学习_之二 指针操作提高反射性能 反射应用

    reflect.go包学习_之二 指针操作提高反射性能 反射应用 反射创建实例 反射信息、反射调用方法、反射修改值...

  • Java中的反射|SquirrelNote

    前言 本篇简介: 反射概述 反射具体功能实现 Android中的反射应用 一、反射(Reflection)概述 1...

  • 反射机制案例

    在Java中,可以利用反射获取指定类型的公有/私有属性、方法,生成该类的实例对象,并调用该对象的方法。 以下反射相...

  • 安卓反射和动态代理的应用

    提纲 java反射基础 反射在Android中的应用 Java动态代理 动态代理在Android的应用 java反...

  • 反射之一

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • 反射之二

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • 这才是理解Java反射的正确姿势

    反射简介 反射是Java的高级特性之一,但是在实际的开发中,使用Java反射的案例却非常的少,但是反射确实在底层框...

网友评论

    本文标题:反射应用案例

    本文链接:https://www.haomeiwen.com/subject/lyutdctx.html