Java反射(原理剖析与使用)

article/2025/9/21 3:53:46

一、反射机制是什么

1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
2、Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

二、反射的作用

我先讲两个案例来说明反射的重要性

大家可以通过案例过个眼熟,只需看懂有什么用即可,关于反射的知识,我会再后面一一讲解

案例1、通过配置文件的全类名,调用该类方法

在众多都框架中使用到了反射,能动态的生成一个对象,并操作该对象的属性与方法,最经典的就是各种注解和Spring框架的AOP等等

没有用到反射机制的时候,我们只能通过硬编码的方法更改代码中的逻辑,虽然这样的确是可行的

但是对于一个超大的项目来说,这样的维护非常耗成本,并且框架就得不到通过配置动态的更新,框架就会变得累赘,Java 根本也就流行不起来

(1)、配置一个properties文件,也可以用xml来配置

class=com.zhao.reflectdemo.pojo.Cat
name=小白

(2)、写两个实体类

Cat类
// @Data是LomBok的注解,如果不熟悉的小伙伴,可以认为他可以自动生成get、set方法等等
@Data
public class Cat {private String name;private Integer age;private Date brith;private String character;// 以下随便取的字段private String a;private String b;private String c;private String d;public void call(String name){this.name = name;System.out.println("猫儿:" + this.name + "在叫");}
}
Dog类
@Data
public class Dog {private String name;private Integer age;private Date brith;private String character;// 以下随便取的字段private String a;private String b;private String c;private String d;public void call(String name){this.name = name;System.out.println("狗儿:" + this.name + "在叫");}
}

(3)、测试:

    // 尝试用正常方法从配置文件中通过获取权类名,然后调用该类的call()方法// @Test为junit的注解,一般用于单元测试,因为一个类只有一个main方法,这个注解就是让该方法可以达到main方法的作用@Testpublic void test() throws Exception{// 从 properties 取出数据Properties properties = new Properties();properties.load(new InputStreamReader(Files.newInputStream(Paths.get("src\\main\\resources\\application.properties")),StandardCharsets.UTF_8)); // 不要在意我写的文件方法路径String className = properties.getProperty("class"); //目前值为:com.zhao.reflectdemo.pojo.CatString name = properties.getProperty("name"); //目前值为:小白properties.clone(); // 关闭流// 正常方法中我们可以通过全类名,new一个对象,调用该类的方法Cat cat = new com.zhao.reflectdemo.pojo.Cat();cat.call(name); // 输出:猫儿:小白在叫// 那不通过反射我们可以,可以得到对象,并输出call()方法吗
//        Cat cat2 = new className(); // 当然是不可以的 className 指向的是一个字符串,并非一个类,使用这里肯定是会报错的
//        cat2.call(dogName);// 所以我们的主角 Class类 反射登场了common(className,name);// 更改 className 为 Dog类 全类名,当然可以在properties文件中更改,我这里就简化一下,免得本文冗长className = "com.zhao.reflectdemo.pojo.Dog";common(className,name);}// 通用方法,我们就可以知道,调用同样的方法,通过反射机制,就可以等到不同结果// 最大的不同就是,我们只通过两个字符串就实现了两个对象的方法private void common(String className,String name) throws Exception{Class<?> animalClass = Class.forName(className); // 通过全类名 得到Class 对象Object o = animalClass.newInstance(); // 生成该Cat类的对象Method call = animalClass.getMethod("call",String.class); // 得到 Method 对象,这个里面就是存储的该类的方法,因为我写的带参,所以这里也带String类的参数call.invoke(o,name);}

(4)、结果:

在这里插入图片描述

案例2、通过反射更改现有对象的属性

目前我们想用案例1中的Cat类,在初始化时,他的所有属性皆不是基本类型,所有默认初始值为null

我们想获取该存在的类并改变它的名字(name属性)为:大白,其他字符从null,改为""

如果不使用反射的话,我们就是一个个的通过set方法,改变该对象的属性,这样是可行的,但代码是不是会比较的冗长呢

进阶:如果说该类的熟悉为private,且没有任何方法能改变属性呢。

下面就一个通过案例解决上述问题

