反射的入门

目录

1.什么是反射

1.1概念

1.2反射的使用场景(为什么需要用到反射)

1.3入门反射案例

1.3.1Code

2.反射原理

3.Class类分析

4. 得到Class类对象的四种方法,在三个阶段都可以获取到

5.类的静态加载和动态加载的区别

5.类加载的阶段


1.什么是反射

1.1概念

        反射就是程序执行期间,可以通过反射相关的API动态地获取类的各种信息(如字段,方法,构造函数等),并可以利用这些信息完成一些操作(如操作属性和执行方法等)。

        通过反射,我们可以在不知道具体类名的情况下,获取类的全限定名,并通过Class.forName(全类名)方法获取对应的Class对象。然后,你可以使用Class对象获取类的各种信息,如类的修饰符、父类、接口、字段、方法和构造函数等。

1.2反射的使用场景(为什么需要用到反射)

  1. 动态加载类:通过反射可以根据全类名动态地加载类,而不需要在编译时就确定要使用的类。
  2. 运行时获取类的信息:通过反射可以获取类的结构信息,包括字段、方法、构造函数等,这样可以在运行时动态地操作类的属性和方法。
  3. 运行时创建对象实例:反射提供了创建对象实例的方法,可以在运行时动态地实例化对象。
  4. 动态代理:反射可以配合动态代理技术实现,在运行时动态地生成代理类,从而在不修改原有代码的情况下,实现对目标对象的增强或拦截。
  5. 框架和工具开发:许多Java框架和工具,如Spring、Hibernate等,都是基于反射机制实现的。它们利用反射可以在运行时动态地加载和管理类,实现高度灵活和可配置化的功能。
  6. 许多设计模式中也有用到反射。

1.3入门反射案例

情景模拟:通过一个配置文件---properties文件完成Cat对象实例的创建和对象内hi方法的调用

传统的我们想要调用一个对象的方法通常是new出该对象的实例,然后通过 实例.方法() 完成调用,但是当前的情景是我们不知道这个类具体是什么以及类中有什么方法可以调用

情景中properties文件和类的创建:

1.3.1Code
package com.hua.reflection;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 根据re.properties指定的信息创建Cat对象并调用里面的hi方法
 * 通过反射机制创建一个Cat对象并且调用Cat类里面的hi方法
 */
public class TestReflection {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String filePath = "src\\main\\resources\\re.properties";
        //创建Properties对象,用来获取全类名和方法
        Properties properties = new Properties();
        properties.load(new FileReader(filePath));

        String classfullpath = properties.getProperty("classfullpath");//全类名
        String methodName = properties.getProperty("method");//方法名

        //使用反射机制
        //1. 通过全类名得到一个类对象(加载类)
        Class cls = Class.forName(classfullpath);
        //2. 通过cls可以得到你加载的这个类对象的实例 相当于可以new一个实例,但是反射用的不是new
        Object cat = cls.newInstance();//Instance:例子,实例
        System.out.println("运行类型:" + cat.getClass());//class com.hua.reflection.Cat
        //现在我们有了对象实例cat了,但是还不知道调用什么方法

        //3. 通过getMethod()方法来得到  这个类中的methodName(这里实际为hi)方法的对象
        //3.1 也就是说在反射中,万物皆对象,这里就是把方法视为了对象
        Method method = cls.getMethod(methodName);

        //4.这里我们就有了实例和方法了,就可以调用实例的方法了,但是反射中的调用与传统的调用是相反的
        //传统:实例.方法();-----------反射:方法.invoke(实例);
        method.invoke(cat);            //invoke---唤起,激活,调用,实施
    }
}

运行后可以看到我们通过反射的方法实现了方法的调用:

几乎在所有框架中会用到反射机制,有了反射机制我们就可以不用修改源码,只修改配置文件即可达到目的。

例如:现在有个需求是我们想要运行的是Cat对象中的另一个方法,我们就可以不修改源码,仅修改配置文件即可。

这里传统的我们要想实现这个需求,就必须要修改源码,改变实例的调用,但是用反射机制我们只需要修改properties文件里面的方法就行了,如下


运行结果


可以实现不用修改源码就能改变程序运行结果,在框架学习中这非常常见,因为框架学习中有非常多的配置文件和注解都是都是用的反射机制。

2.反射原理

一个Java程序在计算机内有三个阶段,分别是编译阶段、类加载阶段、运行阶段。

我们编写好的.java程序在编译阶段通过Javac编译成.class字节码文件,这个字节码文件中就包含程序中一些类的信息。当我们在运行阶段使用new关键字创建对象的时候,对应类就会发生类加载过程,也就是类加载阶段,在这个加载阶段就会将字节码文件加载到内存堆里面,也会生成一个Class类对象(一个类只有一个Class对象),这个对象里面就有这些类的信息(如成员变量,注解,方法,构造器等);.class字节码文件存入内存堆中的过程使用到了类加载器(ClassLoader),它会将类的一些信息当作对象映射        Class类对象中,类加载过程完成后对象就被创建出来了,创建好的对象存保在堆中;并且这个类创建的对象知道它自己是属于哪一个Class类对象的,也就是我们可以通过这个对象得到Class对象,然后得到这个Class对象了就可以做很多事,就是通过反射做事(如创建对象,调用方法等)。

