Java反射编程

Java反射编程

认识反射机制

1、反射机制简介

在Java语言里面之所以会有如此众多的开源技术支撑,很大的一部分是来自于Java最大的特征——反射机制

所有的技术实现的目标只有一点:重用性

对于反射技术首先来考虑的是“反”与“正”的操作,所谓的“正”操作指的是当我们要使用一个类的时候,一定要先导入包, 而后根据类进行对象的时候,并且依靠对象调用类中的方法,但是如果说“反”,根据实例化对象反推出其类型

如果要实现 反 的处理,那么首先要采用Object类中所提供的方法:

  • 获取类对象信息:public final CLass<?> getClass()

Class对象的使用

public class JavaDemo {
   public static void main(String[] args) {
      String string = new String();
      System.out.println(string.getClass());
   }
}

getClass()可以帮助使用者找到对象的根源

2、Class类对象的三种实例化模式

反射之中所有的核心操作都是通过Class类对象展开的,可以说Class类是:反射操作的根源所在,但是这个类如果要想获取它的实例化对象,可以采用三种方式完成

Class类定义:

  • public final class Classextends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement

从JDK1.5开始Class类在定义的时候可以使用泛型进行标记,这样的用法这样的用法主要是希望可以避免所谓的向下转型

1、Object类支持

class Person{}
public class JavaDemo {
   public static void main(String[] args) {
      Person person = new Person();
      Class<? extends Person> aClass = person.getClass();
      System.out.println(aClass);
      System.out.println(aClass.getName());
   }
}
//class com.xialuote.Person
//com.xialuote.Person

如果只是要获得Class类对象,则必须先产生指定类对象后才可以获得

2、JVM支持(类.class)

class Person{}
public class JavaDemo {
   public static void main(String[] args) {
      Class<? extends Person> aClass = Person.class;
      System.out.println(aClass);
      System.out.println(aClass.getName());
   }
}

前提是需要导包

3、Class类支持

Class类提供的static方法:

public static Class<?> forName(String className)throws ClassNotFoundException

使用该方法:

public class JavaDemo {
   public static void main(String[] args) throws ClassNotFoundException {
      Class<?> aClass = Class.forName("com.xialuote.domain.Student");
      System.out.println(aClass);
      System.out.println(aClass.getName());
   }
}

这种模式最大的特点是可以直接采用字符串的形式定义要使用的类型,并且程序中不需要编写任何的import语句

反射应用案例

1、反射实例化对象

通过newInstance方法实例化Person类对象

public class Person {
   //任何情况下如果要实例化对象一定要调用类中的构造方法
   public Person() {
      System.out.println("这里是Person的构造方法");
   }
   
   @Override
   public String toString() {
      return "这里是Person的toString方法";
   }
}

测试类

public class JavaDemo {
   public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
      Class<?> aClass = Class.forName("com.xialuote.domain.Person");
      Object o = aClass.newInstance();
      System.out.println(o);
   }
}
//这里是Person的构造方法
//这里是Person的toString方法

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

当然,该方法在JDK1.9之后就被弃用了

因为默认的Class类中的newInstance()方法只能够调用无参构造,所以有开发者认为描述不准确,所以将其改变了形式

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<?> aClass = Class.forName("com.xialuote.domain.Person");
      Object obj = aClass.getConstructor().newInstance();
      System.out.println(obj);
   }
}

2、反射与工厂设计模式

为什么要提供一个反射的实例化?到底是使用关键字new还是反射呢?

可以通过工厂设计模式来解决,工厂设计模式的最大特点:客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象

传统的工厂设计模式

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      IMessage message = new MessageImpl();
   }
}

interface IMessage{
   void send();
}
class MessageImpl implements IMessage{
   @Override
   public void send() {
      System.out.println("发送消息!");
   }
}

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

利用工厂设计模式解决问题

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      IMessage message = Factory.getInstance("netMessage");
      message.send();
   }
}

interface IMessage{
   void send();
}
class MessageImpl implements IMessage{
   @Override
   public void send() {
      System.out.println("发送消息!");
   }
}
class Factory{
   private Factory() {}    //没有产生实例化对象的意义,所以构造方法直接私有化
   public static IMessage getInstance(String className){
      if ("netMessage".equals(className)){
         return new MessageImpl();
      }
      return null;
   }
}

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

例如如下代码

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      IMessage message = Factory.getInstance("cloudMessage");
      message.send();
   }
}

interface IMessage{
   void send();
}
class MessageImpl implements IMessage{
   @Override
   public void send() {
      System.out.println("发送消息!");
   }
}
class CloudMessageImpl implements IMessage {
   
   @Override
   public void send() {
      System.out.println("发送消息:Cloud");
   }
}
class Factory{
   private Factory() {}    //没有产生实例化对象的意义,所以构造方法直接私有化
   public static IMessage getInstance(String className){
      if ("netMessage".equals(className)){
         return new MessageImpl();
      } else if ("cloudMessage".equals(className)){
         return new CloudMessageImpl();
      }
      return null;
   }
}

