关于反射机制的个人理解

一、什么是反射?

用光的反射来说明,光在遇到玻璃、水面等其他介质的时候,在分界面上又返回原来物质的一种现象。

二、应用场景

在Java的业务开发中,平常很少用到反射原理,也就不怎么接触到反射机制。但是,因为反射我们才能使用各种框架。例如Spring/Spring Boot 、MyBatis等许多框架中都用到了反射机制。

三、优点和缺点

优点:能够让代码更加的灵活,同时也为各种框架提供了便利。
缺点:我们都知道Java 的特性包括封装,恰巧反射机制就破坏了这种封装机制。同时也增加了一些安全问题。同时,反射的性能也稍微的差一点。不过在框架中这种性能差异显得微乎其微。

四、反射示例

4.1、获取Class对象的方式

通过前面的说明,我们知道一定要通过一种介质才能得到想要的对象。而这个介质就是Class类。
根据官网文档的解释,Class 类的实例表示正在运行的 Java 应用程序中的类和接口。Class 类的实例表示正在运行的 Java 应用程序中的类和接口。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

4.1.1 知道具体类

Class clazz = TargetClass.class ;

我们已知一个类,通过.class这种方式就能获取到对应的Class实例。
但是我们一般是不知道具体的类,通过遍历包下面的类来获取对象,通过此方式获取的Class对象不会进行初始化。

4.1.2 通过Class.forName()传入类的路径

Class clazz = Class.forName(" com.test.TargetClass");

例如在Jdbc的连接中,我们要获取驱动,一般就用这种方式来获取相应的Class实例。

4.1.3 通过对象实例instance.getClass()获取

TargetClass object = new TargetClass();
Class clazz = object.getClass();

4.1.4 通过类加载器传入路径

ClassLoader loader = ClassLoader.getSystemClassLoader();
Class clazz = loader.loadClass("com.base.TargetClass");

通过类加载器加载一个类获取Class对象时不会进行初始化。也就意味着构造方法和类初始化器都不会被执行。只有new对象时,这些才会执行。

五、反射的基本操作

5.1、创建一个用来反射操作的类。

public class Apple {

    private String name ;

    public Apple(){
        name = "苹果";
    }

    public void show( String s ){
        System.out.println( "我最喜欢:" + s );
    }

    private void otherShow(){
        System.out.println( "我是:" + name );
    }
}

5.2 使用反射

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectApple {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        //这是通过前面的四种方式来获取Apple的Class实例
        Class<?> clazz = Apple.class;
        Class<?> clazz2 = Class.forName("com.base.Apple");
        Apple apple1 = new Apple();
        Class<?> clazz3 = apple1.getClass();
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        Class<?> clazz4 = loader.loadClass("com.base.Apple");

        //我们用第一个Class实例获取要操作的类
        Apple apple = (Apple)clazz.newInstance();

        /**
         * 用第二个Class实例获取类中的方法,修饰符为public的方法
         * 返回一个包含某些 Method 对象的数组
         * 如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回长度为 0 的数组。
         */
        Method[] methods = clazz2.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        /**
         * 用第三个Class实例获取类中的字段,由于是private,所以数组的长度为0
         * 返回一个包含某些 Field 对象的数组
         */
        Field[] fields = clazz3.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        //获取指定的方法,同时传入参数类型
        Method showMethod = clazz4.getDeclaredMethod("show" , String.class );
        //调用方法,并传参
        showMethod.invoke( apple , "水果");

        Field field = clazz.getDeclaredField("name");
        //为了对其中类的私有字段进行修改,这里取消安全检查
        //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
        field.setAccessible( true );
        field.set( apple , "柠檬");

        //指定获取私有方法
        Method otherShowMethod = clazz.getDeclaredMethod("otherShow" );
        otherShowMethod.setAccessible( true );
        otherShowMethod.invoke( apple );
    }
}

输出的内容如下
show
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
我最喜欢:水果
我是:柠檬

如输出的结果所示,这里的show方法是我们自己定义的。其他的方法则是继承Object
在遍历方法的时候,并不会获取到私有方法,所以遍历时并不会显示otherShow 这个方法。
注意getDeclaredMethod获取指定方法的时候,如果要操作的方法中带有参数,则要加入相应的参数类型Class实例。

以上内容是参考了书集和官方文档以及自己测试结果所写,全部为个人理解。如果有错误请批评指正。
转载请注明出处,谢谢。

上一篇:B_第01章:Java基础


下一篇:mapper.xml-foreach应用场景: curd-批量处理, 拼接等