图取自韩顺平老师 

3.Class类分析

1、Class类也是一个类,只是他的名字有点绕,这个类的名字就叫类,因此它也继承Object类;

2、Class类不是new出来的,而是系统自己创建的,Class类是通过类加载器(ClassLoader)中的 loadClass() 方法加载然后生成对应类的Class类对象;

3、对于某个类的Class类对象,在内存中只有一个,因为对应Class类只加载一次;

4、通过一个Class对象可以得到一个类的结构信息;

5、Class对象是存放在堆里面的;

6、类的字节码二进制文件是存放在方法区(元空间)里面的,有的称为类的元数据(类的方法,字段,访问权限等信息);也就是说类加载过程完成后,堆里面会生成Class类对象,同时在方法区里面会生成二进制数据。

4. 得到Class类对象的四种方法,在三个阶段都可以获取到

package com.hua.reflection;

public class GetClass {
    //获取类对象的四种方式
    public static void main(String[] args) throws Exception {
        String filePath = "com.hua.reflection.Cat";

        //一、编译阶段我们可以用.forName()来获取
        Class<?> cls1 = Class.forName(filePath);
        System.out.println(cls1.hashCode());

        //方式二、类加载过程我们可以用类名.class获取
        Class<Cat> cls2 = Cat.class;
        System.out.println(cls2.hashCode());

        //方式三、我们可以在运行阶段用对象.getClass()获取
        Cat cat = new Cat();
        Class cls3 = cat.getClass();
        System.out.println(cls3.hashCode());

        //方式四、我们可以在类加载过程中获取
        //4.1先得到类加载器ClassLoader
        ClassLoader classLoader = cat.getClass().getClassLoader();
        //4.2再通过类加载器得到Class对象
        Class cls4 = classLoader.loadClass(filePath);
        System.out.println(cls4.hashCode());

        System.out.println("===========cls1 == cls2 == cls3 == cls4===========");
    }

}

运行结果:可以看到他们得到的都是同一个对象

5.类的静态加载和动态加载的区别

静态加载:在编译的时候就发生了加载类,比如静态加载的时候用到的类一定要存在,否则无法通过编译,依赖性很强;如new对象,子类继承父类都属于静态加载。

动态加载:在运行的时候才会加载类,比如编译过程中用到了这个类,但实际这个类不存在,但是可以通过编译,只有在运行的时候才会加载这个类并给出错误异常信息,反射就是一种动态加载类的机制。

5.类加载的阶段

类的加载也分为三个阶段,分为加载(Loading),连接(Linking),初始化(Initialization)三个阶段。

1、加载阶段(Loading):JVM在这个阶段的主要目的是将类的字节码数据以二进制形式保存到内存中,并生成一个代表该类的java.lang.Class对象;

2、连接阶段(Linking):连接阶段中也有三个阶段,分别是验证(Verification),准备(Preparation),解析(Resolution)

  • 验证(Verification):此阶段目的是为了检测字节码文件中是否符合java的一些规范,如安全规范,文件格式校验,元数据验证、字节码验证和符号引用验证等
  • 准备(Preparation):JVM会在该阶段对静态变量(一定是静态变量,实例变量不会在该阶段分配内存并赋初值)进行默认初始化(不管代码中赋值情况,除非是final,都赋默认值,如0,0L,null,false),这些变量所使用的内存都在方法区里面进行分配。(如果被final修饰,如static final int num=10;那么在准备阶段(Preparation)结束后,num的初值是10而不是0,因为它是一个常量,在编译时刻就能确定它的值)
  • 解析(Resolution):JVM将方法区中的符号引用替换为直接引用。在Java中,符号引用是一种用来描述被引用的目标的据数结构,并且与虚拟机实现的内存布局无关。而直接引用则是可以直接指向目标的指针、相对偏移量或者能够间接定位到目标的句柄。在JVM启动时,类加载器会将字节码文件中的符号引用转换为直接引用,这个过程就是解析。通俗来说,就是在类的字节码被装入内存中后,如果该类所依赖的其他类还没有被装入内存中,则这些类的符号引用就无法直接转化为可以调用的直接引用。当程序运行到需要使用这些类的时候,JVM会通过先前已经加载的类信息来动态地确定被引用的类的位置和方法入口地址,将符号引用转变为直接引用,使得类可以正确链接,并且可以运行,(个人感觉类似动态加载,需要的时候才去找)。简单理解:符号引用是一个标示符号,代表着一个内存地址;直接引用是一个真实的内存地址。在类加载时,类装载器读取到类的二进制数据后,会将其中的符号引用替换为直接引用,以便快速访问类中的成员和方法。

3、初始化(Initialization):到初始化阶段,才开始真正开始执行Java程序中的代码,此阶段中client()方法会收集类中所有静态变量和静态代码块语句,进行合并。例如:一个类中有一个静态变量赋值------static int num = 10; 在连接阶段,num的值是默认值0,初始化阶段就是将num的值赋为10的过程

图取自韩顺平老师

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值