工厂设计模式最有效解决的是子类与客户端的耦合问题,但是解决的核心思想是在于提供有一个工厂类作为过渡端,但是随着项目的进行IMessage接口有可能会有更多子类,那么每次有新的子类增加,工厂类就要作出修改,如果一直增加,那将永无止境的修改

此时最好的解决方法就是不使用关键字new来完成

而new Instance()方法只需要有一个类名称的字符串就可以使用

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      IMessage message = Factory.getInstance("com.xialuote.CloudMessageImpl");
      message.send();
   }
}

interface IMessage{
   void send();
}
class MessageImpl implements IMessage{
   @Override
   public void send() {
      System.out.println("发送消息!");
   }
}
class CloudMessageImpl implements IMessage {
   
   @Override
   public void send() {
      System.out.println("发送消息:Cloud");
   }
}
class Factory{
   private Factory() {}    //没有产生实例化对象的意义,所以构造方法直接私有化
   public static IMessage getInstance(String className){
      IMessage instance = null;
      try {
         instance = (IMessage) Class.forName(className).getDeclaredConstructor().newInstance();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
      return instance;
   }
}

对于使用反射机制实现的工厂设计模式,最大的优势在于对于接口子类的扩充将不再影响到工厂类的定义

在实际的项目开发过程之中有可能会存在有大量的接口,并且这些接口都可能需要工厂类实例化,所以此时的工厂设计模式不应该只为一个IMessage接口服务,而应该变为为所有的接口服务

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      IMessage message = Factory.getInstance("com.xialuote.MessageImpl" , IMessage.class);
      message.send();
      IService service = Factory.getInstance("com.xialuote.MessageServiceImpl" , IService.class);
      service.service();
   }
}

interface IMessage{
   void send();
}
class MessageImpl implements IMessage{
   @Override
   public void send() {
      System.out.println("发送消息!");
   }
}

interface IService{
   void service();
}
class MessageServiceImpl implements IService {
   
   @Override
   public void service() {
      System.out.println("为消息服务!");
   }
}

class Factory{
   private Factory() {}    //没有产生实例化对象的意义,所以构造方法直接私有化
   
   /**
    * 获取接口实例化对象
    * @param className className 接口的子类
    * @param clazz 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) {
         throw new RuntimeException(e);
      }
      return instance;
   }

此时的工厂设计模式依靠于泛型将不再受限于指定的接口,可以为所有的接口提供实例化服务

3、反射与单例设计模式

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

懒汉式单例设计模式问题

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      for (int i = 0; i < 3; i++) {
         new Thread(() -> {
            Singleton.getInstance().print();
         } , "单例设计模式 - " + i).start();
      }
   }
}

class  Singleton {
   private static Singleton instance = null;
   private Singleton (){
      System.out.println("单例模式实例化成功");
   }
   public static Singleton getInstance() {
      if (instance == null) {
          instance = new Singleton();
      }
      return instance;
   }
   public void print(){
      System.out.println("输出消息");
   }
}
//单例模式实例化成功
//输出消息
//单例模式实例化成功
//输出消息
//单例模式实例化成功
//输出消息

单例设计模式的最大特点是在整体的运行过程之中只允许产生一个实例化,但是在多线程之中,依然会被创建多个实例化对象,所以这种单例设计模式明显是有问题的。

解决当前问题的关键在于同步处理,同步自然会想到synchronized关键字

修改getInstance()方法

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

这个时候的确是进行了同步处理,但是这个同步的代价有些大,因为效率会变低。因为整体代码里面实际上只有一块部分需要进行同步处理,instance对象的实例化处理部分。因此synchronized并不能完美的解决问题

合理的进行同步处理

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      for (int i = 0; i < 3; i++) {
         new Thread(() -> {
            Singleton.getInstance().print();
         } , "单例设计模式 - " + i).start();
      }
   }
}

class  Singleton {
   private static volatile Singleton instance = null;
   private Singleton (){
      System.out.println("单例模式实例化成功");
   }
   public static synchronized Singleton getInstance() {
      if (instance == null) {
         synchronized (Singleton.class){
            if (instance == null) {
               instance = new Singleton();
            }
         }
      }
      return instance;
   }
   public void print(){
      System.out.println("输出消息");
   }
}
//单例模式实例化成功
//输出消息
//输出消息
//输出消息

面试题:

  • 请编写单例设计模式?编写一个饿汉式的单例设计模式,并且实现构造方法私有化
  • 在Java中那里使用到了单例设计模式?Runtime类、Spring框架
  • 懒汉式单例设计模式中存在的问题? 多线程问题,答案为上述代码

下一篇:反射与类操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MyRedScarf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值