什么是Java反射:作用、使用方法、原理三方面解析

Java反射是Java进阶编程内容之一,掌握反射应用与原理,有助于我们理解一些框架(Spring)和插件(junit)的运行原理。

一、Java反射的作用

简单来说,Java反射主要有两个作用:

  1. 使得程序符合开关原则:实体如类、模块和函数应该对扩展开放,对修改关闭。用户注册公司的平台需要短信验证码,为了防止接入的短信平台故障导致公司的注册业务无法正常营运,常使用1+1做故障转移。这种情况即可使用反射,读取数据库或缓存里的数据,在不修改业务代码的前提下,切换平台。

  2. 不知道实现类的情况下,开发应用或者设计框架:其实这和第一条有点相似,但程序员变成了用户。IDEA的智能提示、Spring的xml配置、junit的测试注解,都大量使用了反射。在正常情况下,我们会使用new关键字实例化一个对象,这是在明确class类的情况下。作为框架的开发者,他们无法得知用户(程序员)使用了什么类,用什么修饰符,类名叫什么,这种情况下,反射的作用就体现出来了。

二、使用方法

1. 反射的使用步骤:

  • Apple类

    public class Apple {
          
          
    	
    	    private int price;
    	
    	    public int getPrice() {
          
          
    	        return price;
    	    }
    	
    	    public void setPrice(int price) {
          
          
    	        this.price = price;
    	    }
    }
    
  • 1.获取类的 Class 对象实例

    Class clz = Class.forName("com.zhenai.api.Apple");
    
  • 2.根据 Class 对象实例获取 Constructor 对象

    Constructor appleConstructor = clz.getConstructor();
    
  • 3.使用 Constructor 对象的 newInstance 方法获取反射类对象

    Object appleObj = appleConstructor.newInstance();
    
  • 4.而如果要调用某一个方法,则需要先获取对象,再使用对象调用invoke方法:

    Method setPriceMethod = clz.getMethod("setPrice", int.class);
    setPriceMethod.invoke(appleObj, 14);
    
  • 完整代码

    public class Apple {
          
          
    
        private int price;
    
        public int getPrice() {
          
          
            return price;
        }
    
        public void setPrice(int price) {
          
          
            this.price = price;
        }
    
        public static void main(String[] args) throws Exception{
          
          
            //new 关键字实例化
            Apple apple = new Apple();
            apple.setPrice(5); // 调用方法
            System.out.println("Apple Price:" + apple.getPrice());
            //使用反射调用
            Class clz = Class.forName("com.chenshuyi.api.Apple");
            Method setPriceMethod = clz.getMethod("setPrice", int.class);
            Constructor appleConstructor = clz.getConstructor();
            Object appleObj = appleConstructor.newInstance();
            setPriceMethod.invoke(appleObj, 14);  //调用方法
            Method getPriceMethod = clz.getMethod("getPrice");
            System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
        }
    }
    
  • 输出

    Apple Price:5
    Apple Price:14
    

到这里,我们已经能够掌握反射的基本使用。除了这些方法,Java还提供了其他的反射API

2.Java反射API
获取反射中的Class对象在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。在 Java API 中,获取 Class 类对象有三种方法:

  • 第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

    Class clz = Class.forName("java.lang.String");
    
  • 第二种,使用 .class 方法。这种方法只适合在编译前就知道操作的 Class。

    Class clz = String.class;
    
  • 第三种,使用类对象的 getClass() 方法。

    String str = new String("Hello");
    Class clz = str.getClass();
    

通过反射创建类对象主要有两种方式:

  • 通过 Class 对象的 newInstance() 方法。

    Class clz = Apple.class;
    Apple apple = (Apple)clz.newInstance();
    
  • 通过 Constructor 对象的 newInstance() 方法

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor();
    Apple apple = (Apple)constructor.newInstance();
    
  • 通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor(String.class, int.class);
    Apple apple = (Apple)constructor.newInstance("红富士", 15);
    

通过反射获取类属性、方法、构造器

  • 我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

    Class clz = Apple.class;
    Field[] fields = clz.getDeclaredFields();
    for (Field field : fields) {
          
          
        System.out.println(field.getName());
    }
    
  • 输出结果是:name

price与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

(三)、反射原理

反射原理本质上还是运用的Java面向对象的思想,简单来说,JDK把我们的类当作一个对象,把对象分解为一个个的属性,并提供一些方法或者辅助对象操作属性。具体的解析放一篇
某乎链接
供大家深入了解。

最后贴一点反射的优缺点:
优点:

  • 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
  • 与 Java 动态编译相结合,可以实现无比强大的功能。
  • 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点:

  • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;(据我所知,目前已经有一些插件可以缓解资源问题)
  • 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。

猜你喜欢

转载自blog.csdn.net/qq_44491709/article/details/114301583