一文弄懂反射机制及其应用!| java基础系列

1. 反射

1.1 java代码的运行

在学习反射之前,我们首先要理解我们编写的 **java代码是如何编译运行 **的;它经历了三个阶段:

  • 源代码阶段:
    • 首先假设我们写好了A.java文件
    • 然后,我们编译A.java,编程了A.class文件(可执行文件)(字节码文件)
  • 加载阶段
    • 然后我们要运行A.class文件,首先要加载到内存中(原来是在硬盘上存的来)
  • 运行阶段
    • 加载到内存中,就可以执行啦

1.2 反射基本概念

好,了解了java代码要运行时要经历的三个阶段之后,我们正式开始看反射:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

hh,是不是看得云里雾里的,来让我给大家仔细剖析一下:
反射就是把java类中的各种成分映射成一个个的Java对象
image.png

其实反射,就是通过** 运行中 **的.class文件中的内容(成员变量,方法)映射成一个个Java对象,然后通过.class文件,来反向操作这些对象

2. 快速入门

环境准备:

  • com.hspedu.Cat实体类,类中有一个hi方法(打印“你好,我是–”)
  • 创建.properties文件todo:加链接,里面有俩个信息:Cat类相对路径,方法名hi
classfullpath=com.hspedu.Cat
method=hi
  • 通过字符串 **classfullpath **来获取.properties文件中的Cat类相对路径
  • 通过字符串 **methodName **来获取.properties文件中方法名hi

使用反射来创建Cat对象:

  1. 加载类
// classfullpath是通过Properties类获取的.properties
Class cls = Class.forName(classfullpath);
  1. 通过 cls 的到你加载的类com.hspedu.Cat的对象
Object o = cls.newInstance();
// 拓展一个方法:o.getClass():获取运行类型(o是一个不知道什么类型的对象)
  1. 通过cls得到你加载的类com.hspedu.Cat 的 methodName"hi" 的方法对象

即:在反射中,可以把方法视为对象

Method method = cls.getMethod(methodName);
  1. 通过method 调用的invoke(o)方法,相当于来使用对象o调用方法,即通过方法对象来了实现调用方法
method.invoke(o); // 传统方法,对象.方法(),反射机制:方法.invoke(对象)

附:

Class cls = Class.forName(classfullpath);
Object o = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(o);

这其实也是反射(reflection)的一个重要作用:动态的创建对象并调用方法
作用:通过外部文件配置(.properties等类型文件),在不修改源码情况下,来控制程序,符合设计模式ocp原则(开闭原则:不修改源码,扩容功能)

如何理解呢?
在这个案例中,如果我们修改了文件中method的值(方法名),重新运行项目,就可以运行我们指定的方法啦;注意!这里是重新运行,而不是重新编译,因为我们并没有修改源码,但是项目中对象的功能发生了改变

其实有人肯定会说,如果想要通过文件来实现创建对象调用方法,那么只要通过某种方式将文件中的字符串给转化为包名,然后直接new这个对象不就行了
但样的话,其实本质还是改了源码,这就相当于你直接在源码中new了一个对象,当你名字改变时,你的对象就变了,到时候编译的文件也就自然而然的变了
而使用反射,他是通过编译好的文件,通过反射,映射成一个一个对象,然后操作时,就是寻找我们要操作的东西(类,属性,方法),使我们获取了在运行时分析类以及执行类中方法的能力

3. 获取class对象的三种方式

  1. Class.forName(“全类名”);
  2. 类名.class;
  3. 对象.getClass();

3.1 详解三种方式Class.forName(“全类名”)

  1. Class.forName(“全类名”);
    • 全类名:包名 + 类名
    • 全类名的获取:选中类,然后右击点击copy Prefence
    • 将所有的类都扫一遍,来获取到对应
    • 最常用
  2. 类名.class;
    • 当做参数进行传递
  3. 对象.getClass();
    • 当有了这个类的对象时,才可以使用

4. 通过Class类来获取构造方法

Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组(包括私有的)
Constructor getConstructor(Class<?>...parameterTypes):返回(指定)单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>…parameterTypes):返回(指定)单个构造方法对象

5. 通过Class类来获取成员变量

Field[] getFields():返回所有公共成员变量对象的数组
Field[] getDeclaredFields():返回所有成员变量对象的数组
Field getField():返回(指定)单个公共成员变量对象
Field getDeclaredField():返回所有公共成员变量对象的数组

6. 通过Class类来获取成员方法

Method[] getMethods():返回所有公共成员变量方法的数组
Method[] getMethods():返回所有公共成员变量方法的数组
Method getMethod():返回所有公共成员变量方法的数组
Method getMethod():返回所有公共成员变量方法的数组

7. 作用

作用1:获取一个类里的所有信息,获取到了之后,再执行其他的业务逻辑
作用2:!!结合配置文件,动态的创建对象并调用方法

7.1 获取类信息

public static void saveObject(Object obj) {
	// 1. 获取字节码文件的对象
	Class clazz = obj.getClass();

	// 2. 获取所有的成员变量
	Field[] fields = clazz.getDeclaredFields();
	for (Field field : fields) {
        field.setAccessible(true);
        // 获取成员变量的名字
        String name = field.getName();
        // 获取成员变量的值
        Object value = field.get(obj);
        System.out.println(name + "=" + value);
    }
}

7.2 动态创建对象

这个应用就是快速入门里的那个案例;

8. * 八股

8.1 何为反射?

如果说大家研究过框架的底层原理或者咱们自己写过框架的话,一定对反射这个概念不陌生。
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性
– 好好理解这句话,比任何课程对反射的解释都清楚

8.2 反射的应用场景了解么?

像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。
但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。
这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。
比如下面是通过 JDK 实现动态代理的示例代码,其中就使用了反射类 Method 来调用指定的方法。

public class DebugInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after method " + method.getName());
        return result;
    }
}


另外,像 Java 中的一大利器 注解 的实现也用到了反射。
为什么你使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
这些都是因为你可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。

8.3 谈谈反射机制的优缺点

优点:可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
缺点:让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值