(1)、使用Cat类,去掉@Data注解和任何方法

public class Cat {private String name;private Integer age;private Date brith;private String character;// 以下随便取的字段private String a;private String b;private String c;private String d;
}

(2)、测试类:

    public void Test2() throws Exception {Cat cat = new Cat();common2(cat);System.out.println(cat);}private <T> void common2(T t) throws Exception{// 我这里直接塞的是对象了,对象也可以通过getClass方法,获取该对象的Class类Class<?> tClass = t.getClass(); // 通过泛型得到该类的Class对象Field[] fields = tClass.getDeclaredFields(); // 得到该类所有的属性(包括private)for (Field field : fields) {field.setAccessible(true); // 关闭验证,我们就可以操作私有属性(private)if (field.getType() == String.class) { // 如果获取到属性的类型是StringString name = field.getName(); // 获取该 Class 类的属性名Object o = field.get(t); // 获取该类的该方法的值if (Objects.isNull(o)){ // 如果值是空的话if (name.equals("name")) { // 如果获取到属性的名字是namefield.set(t,"大白"); // 设置name值为大白}else {field.set(t,""); // 设置属性值为 ""}}}}}

(3)、结果:debug看一下,的确可以实现改熟悉的值

在这里插入图片描述

三、反射原理

要搞懂Java反射的原理,我们要从写好一个java文件,到编译成class文件,在通过类加载器加载到JVM的堆,加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造,形成每个对象对应的全局唯一的一个Class方法

我就从Java程序计算机的三个阶段讲起:编译、类加载、运行

1、编译

一个我们编写的java文件,这个文件时面向与程序员的,可以通过javaSE很直观明了的代码的逻辑,但是计算机是看不懂的,计算机只能识别强弱电信号,就是以二进制形式存在的二进制文件,所以我们需要通过编译得到class字节码文件,再由jvm转换为计算机可识别的二进制数据

第一步,很简单就是通过javac命令,让java文件编译成class文件

图解

在这里插入图片描述

Java文件与Class文件对比

在这里插入图片描述

2、类加载

通过ClassLoader的defineClass 方法自动构造的,全局唯一的Class对象

把他的所有的成员变量、构造方法、成员方法封装为Class类中的Field数组、Constructor数组、Method数组,这个数组里面存放的就是类中的成员变量、构造方法、成员方法等信息

图解

在这里插入图片描述

DeBug看一下

在这里插入图片描述

查看Class对象是否是唯一的

在这里插入图片描述

3、运行

通过主方法运行 Cat cat = new Cat() 代码时,会先找对应的Class对象信息,把信息加载到堆中,生成一个cat对象

Class对象,可以想象成一个镜子,镜子里面有类的各种信息,反射就是指,通过这面镜子拿到自己想要的信息

在这里插入图片描述

四、使用反射

反射类集中在java.lang.reflect包下面

反射机制相关的重要的类

含义
java.lang.Class代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Method代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

一、获取class对象的三种方法

如果使用的类形同,那么获取的是同一个Class对象

class对象一般要配合Method、Constructor、Field对象来使用

方法
Class.forName(“全类名”)
对象.getClass()
类.class
Cat cat = new Cat();
Class<? extends Cat> class1 = cat.getClass();
Class<Cat> class2 = Cat.class;
Class<?> class3 = Class.forName("com.zhao.reflectdemo.pojo.Cat");
System.out.println(class1 == class2); //true
System.out.println(class1 == class3); //true
System.out.println(class2 == class3); //true

二、Class类

其实这个类取的名字是带有误解性的,因为他是的的确确存在与java.lang包下的一个类,与我上述讲解的class对象,不是一个东西,但又有千丝万缕的关系

Class 类没有公共构造方法。Class 对象是在加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

Class类常用的方法

特别强调一下newInstance()方法,可以反射实例化对象

方法名备注
public T newInstance()创建对象
public String getName()返回完整类名带包名
public String getSimpleName()返回类名
public Field[] getFields()返回类中public修饰的属性
public Field[] getDeclaredFields()返回类中所有的属性
public Field getDeclaredField(String name)根据属性名name获取指定的属性
public native int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
ublic Method[] getDeclaredMethods()返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors()返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass()返回调用类的父类
public Class<?>[] getInterfaces()返回调用类实现的接口集合

三、反射获取方法(Method)

Method类

方法名备注
public String getName()返回方法名
public int getModifiers()获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType()以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args)调用方法

