这应该是基础吧
- 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 和反射的对比
4.java相关类介绍
类名 | 描述 |
---|---|
Class<T> | 代表类的实体,在运行的Java应用程序中表示类或者接口 |
Field | 类的成员变量(成员变量也称为类的属性) |
Method | 类的方法 |
Constructor<T> | 类的构造方法 |
3.获取class的三种方式
3.1.通过已知的类型获取class
1.上代码
public Class<User> getUser01(){
Class<User> clazz = User.class;
return clazz;
}
2.运行结果
3.2.通过实例对象获取class
1.上代码
public Class<User> getUser02(){
User user = new User();
Class<?> clazz = user.getClass();
return (Class<User>)clazz;
}
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;
}
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)
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