zoukankan      html  css  js  c++  java
  • java反射(二)--反射应用案例

    一.反射实例化对象

      经过一系列的分析之后发现虽然可以获取Class类的实例化对象,但是依然觉得这个对象的获取意义不是很大,因此可以通过以下几个案例去理解反射的核心意义
    --反射实例化对象:获取Class对象之后最大的意义并不是在于只是一个对象的实例化操作形式,更重要的是Class类中提供有一个对象的反射实例化方法,在JDK1.9之前的实例化:public T newInstance() throw InstantiationException,IllegalAccessException,该方法代替了new 关键字的使用,但是在JDK1.9之后则发生了变化:class.getDeclaredConstructor().newInstance();
    --范例:通过newInstance()方法实例化对象

     1 package 反射.认识反射机制.entity;
     2 
     3 /**
     4  * @author : S K Y
     5  * @version :0.0.1
     6  */
     7 public class Person {
     8     public Person() {   //任何情况下只要实例化对象则一定要调用类中的构造方法
     9         System.out.println("Person对象实例化了");
    10     }
    11 
    12     @Override
    13     public String toString() {
    14         return "我是一个好人";
    15     }
    16 }
    1 public class Demo {
    2     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    3         Class<?> aClass = Class.forName("反射.认识反射机制.entity.Person");
    4         Object o = aClass.newInstance();    //实例化对象
    5         System.out.println(o);
    6     }
    7 }

    --运行结果

    Person对象实例化了
    我是一个好人
    
    Process finished with exit code 0

    --现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于new 关键字的使用,但是该方法在JDK1.9之后被替代了,因为默认的Class类中的newInstance()方法只能够调用无参构造,所以很多的开发者认为其描述的不准确,于是将其变换了形式(后续会说明)

    二.反射与工厂设计模式
      如果要想进行对象的实例化处理除了可以使用关键字new 之外,还可以挺过反射机制来完成.那么思考一个问题:为什么要提供有一个反射的实例化?到底是使用关键字new还是使用反射进行对象实例化呢?
    --如果想要更好的解决此类问题,最好的解释方案就是通过工厂设计模式来解决.工厂设计模式的最大特点:客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂了获取接口的实例化对象,传统的工厂设计模式:

     1 interface Message{
     2     public void send();     //消息发送
     3 }
     4 class NetMessage implements Message{        //网络消息实现类
     5     @Override
     6     public void send() {
     7         System.out.println("发送网络消息");
     8     }
     9 }
    10 public class FactoryDemo {
    11     public static void main(String[] args) {
    12         Message message = new NetMessage(); //如果直接实例化则一定会有耦合问题
    13     }
    14 }

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

     1 interface Message {
     2     public void send();     //消息发送
     3 }
     4 
     5 class NetMessage implements Message {        //网络消息实现类
     6     @Override
     7     public void send() {
     8         System.out.println("发送网络消息");
     9     }
    10 }
    11 
    12 class Factory {
    13     private Factory() {
    14     }     //没有产生实例化对象的意义
    15 
    16     public static Message getInstance(String className) {
    17         if ("NetMessage".equals(className)) {
    18             return new NetMessage();
    19         }
    20         return null;
    21     }
    22 }
    23 
    24 public class FactoryDemo {
    25     public static void main(String[] args) {
    26         Message message = Factory.getInstance("NetMessage");
    27         message.send();
    28     }
    29 }

    --此种工厂设计模式属于静态工厂设计模式,此时如果追加一个子类,那么工厂类就需要进行相应的修改(追加相应的判断语句),否则无法获得新的子类的实例化对象.工厂设模式最有效解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供有一个工厂类作为过渡端,可是随着项目的进行,Message接口可能会有更多的子类,而且随着时间的推移,子类会越来越多,因此工厂类永远都需要修改,并且永无停止之日.
    --此时最好的解决方案就是不使用关键字new来完成对象的实例化,因为关键字new在使用的时候需要有一个明确的类存在.而newInstance()的方法只需要有一个明确表示类名称的字符串即可应用:

     1 interface Message {
     2     public void send();     //消息发送
     3 }
     4 
     5 class NetMessage implements Message {        //网络消息实现类
     6     @Override
     7     public void send() {
     8         System.out.println("发送网络消息");
     9     }
    10 }
    11 
    12 class Factory {
    13     private Factory() {
    14     }     //没有产生实例化对象的意义
    15 
    16     public static Message getInstance(String className) throws Exception {
    17         return (Message) Class.forName(className).newInstance();
    18     }
    19 }
    20 
    21 public class FactoryDemo {
    22     public static void main(String[] args) throws Exception {
    23         Message message = Factory.getInstance("反射.反射应用案例.NetMessage");
    24         message.send();
    25     }
    26 }

    --此时如果对子类继续进行扩充的话,是没有必要修改工厂类的.利用反射机制实现的工厂设计模式,最大的优势在于,对于接口的子类的扩充,将不再影响到工厂类的定义.但是现在依然需要进行思考,在实际的项目开发之中,有可能会存在大量的接口,并且这些接口可能都需要通过工厂类来实例化对象,所以此时的工厂设计模式不应该只为一个Message接口服务,而应该变为为所有的接口服务(使用泛型实现开发需求): 

     1 interface Service {
     2     public void service();
     3 }
     4 
     5 class HouseService implements Service {
     6     @Override
     7     public void service() {
     8         System.out.println("为您的住房提供服务.");
     9     }
    10 }
    11 
    12 interface Message {
    13     public void send();     //消息发送
    14 }
    15 
    16 class NetMessage implements Message {        //网络消息实现类
    17     @Override
    18     public void send() {
    19         System.out.println("发送网络消息");
    20     }
    21 }
    22 
    23 class Factory {
    24     private Factory() {
    25     }     //没有产生实例化对象的意义
    26 
    27     /**
    28      * 获取接口实例化对象
    29      *
    30      * @param className 接口的子类
    31      * @param tClass    描述的是一个接口的类型
    32      * @return 如果子类存在则返回指定接口
    33      * @throws Exception
    34      */
    35     public static <T> T getInstance(String className, Class<T> tClass) throws Exception {
    36         return tClass.cast(Class.forName(className).newInstance());
    37     }
    38 }
    39 
    40 public class FactoryDemo {
    41     public static void main(String[] args) throws Exception {
    42         Message message = Factory.getInstance("反射.反射应用案例.NetMessage",Message.class);
    43         message.send();
    44         Service instance = Factory.getInstance("反射.反射应用案例.HouseService", Service.class);
    45         instance.service();
    46     }
    47 }

    --此时的工厂设计模式才是所谓的高可用的工厂设计模式,而这种操作的实现依赖的就是泛型.此时的工厂设计模式将不再受限于指定的接口,可以为所有的接口提供实例化对象.

    三.反射与单例设计模式
      单例设计模式的核心本质在于类内部的构造方法私有化,在类的内部产生实例化对象之后在外部通过static方法获取到实例化对象进行类中的结构调用.单例设计模式一共有两种,懒汉式和饿汉式(饿汉式的单例是不再本次的讨论范围之内的,主要讨论懒汉式的单例)
    --范例:观察懒汉式单例的问题

     1 class Singleton {
     2     private static Singleton instance = null;
     3 
     4     private Singleton() {
     5     }
     6 
     7     public static Singleton getInstance() {
     8         if (instance == null) {
     9             instance = new Singleton();
    10         }
    11         return instance;
    12     }
    13 
    14     public void print() {
    15         System.out.println("单例模式加载");
    16     }
    17 
    18 }
    19 
    20 public class LazyLoadDemo {
    21     public static void main(String[] args) {
    22         Singleton singleton = Singleton.getInstance();
    23         singleton.print();
    24     }
    25 }

    --此时我们的操作是在单线程的环境下运行的,如果使用多线程

     1 class Singleton {
     2     private static Singleton instance = null;
     3 
     4     private Singleton() {
     5         System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
     6     }
     7 
     8     public static Singleton getInstance() {
     9         if (instance == null) {
    10             instance = new Singleton();
    11         }
    12         return instance;
    13     }
    14 
    15     public void print() {
    16         System.out.println("单例模式加载");
    17     }
    18 
    19 }
    20 
    21 public class LazyLoadDemo {
    22     public static void main(String[] args) {
    23         for (int i = 0; i < 3; i++) {
    24             new Thread(() -> {
    25                 Singleton.getInstance();
    26             }, "[单例创建者" + (i + 1) + "]").start();
    27         }
    28     }
    29 }

    --运行结果

    [单例创建者1] 实例化单例对象
    [单例创建者2] 实例化单例对象
    [单例创建者3] 实例化单例对象
    
    Process finished with exit code 0

    --单例设计模式最大的特点是在整体运行之中,只允许产生一个实例化对象,当有了若干实例化对象之后,那么就不是单例设计模式了,我们可以大致分析单例模式的运行流程如下:
      1.判断instance是否为空?
      2.如果instance为空,实例化instance对象
      3.返回当前的instance
    --因此在多线程的设计中,每一个线程在执行步骤1的时候都会认为此时的对象为空,那么都会去创建这个对象的实例,这样一来单例设计模式也就失去了意义,如果想要解决这类问题,关键的核心就在于要解决同步处理,而解决同步处理的核心就是使用synchronized关键字

     1 class Singleton {
     2     private static Singleton instance = null;
     3 
     4     private Singleton() {
     5         System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
     6     }
     7 
     8     public static synchronized Singleton getInstance() {
     9         if (instance == null) {
    10             instance = new Singleton();
    11         }
    12         return instance;
    13     }
    14 
    15     public void print() {
    16         System.out.println("单例模式加载");
    17     }
    18 
    19 }
    20 
    21 public class LazyLoadDemo {
    22     public static void main(String[] args) {
    23         for (int i = 0; i < 3; i++) {
    24             new Thread(() -> {
    25                 Singleton.getInstance();
    26             }, "[单例创建者" + (i + 1) + "]").start();
    27         }
    28     }
    29 }

    --运行结果

    [单例创建者1] 实例化单例对象
    
    Process finished with exit code 0

    --此时却是进行了同步处理,但是这个同步的代价却是很大的,因为效率会降低.因为整体代码中实际上只有一块区域需要同步处理,那就是instance对象的实例化处理部分,在这样的情况下同步加的未免显得有些草率,更加合理的进行同步处理:

     1 class Singleton {
     2     //在对象实例化的时候,应该立刻与主内存中的实例对象保持同步,而不应该存在副本
     3     private static volatile Singleton instance = null;  
     4 
     5     private Singleton() {
     6         System.out.println(Thread.currentThread().getName() + " 实例化单例对象");
     7     }
     8 
     9     public static Singleton getInstance() {
    10         synchronized (Singleton.class) { //static方法只能使用Singleton.class
    11             if (instance == null) {
    12                 instance = new Singleton();
    13             }
    14         }
    15         return instance;
    16     }
    17 
    18     public void print() {
    19         System.out.println("单例模式加载");
    20     }
    21 
    22 }
    23 
    24 public class LazyLoadDemo {
    25     public static void main(String[] args) {
    26         for (int i = 0; i < 3; i++) {
    27             new Thread(() -> {
    28                 Singleton.getInstance();
    29             }, "[单例创建者" + (i + 1) + "]").start();
    30         }
    31     }
    32 }
  • 相关阅读:
    Flask--偏函数, 线程安全, 请求上下文
    jQuery中DOM操作
    jQuery简介以及jQuery选择器
    并发编程——操作系统介绍(1)
    面向对象——内置方法
    面向对象——反射
    面向对象——元类
    面向对象——绑定方法与非绑定方法
    面向对象——property
    面向对象——封装
  • 原文地址:https://www.cnblogs.com/skykuqi/p/11421787.html
Copyright © 2011-2022 走看看