案例:

1、有个Cat类:

public class Cat {public void call(String name){System.out.println("cat的有参方法,参数:" + name);}public String call(){System.out.println("cat的无参方法");return "无参方法";}private String privatization(){System.out.println("cat的私有方法");return "私有方法";}
}

2、测试案例:

    @Testpublic void Test2() throws Exception {Class<Cat> catClass = Cat.class; // 获取Cat的class对象Cat cat = catClass.newInstance(); // 得到一个Cat对象,与 new Cat() 类似System.out.println("============获取单个非私有方法==================");Method method = catClass.getMethod("call"); // 获取单个非私有方法Object o = method.invoke(cat);System.out.println("method的返回值是" + o);Method method2 = catClass.getMethod("call",String.class); // 获取单个非私有方法,带参method2.invoke(cat, "随便");
//        Method method3 = catClass.getMethod("privatization"); // 获取单个私有方法 //会报错
//        method3.invoke(cat);// 由于数据里面的方法,包括Object类太多了,就不打印出来了,可以自己解开试一试
//        System.out.println("============获取多个非私有方法==================");
//        Method[] methods = catClass.getMethods(); // 获取多个非私有方法
//        for (Method method1 : methods) {
//            System.out.println("================for循环=============================");
//            // 获取修饰符列表
//            System.out.print("获取修饰符列表:");
//            System.out.println(method1.getModifiers());
//            // 获取方法的返回值类型
//            System.out.print("获取方法的返回值类型:");
//            System.out.println(method1.getReturnType().getSimpleName());
//            // 获取方法名
//            System.out.print("获取方法名:");
//            System.out.println(method1.getName());
//        }Method[] declaredMethods = catClass.getDeclaredMethods(); // 获取全部方法,包括私有System.out.println("============获取单个方法==================");Method declaredMethod = catClass.getDeclaredMethod("privatization"); //获取单个私有方法declaredMethod.setAccessible(true); // 关闭检查,这样才可以调用私有方法declaredMethod.invoke(cat);}

3、结果

在这里插入图片描述

四、反射获取字段(Field)

Field类

方法名备注
public String getName()返回属性名
public int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType()以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)设置属性值
public Object get(Object obj)读取属性值

案例:

1、有个Cat类:

@Data
public class Cat {//相信以大家的聪明才智肯定能举一反三,使用我就直接全部私有化属性了private String name;private Integer age;private Date brith;private String character;// 以下随便取的字段private String a;private String b;private String c;private String d;
}

2、测试案例:

借用一下案例二的例子

目前我们想用案例1中的Cat类,在初始化时,他的所有属性皆不是基本类型,所有默认初始值为null

我们想获取该存在的类并改变它的名字(name属性)为:大白,其他字符从null,改为""

    @Testpublic void Test3() throws Exception{//相信以大家的聪明才智肯定能举一反三,使用我就直接全部私有化属性了Cat cat = new Cat();cat.setA("随便,随便拉");Class<?> tClass = cat.getClass(); // 通过泛型得到该类的Class对象Field[] fields = tClass.getDeclaredFields(); // 得到该类所有的属性(包括private)for (Field field : fields) {field.setAccessible(true); // 关闭验证,我们就可以操作私有属性(private)if (field.getType() == String.class) { // 如果获取到属性的类型是StringString name = field.getName(); // 获取该 Class 类的属性名Object o = field.get(cat); // 获取该类的该方法的值if (Objects.isNull(o)){ // 如果值是空的话if (name.equals("name")) { // 如果获取到属性的名字是namefield.set(cat,"大白"); // 设置name值为大白}else {field.set(cat,""); // 设置属性值为 ""}}}}System.out.println(cat);}

3、Debug

在这里插入图片描述

五、反射获取构造函数(Constructor)

Constructor类

方法名备注
public String getName()返回构造方法名
public int getModifiers()获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes()返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs)创建对象【参数为创建对象的数据】

