java每日一记 —— 谈谈反射

这应该是基础吧

  • 1.先来说点前置知识:类的加载机制
  • 2.以自己的方式来谈反射的概念
  • 3.获取class的三种方式
    • 3.1.通过已知的类型获取class
    • 3.2.通过实例对象获取class
    • 3.3.通过Class.forName获取全路径指定类名的class
  • 4.整理了一下API:坦言说累
  • 5.现场玩一把
  • 6.反射机制应用的场景

本篇代码在jdk11中测试通过

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

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

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

一般流程如下:

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

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

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

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

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

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

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

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

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

我来以生活的方式来说:

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

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

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

3.new 和反射的对比

java每日一记 —— 谈谈反射_第1张图片
4.java相关类介绍

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

3.获取class的三种方式

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

1.上代码

public Class<User> getUser01(){
    Class<User> clazz = User.class;
    return clazz;
}

2.运行结果
java每日一记 —— 谈谈反射_第2张图片

3.2.通过实例对象获取class

1.上代码

public Class<User> getUser02(){
    User user = new User();
    Class<?> clazz = user.getClass();
    return (Class<User>)clazz;
}

2.运行结果
java每日一记 —— 谈谈反射_第3张图片

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;
}

2.运行结果
java每日一记 —— 谈谈反射_第4张图片

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)

2.Method常用的操作方法

// 获得方法的放回类型
public Class<?> getReturnType()

// 获得方法的传入参数类型
public Class<?>[] getParameterTypes()

// obj是实例对象,args是方法,反过来由Method控制对象的方法调用
public Object invoke(Object obj, Object... args)

3.Field常用的操作方法

// 属性与obj相等则返回true
public boolean equals(Object obj)

// 获得obj中对应的属性值
public Object get(Object obj)

// 设置obj中对应属性值
public void set(Object obj, Object value)

4.Constructor

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

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

  • 从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.根据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;
}

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;
}

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测试");
}

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

你可能感兴趣的:(java每日一记,java,开发语言)