1️⃣ 反射的含义
- 反射是将Java类中的各种成分映射成相应的Java对象。
- Class对象的产生是基于将class文件读入内存,并为之创建一个对应的Class对象。需要注意的是,一个类在运行期间只会产生一个Class对象。
- 反射是许多框架设计的核心。使用反射的前提是:必须先获取代表的字节码的Class对象。Class类代表着.class文件(也就是字节码)。
2️⃣ 获取Class对象的三种方式
- 通过对象获取:
Object ——> getClass();
- 通过数据类型的“静态”class属性:
任何数据类型(包括基本数据类型)都有一个“静态”的class属性
- 通过Class类的静态方法获取:
Class.forName(String className)
。这种方式是常用的。
🔹 完整示例
package com.study.notes.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* @program: study-notes
* @description: 作家
* @author: lzq
* @create: 2023-06-27 15:38
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
//id
private Long id;
//姓名
public String name;
//年龄
private Integer age;
//简介
private String intro;
//薪水
private BigDecimal salary;
//作品
private List<Book> books;
/**************构造方法***************/
public Author(String name) {
this.name = name;
}
/**************成员方法***************/
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return String.valueOf(age);
}
}
package com.study.notes.reflect;
import com.study.notes.bean.Author;
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
* 注意:在运行期间,一个类,只有一个Class对象产生。
* 三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
*
*/
public class ReflectClass {
public static void main(String[] args) {
System.out.println("**********************第一种方式获取Class对象*********************************");
Author author1 = new Author();//这一new 产生一个Author对象,一个Class对象。
Class authorClass = author1.getClass();//获取Class对象
System.out.println(authorClass.getName());
System.out.println("**********************第二种方式获取Class对象*********************************");
Class authorClass2 = Author.class;
System.out.println(authorClass == authorClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
System.out.println("**********************第三种方式获取Class对象*********************************");
try {
Class authorClass3 = Class.forName("com.study.notes.bean.Author");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(authorClass3 == authorClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
📌注意事项
- 在运行期间,一个类只会产生一个Class对象。
- 虽然有三种方式获取Class对象,但通常最常用的是第三种。原因是:
- 使用第一种方式时,对象已经存在,那么进行反射似乎显得多余。
- 使用第二种方式时,需要导入类的包,这增加了依赖性。如果不导入相应的包,则会抛出编译错误。
- 第三种方式是最灵活的。通过传入一个字符串来获取Class对象,这个字符串可以是硬编码的,也可以从配置文件、数据库等多种来源获得。
3️⃣ 通过Class对象访问类的成分
通过Class对象,我们可以访问某个类的构造方法、成员变量、以及成员方法。这为我们提供了动态创建对象、访问和修改成员等功能。
🔹 批量获取构造方法
- 获取所有公有构造方法:
public Constructor[] getConstructors()
- 获取所有构造方法(无论是公有、私有、受保护、默认):
public Constructor[] getDeclaredConstructors()
🔹 获取单个构造方法并调用
-
获取单个公有构造方法:
public Constructor getConstructor(Class... parameterTypes)
-
获取某个构造方法(可以是私有的、受保护的、默认的或公有的):
public Constructor getDeclaredConstructor(Class... parameterTypes)
-
调用构造方法创建新实例:
Constructor.newInstance(Object... initargs)
🔹 完整示例
package com.study.notes.reflect;
import java.lang.reflect.Constructor;
/**
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
* public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
*
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class ReflectConstructor {
public static void main(String[] args) throws Exception {
System.out.println("************获取Class对象********************");
Class clazz = Class.forName("com.study.notes.bean.Author");
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
System.out.println("obj = " + obj);
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(String.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance("男");
System.out.println("obj = " + obj);
}
}
4️⃣ 获取成员方法并执行
通过Class对象,你不仅可以访问类的构造方法,还可以访问它的成员方法,并在运行时动态地执行这些方法。这是Java反射机制的另一个强大功能。
🔹 批量获取
-
获取所有公有方法(这包括它的所有父类的公有方法,也包括Object类的方法):
public Method[] getMethods()
-
获取该类所有的方法(不论是私有、受保护、默认、公有,但不包括继承的方法):
public Method[] getDeclaredMethods()
🔹 获取单个方法
-
获取某个公有的方法:
public Method getMethod(String name, Class<?>... parameterTypes)
参数:
name
: 要获取的方法名Class<?>... parameterTypes
: 方法的参数类型,按声明顺序
-
获取某个方法(可以是私有的、受保护的、默认的或公有的):
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
🔹 执行方法
- 调用某个对象的方法:
参数:public Object invoke(Object obj, Object... args)
obj
: 要执行方法的对象实例args
: 调用方法时的参数
这为我们提供了在运行时动态地获取和执行对象的任何方法的能力,无论这些方法是公有的还是私有的。
🔹 完整示例
package com.study.notes.reflect;
import java.lang.reflect.Method;
/**
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 3.调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
*/
public class ReflectMethod {
public static void main(String[] args) throws Exception {
System.out.println("************获取Class对象********************");
Class clazz = Class.forName("com.study.notes.bean.Author");
System.out.println("***************获取所有的”公有“方法*******************");
clazz.getMethods();
Method[] methodArray = clazz.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = clazz.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = clazz.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Author对象
Object obj = clazz.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = clazz.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
}
5️⃣ 获取成员变量并操作
在Java中,通过反射机制,你不仅可以访问类的构造方法和成员方法,还可以访问类的成员变量,并在运行时动态地修改这些变量的值。这为动态操作对象提供了很大的灵活性。
🔹 批量获取
-
获取所有公有变量:
Field[] getFields()
这个方法返回一个包含所有公有成员变量的数组,但不包括继承的变量。
-
获取该类的所有变量:
Field[] getDeclaredFields()
这个方法返回一个Field数组,包含该类的所有成员变量(不论是私有、受保护、默认、公有)
🔹 获取单个变量
-
获取某个公有变量:
public Field getField(String fieldName)
参数
fieldName
是你想要获取的成员变量的名字。 -
获取某个变量(不论是私有、受保护、默认、公有):
public Field getDeclaredField(String fieldName)
🔹 操作成员变量
- 设置成员变量的值:
参数:public void set(Object obj, Object value)
obj
: 要修改的字段所在的对象value
: 要为字段设置的新值
此功能使我们能够在运行时动态地访问和修改对象的任何成员变量,不论它是公有的还是私有的。
🔹 完整示例
package com.study.notes.reflect;
import com.study.notes.bean.Author;
import java.lang.reflect.Field;
/**
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*
*/
public class ReflectField {
public static void main(String[] args) throws Exception {
System.out.println("************获取Class对象********************");
Class clazz = Class.forName("com.study.notes.bean.Author");
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = clazz.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = clazz.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("*************获取公有字段并调用***********************************");
Field f = clazz.getField("name");
System.out.println(f);
//获取一个对象
Object obj = clazz.getConstructor().newInstance();//产生Author对象--》Author author = new Author();
//为字段设置值
f.set(obj, "刘德华");//为Author对象中的name属性赋值--》author.name = "刘德华"
//验证
Author author = (Author)obj;
System.out.println("验证姓名:" + author.name);
System.out.println("**************获取私有字段并调用********************************");
f = clazz.getDeclaredField("intro");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "你好");
System.out.println("简介:" + author);
}
}