案例:

1、有个Cat类:

public class Cat {public String name;private Cat(){}public Cat(String name){ this.name = name;}
}

2、测试案例:

    @Testpublic void Test4() throws Exception{Class<Cat> catClass = Cat.class;Constructor<Cat> constructor = catClass.getConstructor(String.class); // 获取非私有的构造函数Cat cat1 = constructor.newInstance("名字是个啥");System.out.println(cat1.name);Constructor<Cat> declaredConstructor = catClass.getDeclaredConstructor(); // 获取构造函数declaredConstructor.setAccessible(true); // 私有的要关闭检验Cat cat2 = declaredConstructor.newInstance(); // 通过私有的构造函数,得到Cat对象System.out.println(cat2.name);}

3、结果

在这里插入图片描述


http://chatgpt.dhexx.cn/article/NfYqn6DF.shtml

相关文章

java反射机制原理详解

Java反射机制是指在运行时动态地获取一个类的信息并能够操作该类的属性和方法的能力。Java反射机制使得程序能够在运行时借助Class类的API来操作自身的属性和方法&#xff0c;从而大大增强了Java的灵活性和可扩展性。本文将详细介绍Java反射机制的原理以及如何使用它。 1、反射…

Java 反射及原理

反射&#xff0c;指的是对于任意一个类&#xff0c;都可以动态的获得它的所有属性和方法&#xff0c;对于任意一个对象都能调用的它的所有属性和方法&#xff0c;都能够调用它的任意方法和属性&#xff1b;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。…

回车、换行、回车换行、硬回车以及软回车

回车、换行、回车换行、硬回车以及软回车 要想一句话说清楚它们之间的关系&#xff0c;不太简单。但认真看完后&#xff0c;会发现它们之间的关系其实也挺简单的。 回车、换行与回车换行 英文缩写对应按键英文全称中文名称解释转义表达式使用平台CRreturnCarriage Return回车…

图解回车和换行的区别

文章目录 1. 定义2. 图解3. 讨论4. 结论 1. 定义 中文英文简写HEXCharacterASCII回车Carriage returnCR0x0D\r13换行Line feedLF0x0A\n10 2. 图解 Win11 Experiment by Pycharm with Python 3.9 print(---)print(Hello World)print(---)# \rprint(Hello \r World)print(---…

不同系统下回车和换行的区别

在计算机还没有出现之前&#xff0c;有一种叫做电传打字机&#xff08;Teletype Model 33&#xff09;的玩意&#xff0c;每秒钟可以打10个字符。但是它有一个问题&#xff0c;就是打完一行换行的时候&#xff0c;要用去0.2秒&#xff0c;正好可以打两个字符。要是在这0.2秒里面…

python回车和换行的区别_回车与换行的区别(转)

add by zhj: 不同操作系统下换行符不同&#xff0c;如下&#xff1a; \n: UNIX \n\r: window \r: MAC OS 我们经常遇到的一个问题就是&#xff0c;Unix/Mac系统下的文件在Windows里打开的话&#xff0c;所有文字会变成一行&#xff1b;而Windows里的文件在Unix/Mac下打开的…

回车与换行的区别,CRLF、CR、LF详解(\r \n \r\n的区别)

先上结论 缩写ASCⅡ转义系统ASCⅡ值CR\rMacIntosh&#xff08;早期的Mac&#xff09;13LF\nUnix/Linux/Mac OS X10CR LF\r\nWindows 很长一段时间里&#xff0c;对于CRLF、CR、LF的理解仅限于不同操作系统下对换行符的定义。所谓知其然需知其所以然&#xff0c;从学习中找到乐…

回车符,换行符的区别

首先介绍一下“回车”&#xff08;carriage return,’\r’&#xff09;和“换行”&#xff08;line feed,’\n’&#xff09;这两个概念的来历和区别。在计算机还没有出现之前&#xff0c;有一种叫做电传打字机&#xff08;Teletype Model 33&#xff09;的玩意&#xff0c;每秒…

换行和回车的区别

我们在看他们的区别时我们先看看他们的分别指的是什么&#xff1a; 回车<\r>(carriage return)&#xff1a;告诉打印机把打印头定位到左边界&#xff0c;就是指的&#xff0c;那个打印头重新放在这一行的开始。 换行<\n>(line feed)&#xff1a;告诉打印机把打印头…

回车符和 换行符的区别

首先介绍一下“回车”&#xff08;carriage return,’\r’&#xff09;和“换行”&#xff08;line feed,’\n’&#xff09;这两个概念的来历和区别。在计算机还没有出现之前&#xff0c;有一种叫做电传打字机&#xff08;Teletype Model 33&#xff09;的玩意&#xff0c;每秒…

回车符、换行符和回车换行符

我的CSDN主页My Python 学习个人备忘录我的HOT博 自学并不是什么神秘的东西&#xff0c;一个人一辈子自学的时间总是比在学校学习的时间长&#xff0c;没有老师的时候总是比有老师的时候多。             ——华罗庚 \r 、\n 和 \r\n 题目代码运行效果我的解题思路…

回车符与换行符的区别

引用了博客&#xff1a;https://blog.csdn.net/fanwenbo/article/details/54848429 这里是我自己做的实验&#xff0c;如下&#xff1a; printf("A is here!");printf("I am here!");putchar(\r);printf("First input!");putchar(\n);printf(&qu…

“回车”、“换行”浅谈

“回车”、“换行”浅谈 转义字符\r表示回车&#xff08;carriage return&#xff09;即回到行首&#xff0c;并没有包含换行的动作&#xff0c; \n表示换行&#xff08;line feed&#xff09;即移动到新的一行&#xff08;下一行&#xff09;。顺便提及&#xff0c;\b表示退格…

回车与换行符的区别及python中使用

一、区别 转载自http://www.pythontab.com/html/2017/linuxkaiyuan_0115/1116.html 1. 由来 在计算机还没有出现之前&#xff0c;有一种叫做电传打字机&#xff08;Teletype Model 33&#xff09;的机械打字机&#xff0c;每秒钟可以打10个字符。但是它有一个问题&#xff0c…

回车和换行的区别

回车和换行的区别 回车和换行的概念不同的系统间传递文件会涉及格式的转换Unix -> WindowsUnix <- Windows 回车和换行的概念 首先介绍一下“回车”&#xff08;carriage return,’\r’&#xff09;和“换行”&#xff08;line feed,’\n’&#xff09;这两个概念的来历…

在数组中查找指定元素 (15分)

int search( int list[], int n, int x ){int index -1;for(int i0;i<n;i){if(list[i] x){index i;break;}}return index; }

[PTA]习题8-2 在数组中查找指定元素

Spring-_-Bear 的 CSDN 博客导航 本题要求实现一个在数组中查找指定元素的简单函数。 函数接口定义&#xff1a; int search( int list[], int n, int x );其中 list[] 是用户传入的数组&#xff1b;n&#xff08;≥0&#xff09;是 list[] 中元素的个数&#xff1b;x 是待查…

[PTA]实验8-1-5 在数组中查找指定元素

Spring-_-Bear 的 CSDN 博客导航 本题要求实现一个在数组中查找指定元素的简单函数。 函数接口定义&#xff1a; int search( int list[], int n, int x );其中 list[] 是用户传入的数组&#xff1b;n&#xff08;≥ 0&#xff09;是 list[] 中元素的个数&#xff1b;x 是待…

习题8-2 在数组中查找指定元素

习题8-2 在数组中查找指定元素 (15 分) 本题要求实现一个在数组中查找指定元素的简单函数。 函数接口定义&#xff1a; int search( int list[], int n, int x );其中list[]是用户传入的数组&#xff1b;n&#xff08;≥0&#xff09;是list[]中元素的个数&#xff1b;x是待…

C语言刷题系列——9.在数组中查找指定元素

&#x1f6a9;实现一个在数组中查找指定元素的简单函数 &#x1f506;一) 题目要求&#x1f506;二) 题解 &#x1f506;一) 题目要求 函数接口定义&#xff1a;int search( int list[], int n, int x );其中list[]是用户传入的数组&#xff1b;n&#xff08;≥0&#xff09;是…