内容学习于:edu.aliyun.com


1. 反射实例化对象

  通过分析之后实际,上可以发现,对于Class类的对象有三种实例化的方式,但是获取Class类的实例化对象不仅仅只是获取它对应类的信息,实际上它可以实现更加丰富的功能。

  获取Class类对象之后最大用处在于可以直接利用反射提供的方法调用指定类的构造实现对象的实例化处理,而此操作方法:

@Deprecated(since="9") 
public T newInstance() throws InstantiationException, IllegalAccessException

clazz.getDeclaredConstructor().newInstance()

反射对象实例化:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        //反射定义中的泛型基本上都比较尴尬,所以出现的?居多
       Class<?> clazz = Class.forName("java.util.Date");//第一种方法
        Object obj = clazz.newInstance();//反射实例化对象,表示的相当于“new java.util.Date()”
        System.out.println(obj);//调用的是date的构造
    }
}

结果:

Tue Feb 04 08:58:58 CST 2020

  本操作最大的特点在于,整体的代码里面并没有一个地方使用了import进行java.util.Date类的导入,同时也没有明确的进行构造方法的调用,但是在JDK 1.9之后“ clazz.newInstanee()"操作已经变为了过期处理,所以以上的代码可以更换为:

代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        //反射定义中的泛型基本上都比较尴尬,所以出现的?居多
       Class<?> clazz = Class.forName("java.util.Date");//第一种方法
        Object obj = clazz.getDeclaredConstructor().newInstance();//反射实例化对象,表示的相当于“new java.util.Date()”
        System.out.println(obj);//调用的是date的构造
    }
}

结果:

Tue Feb 04 09:04:50 CST 2020

  如果要想深刻的理解反射实际上就需要进行更加详细的设计,此时只是告诉了大家对于类对象实例化除了使用关键字new之外,还可以考虑使用反射机制完成。

2. 反射与工厂设计模式

  对于此时来讲,对象的实例化操作有了两种不同的支持:关键字new、反射机制,那么对于这两种实例化机制,有那些不同呢?为什么Java 原始提供了关键字new,而后又需要提供有反射实例化呢?

定义基础工厂类:

interface IMessage{
    public void send(String msg);
}

class Newspaper implements IMessage{
    @Override
    public void send(String msg) {
        System.out.println("【报纸报道】"+msg);
    }
}

class NetMessage implements IMessage{
    @Override
    public void send(String msg) {
        System.out.println("【网络报道】"+msg);

    }
}

class Factor{
    private Factor(){};
    public static IMessage getInstance(String str){
        if ("newspaper".equalsIgnoreCase(str))
        {
            return new Newspaper();
        }
        else if ("netmessage".equalsIgnoreCase(str)){
            return new NetMessage();
        }
        return null;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        IMessage message = Factor.getInstance("newspaper");
        message.send("MLDN");
    }
}

结果:

【报纸报道】MLDN

  对于此时的代码,是属于java面向对象编程的基本模型,因为只要是获取接口实例,应该都通过工厂类来完成,于是当前的类设计结构如下。

  现在虽然实现了一个基础的工厂设计模式,但是在此时实际上也存在有另外一个问题,如果此时要扩充新的子类呢?那么此时工厂类上一定要发生变更。
  假设说现在IMessage接口可能定义了5000W个子类,也有可能每秒钟消失30个子类,每分钟追加100W个子类,那么你的工厂类将受到如何的刺激呢?实际上这一切问题产生的根源就在于关键字“new”,关键字new是Java提供的原生的对象实例化支持,但是其却需要固定的结构才可以使用。那么在这个时候如果要想解决关键字new的问题,最好的做法就是利用反射机制。
  如下图所示:

通过反射修改工厂:

interface IMessage {
    public void send(String msg);
}

class Newspaper implements IMessage {
    @Override
    public void send(String msg) {
        System.out.println("【报纸报道】" + msg);
    }
}

class NetMessage implements IMessage {
    @Override
    public void send(String msg) {
        System.out.println("【网络报道】" + msg);

    }
}

class Factor {
    private Factor() {
    }

    public static IMessage getInstance(String str) {
        IMessage message = null;
        try {//包含类加载异常、对象实例化异常、强制转型异常
            message = (IMessage) Class.forName(str).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
        }
        return message;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        IMessage message = Factor.getInstance("com.xzzz.demo.NetMessage");
        message.send("MLDN");

    }
}

结果:

【网络报道】MLDN

  那么此时所编写的工厂类可以适应于各种环境,都可以获取IMessage接口实例。

3. 反射与单例设计模式

  单例设计模式的核心本质在于,一个类在-一个JVM进程之中只允许有一个实例化对象,对于单例设计模式它本身考虑到设计情况实际上提供有两种结构:饿汉式单例、懒汉式单例,懒汉式的单例问题比较麻烦。

观察懒汉式单例设计模式:

class Singleton {
    private static Singleton instance;

    private Singleton() {
        System.out.println("【Singleton类构造】实例化Singleton类对象");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            return new Singleton();
        }
        return instance;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                Singleton intance = Singleton.getInstance();
            }).start();
        }
    }
}

结果:

【Singleton类构造】实例化Singleton类对象
【Singleton类构造】实例化Singleton类对象
【Singleton类构造】实例化Singleton类对象
【Singleton类构造】实例化Singleton类对象
【Singleton类构造】实例化Singleton类对象

  单例设计的本质在于,一个JVM进程只允许有该类的一个实例化对象,但是发现一旦使用了多线程,并且结合了懒汉式设计的时候,程序代码中出现了对象的多次实例化,如果现在要想解决当前的问题,唯一可以想到的就是同步处理。

追加同步:

class Singleton {
    private static Singleton instance;

    private  Singleton() {
        System.out.println("【Singleton类构造】实例化Singleton类对象");
    }

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                Singleton intance = Singleton.getInstance();
            }).start();
        }
    }
}

结果:

【Singleton类构造】实例化Singleton类对象

  此时利用了同步解决了单例实例化对象的问题,但是随之而来所带来一个新的情况:如果说现在要是十万个线程获取Singleton类的实例化对象,那么最终就会造成十万个线程要依次同步等待后获取,直接将产生严重的性能问题。
  同步是否添加的决定性因素:如果读取不要加同步,如果更新要加同步。

解决方案:

class Singleton {
    private static Singleton instance;

    private Singleton() {
        System.out.println("【Singleton类构造】实例化Singleton类对象");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                Singleton intance = Singleton.getInstance();
            }).start();
        }
    }
}

结果:

【Singleton类构造】实例化Singleton类对象

  此时的代码保证了getInstance()方法的操作性能,同时又保护了Singleton 类对象的实例化次数。