当前位置: 首页 > news >正文

java核心基础之java反射机制详解

(一)什么是反射?

反射就是将类的各个组成部分封装为其他对象。

在详细了解反射机制之前,我们先来了解一下java代码在计算机中的运行过程:

比如当我们编写好一个类:Student.java,里面包含学生的姓名和年龄,构造方法,其他方法。

第一个阶段:Source阶段

javac会把我们写的代码编译成.class字节码文件,保存在硬盘中,这个文件中保存着该类的类名,成员名,构造方法等等。

第二个阶段:Class阶段

Class阶段会把字节码文件中的信息转化成class类对象,比如成员变量用Field[]保存,构造方法用Constructor[]保存,成员方法用Method[]保存

第三个阶段:Runtime阶段

通过new Student(),根据第二个阶段的类对象创建出Student对象

这里的第二个阶段,将类的各个组成部分封装为其他对象就是反射机制。

反射的好处:

1.可以在程序运行过程中操作对象。

2.解耦,提高程序可扩展性。

(二)获取字节码Class对象的三种方式

获取Class对象有三种方式,分别对应于前面的三个阶段:

1.对应于第一个阶段的方法是将字节码文件加载进内存中:

class.forname("全类名");

2.第二个阶段已经生成了class类对象,因此方法如下:

类名.class;

3.第三个阶段生成了对象,方法如下:

对象.getclass();

注意:

同一个字节码文件(.class)在一次程序运行过程中只会被加载一次,通过以上三种方法创建的class对象是同一个。

通过代码演示:

新建Student类:

public class Student {
    private String name;
    private int age;
    //方便后期测试的成员变量
    public int a;
    public Student(){}
    public Student(String name, int age,int a) {
        this.name = name;
        this.age = age;
        this.a=a;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + a +
                '}';
    }
}

测试获取Class对象的三种方式

public class reflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //方法一
        Class cls1 = Class.forName("com.sdxb.reflect.Student");
        System.out.println(cls1);
        //方法二
        Class cls2 = Student.class;
        System.out.println(cls2);
        //方法三
        Student student=new Student();
        Class cls3 = student.getClass();
        System.out.println(cls3);
        //判断是否是同一对象
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3);
    }
}

(三)Class获取对象方法

1.1 获取成员变量

Field getField(String name) //获取指定名称public修饰的成员变量
Field[] getFields()  //获取所有public修饰的成员变量
Field getDeclaredField(String name) //获取指定名称成员变量
Field[] getDeclaredFields()  //获取所有成员变量

1.2 操作成员变量

Object get(Object obj)  //通过Field获取对象
void set(Object obj, Object value)  //修改Field的值

通过代码演示

public class FieldTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //1.获取所有public的成员变量
        Field[] fields = cls.getFields();
        for (Field field:fields) {
            System.out.println(field);
        }
        //2.获取指定名字的public成员变量
        Field a = cls.getField("a");
        Student student=new Student();
        //3.操作Field的方法,get和set
        System.out.println(a.get(student));
        a.set(student,10);
        System.out.println(student);
        //4.获取所有成员变量
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }
}

这里的两个操作Field的方法只能操作public修饰的变量,如果需要访问其他修饰符修饰的元素,则要添加安全许可:

a.setAccessible(true);

2.1 获取构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)  //根据参数不同获取指定的public构造方法
Constructor<?>[] getConstructors()  //获取所有public构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //根据参数不同获取指定的构造方法
Constructor<?>[] getDeclaredConstructors() //获取所有构造方法

2.2 操作构造方法

T newInstance(Object... initargs)  //创建对象

通过代码演示其中一两种操作:

public class reflectTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //有参构造方法
        Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
        Object student = constructor.newInstance("sdxb", 24, 1);
        System.out.println(student);
        //无参构造方法
        Constructor constructor2 = cls.getConstructor();
        Object student2 = constructor2.newInstance();
        System.out.println(student2);
        //无参构造方法可以用下面的方式代替
        cls.newInstance();
    }
}

3.1 获取成员方法

Method getMethod(String name, Class<?>... parameterTypes) //根据名称和参数类型获取public方法
Method[] getMethods()  //获取所有public方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //根据名称和参数类型获取方法
Method[] getDeclaredMethods()  //获取所有方法

3.2 成员方法的操作

Object invoke(Object obj, Object... args)  //执行成员方法

为了方便测试,我为Student类增加了两个成员方法:

public void run(){
    System.out.println("run");
}
public void run(int speed){
    System.out.println("run"+speed);
}

接着对成员方法的反射进行测试:

public class reflectTest3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class cls = Student.class;
        Student student=new Student();
        //无参方法
        Method run = cls.getMethod("run");
        run.invoke(student);
        //带参数方法
        Method run2=cls.getMethod("run", int.class);
        run.invoke(student,1);
    }
}

(四)总结

java的反射机制在框架中应用十分广泛,被誉为是框架的灵魂。原因是框架是一个半成品,我们无法通过new去创建框架中定义的类,因此反射起到了很大的作用。

相关文章:

  • Android Pdf文档的生成、显示与打印
  • java核心基础之代理机制详解(静态代理、动态代理:JDK、CGlib)
  • Spring事务管理详解(传播属性、隔离级别)
  • 5分钟学会使用Less预编译器
  • RabbitMQ学习系列(一):RabbitMQ的了解安装和使用
  • RabbitMQ学习系列(二):简单队列详解
  • spring学习笔记(4)依赖注入详解
  • RabbitMQ学习系列(三):工作队列详解
  • RabbitMQ学习系列(四):发布-订阅模型详解
  • Android进阶学习
  • RabbitMQ学习系列(五):routing路由模式和Topic主题模式
  • RabbitMQ学习系列(六):RabbitMQ消息确认机制
  • cisco 交换机自动备份配置
  • 应届毕业生因为疫情休息在家,可以通过哪些途径提高自己?
  • APP产品交互设计分析总结(不断更新中...)
  • ES6系统学习----从Apollo Client看解构赋值
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JavaScript设计模式系列一:工厂模式
  • JS实现简单的MVC模式开发小游戏
  • k8s如何管理Pod
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • vue--为什么data属性必须是一个函数
  • 盘点那些不知名却常用的 Git 操作
  • 前端知识点整理(待续)
  • 容器服务kubernetes弹性伸缩高级用法
  • 实战|智能家居行业移动应用性能分析
  • 微服务核心架构梳理
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 小程序测试方案初探
  • 由插件封装引出的一丢丢思考
  • puppet连载22:define用法
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​十个常见的 Python 脚本 (详细介绍 + 代码举例)
  • ​字​节​一​面​
  • # dbt source dbt source freshness命令详解
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #ubuntu# #git# repository git config --global --add safe.directory
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • (2024.6.23)最新版MAVEN的安装和配置教程(超详细)
  • (4)事件处理——(7)简单事件(Simple events)
  • (6)STL算法之转换
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (Python) SOAP Web Service (HTTP POST)
  • (python)数据结构---字典
  • (zhuan) 一些RL的文献(及笔记)
  • (笔记自用)LeetCode:快乐数
  • (多级缓存)缓存同步
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (回溯) LeetCode 40. 组合总和II
  • (六)DockerCompose安装与配置