• java每日一记 —— 谈谈反射


    本篇代码在jdk11中测试通过

    1.先来说点前置知识:类的加载机制

    1.博主是这样理解的:Java 类的加载机制就是将类从字节码转换为运行时实例的过程(好吧!!!我是来搞笑的😱😱😱😱)

    2.回归正题:Java 类的加载机制是一个动态过程,由 Java 虚拟机 (JVM) 自动完成

    一般流程如下:

    • 加载:将类从磁盘或其他数据源加载到 JVM 内存中
    • 验证:确保类有效,并符合语义要求
    • 准备:给类变量分配内存,并设置类变量的默认值
    • 解析:将常量池中的符号引用替换为直接引用
    • 初始化:给类变量赋值,并调用类构造函数

    来一张小破图:
    在这里插入图片描述

    开个小玩笑:我怎么越看越像上高速公路的一个过程??
    加载?好像是从四面八方来的车要进入收费站入口
    验证?怎么看都是收费站的要判断你是否符合上高速的要求啊?
    准备?这个有点像给你发的通行卡中记录了你的信息?
    解析?你进入高速匝道后的指示牌给你直接引用到正确的路线?
    初始化?这个不就是进入高速后给车加油门,并且打开我们的bgm?

    3.总结一下:其实吧每个阶段都涉及 JVM 内部的操作,这只是为了确保类的安全性和可用性。因此,在运行时可以通过反射轻松访问 Java 类的信息

    特别注意:今天的主角不是类加载机制,这些只是根据经验写了点

    2.以自己的方式来谈反射的概念

    1.定义:Java反射是一种允许我们在程序运行时访问和修改其自身行为的技术。它允许我们在运行时检查和修改程序的行为,而不需要重新编译或启动程序

    2.面试时我如果按照定义说,面试官肯定肯说我背了八股,所以我用自己的理解说下反射的定义吧:

    • 通俗说法:Java反射是一种能够在运行时检查和修改程序自身行为的能力。它可以让我们在不重新编译或重启应用程序的情况下,对程序的行为进行更改。例如,我们可以使用反射来动态地改变一个类的方法或者字段值,或者动态地创建和访问类的实例

    我来以生活的方式来说:

    首先,让我们从一个简单的例子开始。假设我们正在准备一顿晚餐,并且想要制作一道美味的意大利面。在烹饪过程中,我们需要了解各种食材的特性,比如面条需要煮多久才能达到最佳口感,番茄酱应该如何调配等等。这就像是我们在编程时需要了解每个类和方法的功能一样。

    现在,想象一下如果我们有一本食谱,它不仅告诉我们如何做菜,还告诉我们可以用哪些不同的食材来替代原本的食材,甚至还可以告诉我们每种食材有哪些未知的特性和用途。这就是Java反射的概念

    Java反射允许我们在运行时检查和修改程序的行为。就像我们的食谱一样,它提供了一种方式来查看和操作代码的各种元素,如类、接口、字段和方法。我们可以利用这些信息来创建新的对象、调用方法、改变字段值等

    3.new 和反射的对比

    在这里插入图片描述
    4.java相关类介绍

    类名描述
    Class代表类的实体,在运行的Java应用程序中表示类或者接口
    Field类的成员变量(成员变量也称为类的属性)
    Method类的方法
    Constructor类的构造方法

    3.获取class的三种方式

    3.1.通过已知的类型获取class

    1.上代码

    public Class<User> getUser01(){
        Class<User> clazz = User.class;
        return clazz;
    }
    
    • 1
    • 2
    • 3
    • 4

    2.运行结果
    在这里插入图片描述

    3.2.通过实例对象获取class

    1.上代码

    public Class<User> getUser02(){
        User user = new User();
        Class<?> clazz = user.getClass();
        return (Class<User>)clazz;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.运行结果
    在这里插入图片描述

    3.3.通过Class.forName获取全路径指定类名的class

    1.上代码

    public static Class<User> getUser03()throws ClassNotFoundException{
        Class<?> clazz = Class.forName("com.andy.fan_she.pojo.User");
        return (Class<User>)clazz;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.运行结果
    在这里插入图片描述

    4.整理了一下API:坦言说🪡累

    1.Class常用操作方法

    // 获取所有的构造方法 (private和public都可以)
    public Constructor<?>[] getDeclaredConstructors()
    
    // 获取特定的构造方法 (private和public都可以)
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
       
    // 获取类的父类
    public native Class<? super T> getSuperclass()
    
    // 获取类实现的接口
    private Class<?>[] getInterfaces(boolean cloneArray)
    
    // 获取在类内定义的内部类或接口
    public Class<?>[] getDeclaredClasses()
    
    // 获取所有的方法
    public Method[] getDeclaredMethods() throws SecurityException
    
    // 根据方法名和参数获得特定的方法
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    
    // 获取类型的定义的所有属性
    public Field[] getFields()
    
    // 根据属性命名获得特定的Field
    public Field getField(String name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.Method常用的操作方法

    // 获得方法的放回类型
    public Class<?> getReturnType()
    
    // 获得方法的传入参数类型
    public Class<?>[] getParameterTypes()
    
    // obj是实例对象,args是方法,反过来由Method控制对象的方法调用
    public Object invoke(Object obj, Object... args)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.Field常用的操作方法

    // 属性与obj相等则返回true
    public boolean equals(Object obj)
    
    // 获得obj中对应的属性值
    public Object get(Object obj)
    
    // 设置obj中对应属性值
    public void set(Object obj, Object value)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.Constructor

    // 根据传递的参数创建类的对象:initargs 构造方法参数
    public T newInstance(Object... initargs) 
    
    • 1
    • 2

    ⚠️注意啦!!!注意啦!!!⚠️

    • 从Java 11开始,newInstance()方法已被弃用,因为它存在一定的安全风险。现在推荐使用Class.getDeclaredConstructor().newInstance()的方式来替代

    • 在Java 11及更高版本中,如果没有显式声明默认构造函数,那么默认情况下是不会生成无参数构造函数的。因此,如果要调用newInstance()方法,则需要确保类具有可访问的无参数构造函数

    5.现场玩一把

    来个测试对象类

    /**
     * @author Andy
     * @version 0.0.1
     */
    public class User implements Serializable {
    
        private Integer id;
    
        private String username;
        
    	private string password;
    
        private String password;
        
        public User(Integer id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    
        ...getter/setter
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.根据class创建对象

    public User getUser01() throws Exception {
    	Class<User> clazz = User.class;
        Constructor<User> constructor = clazz.getConstructor(Integer.class, String.class, String.class);
        constructor.setAccessible(true);
        User user = constructor.newInstance(1, "andy", "123456");
        return user;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.由class获取Field,并操作实例的属性

    public static User getUser02() throws Exception{
        Class<User> clazz = User.class;
        Constructor<User> constructor = clazz.getConstructor(Integer.class, String.class, String.class);
        constructor.setAccessible(true);
        User user = constructor.newInstance(1, "andy", "123456");
        // 主要是这块逻辑
        Field declaredField = clazz.getDeclaredField("id");
        declaredField.setAccessible(true);
        declaredField.set(user, 2);
        return user;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.由class获取Method,并反射调用实例方法

    // 在pojo中加入:
    public void getTestUser(String name){
        System.out.println("测试反射方法:" + name);
    }
    
    // 测试方法:
    public static void getUser03() throws Exception {
        Class<User> clazz = User.class;
        Constructor<User> constructor = clazz.getConstructor(Integer.class, String.class, String.class);
        constructor.setAccessible(true);
        User user = constructor.newInstance(1, "andy", "123456");
        //主要逻辑
        Method declaredMethod = clazz.getDeclaredMethod("getTestUser", String.class);
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(user, "Andy测试");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6.反射机制应用的场景

    • 动态拓展:假设有同一组类是实现相同的接口,并且类的加载方式不限制。当我们需要那种具体类实现的功能时,只需加载.class文件,并获取对应的Class对象。可以由Class或者Constructor实例化对象instance;根据接口定义,可以获取Class里的某一方法Method,并配合instance反射调用功能方法
    • Spring的IOC就是基于反射机制实现
    • JDK的动态代理

    博主记得之前写过一个动态代理的博客,今天特地的跑去看了下🪡的是反射机制。有兴趣的可以看下:https://blog.csdn.net/weixin_44702984/article/details/130278266?spm=1001.2014.3001.5502

  • 相关阅读:
    MyBatis之分页查询:MyBatis PageHelper
    PHP 中传值与传引用的区别,什么时候传值什么时候传引用?
    密码密钥硬编码检查
    园区/厂区怎么实现wifi上网短信认证
    (JAVA)认识Java中的数据类型和变量
    用c++补全二维数组问题代码
    机器学习(八) ----------支持向量积(SVM)
    数据模型是什么,有哪些常见的分析方法? 财务、人力、运营等数据分析人士必看!(内附2000+套数据可视化模版)
    node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
    使用vite搭建vue3
  • 原文地址:https://blog.csdn.net/weixin_44702984/article/details/134474801