Java之反射
什么是反射
- Java中的反射(Reflection)是指在程序运行时动态地获取类的信息(如类名、方法、属性等),并能够操作对象的方法和属性,而不需要预先知道这些信息。反射使得程序具有更高的灵活性和可扩展性,但也会带来一定的性能开销。
反射的种类
| 类名 | 说明 |
|---|---|
| Field类 | 类的成员变量 |
| Method类 | 类的方法 |
| Constructor | 类的构造方法 |
反射获取Class对象的三种方式
- Class.forName("全类名");
- 类名.class
- 对象.getClass();
package Demo;
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}package Demo;
import org.junit.Test;
public class ReflectionTest {
//第一种方式:Class.forName
// 全类名: 包名+类名 (全类名) 通常用第一种方式
@Test
public void show() throws ClassNotFoundException {
//Class.forName("全类名").var
Class<?> aClass = Class.forName("Demo.Person");
System.out.println(aClass); //class Demo.Person
}
//第二种方式:类名.class
// 通常在多线程中使用 用来当对象锁 synchronized
@Test
public void show1() {
//Person.class.var
Class<Person> personClass = Person.class;
System.out.println(personClass); //class Demo.Person
}
//第三种方式: 对象.getClass
//有了这个类当对象的时候才可以使用
@Test
public void show2() {
//先实例化对象 通过对象去调用getClass
Person p = new Person();
//p.getClass().var
Class<? extends Person> aClass = p.getClass();
System.out.println(aClass); //class Demo.Person
}
}反射获取构造方法
| 属性 | 说明 |
|---|---|
| newInstance | 创建对象 |
| getConstructors | 获取公共的构造方法 |
| getDeclaredConstructor | 获取空参的构造方法 |
| getDeclaredConstructors | 获取所有的构造方法 |
public class Person {
String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
//受保护的构造方法
protected Person(int age) {
this.age = age;
}
//私有构造方法 通过暴力反射可以进行访问
private Person(String name, int age) {
this.name = name;
this.age = age;
}
}public class ReflectionTest02 {
public static void main(String[] args) throws Exception {
//1.获取Class字节码文件对象
Class aClass = Class.forName("Demo.Person");
//2.获取公共的构造方法
Constructor[] constructors = aClass.getConstructors();
//3.获取所有的构造方法
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
//4.获取空参的构造方法
Constructor dc = aClass.getDeclaredConstructor();
//5.获取Sring构造方法
Constructor dc1 = aClass.getDeclaredConstructor(String.class);
//6.获取int构造方法
Constructor dc2 = aClass.getDeclaredConstructor(int.class);
//7.获取String和int构造方法
Constructor dc3 = aClass.getDeclaredConstructor(String.class, int.class);
//8.通过7来创建对象
//setAccessible:暴力反射 取消权限检验
dc3.setAccessible(true);
//创建对象
Person person = (Person) dc3.newInstance("阿乐的小屋", 22);
System.out.println(person);
}
}反射获取成员变量
| 属性 | 说明 |
|---|---|
| getFields | 获取公开的成员变量对象 |
| getDeclaredFields | 获取所有成员变量对象 |
| getDeclaredField | 获取单个成员变量 |
| getModifiers | 获取权限修饰符 |
| getName | 获取成员变量的名字 |
| getType | 获取成员变量的数据类型 |
| setAccessible | 取消修饰的权限检测 |
public class Person {
private String name;
private int age;
public int id;
public Person() {
}
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}public class ReflectionTest02 {
public static void main(String[] args) throws Exception {
//1.获取Class字节码文件的对象
Class aClass = Class.forName("Demo.Person");
//2.获取公开的成员变量的对象
Field[] fields = aClass.getFields(); //public int Demo.Person.id 数组
//3.获取所有的成员变量对象
Field[] fields1 = aClass.getDeclaredFields();
//4.获取单个成员变量
Field name = aClass.getDeclaredField("name");
//5.获取权限修饰符
int modifiers = name.getModifiers(); //2
//6.获取成员变量的名字
String name1 = name.getName(); //name
//7.获取成员变量的数据类型
Class<?> type = name.getType(); //class java.lang.String
//8.获取成员变量记录的值
Person p = new Person("blogdx.com",22,1);
name.setAccessible(true);
String value = (String) name.get(p); //blogdx.com
//修改对象里面记录的值
name.set(p,"阿乐的小屋"); //Person{name='阿乐的小屋', age=22, id=1}
//获取成员变量int,将int修改为20
//获取age的成员变量
Field age = aClass.getDeclaredField("age");
//暴力反射取消权限检测
age.setAccessible(true);
//读取age的值
int i1 = (int) age.get(p); //22
//修改age的值
age.set(p,20);
System.out.println(p); //Person{name='阿乐的小屋', age=20, id=1}
}
}反射获取成员方法
| 属性 | 说明 |
|---|---|
| getMethods | 获取所有方法对象包含父类方法 |
| getDeclaredMethods | 获取本类所有方法对象包含私有方法 |
| getDeclaredMethod | 获取指定单一方法 |
| getModifiers | 获取方法的修饰符 |
| getName | 获取方法的名字 |
| getParameters | 获取方法的形参 |
| getExceptionTypes | 获取方法抛出的异常 |
| invoke("实例","参数") | 指定实例运行方法 |
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void sleep(){
System.out.println("睡觉");
}
//私有方法
private void eat (String name) throws RuntimeException,NullPointerException{
System.out.println(name + "在吃东西");
}
//公开方法有返回值
public String fly(String name){
System.out.println(name + "在飞翔");
return "在飞翔";
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}public class ReflectionTest02 {
public static void main(String[] args) throws Exception {
//获取class字节码文件对象
Class aClass = Class.forName("Demo.Person");
//1.获取所有的方法对象(包含父类的公共方法)
Method[] methods = aClass.getMethods();
//2.获取本类里面的所有方法和对象(包含私有的方法)
Method[] methods1 = aClass.getDeclaredMethods();
//3.获取指定的单一方法
Method m = aClass.getDeclaredMethod("eat", String.class); //private void Demo.Person.eat(java.lang.String)
//4.获取方法的修饰符
int modifiers = m.getModifiers(); //2
//5.获取方法的名字
String name = m.getName();
System.out.println(name);
//6.获取方法的形参
Parameter[] parameters = m.getParameters(); //java.lang.String arg0
//7.获取方法抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();//class java.lang.RuntimeException class java.lang.NullPointerException
//8.运行方法
Person p = new Person();
m.setAccessible(true);
//p是方法的调用者
//"张三" 在调用方法的时候传入的参数
m.invoke(p,"张三");
//9.获取方法的返回值
Method f = aClass.getDeclaredMethod("fly", String.class);
Person pp = new Person();
//返回值
String bird = (String) f.invoke(pp, "鸟儿");
System.out.println(bird); //在飞翔
}
}反射的好处
- 作用:获取任意一个类中的所有信息 结合配置文件动态创建对象
- 动态性:反射允许程序在运行时动态地获取类的信息,从而可以根据需要创建对象、调用方法、访问属性等。
- 灵活性:通过反射可以访问类的私有成员,以及无法直接访问的成员,从而增强了程序的灵活性。
- 扩展性:反射机制使得程序可以利用外部的类或者对象,而不需要在编译时就将其引入到程序中。
- 逆向工程:反射允许程序在运行时检查类的结构,进而进行逆向工程,例如生成Java代码、序列化和反序列化等。
- 框架开发:许多框架都使用了反射,例如Spring框架就利用反射来实现依赖注入、AOP等功能。这些框架因此具有高度的灵活性和可扩展性。
作业练习
- 创建一个
Person类,用反射创建对象并且将内容保存到本地文本中
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}public class PersonTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//1.获取字节码文件
Class aClass = Class.forName("Demo.Person");
//2.创建缓冲流
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//3.获取成员变量
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
//4.获取构造方法
Constructor con = aClass.getDeclaredConstructor(String.class, int.class);
Person p = (Person) con.newInstance("阿乐的小屋",22);
field.setAccessible(true);
bw.write(field.getName() + "=" + field.get(p));
bw.newLine();
}
bw.close();
}
}- 通过
Class.forName()方法获取类Demo.Person的字节码文件。 - 创建缓冲流
BufferedWriter并指定输出文件 a.txt。 - 通过
aClass.getDeclaredFields()方法获取类Demo.Person中所有声明的成员变量。 - 遍历
fields数组,对于每个成员变量,通过 getDeclaredConstructor() 方法获取构造方法,然后利用newInstance()方法创建一个新的Person对象。 - 调用
field.setAccessible(true)方法,使得可以访问非公有成员变量。然后利用field.getName()和field.get()方法分别获取成员变量的名称和值,并将其写入文件 bw 中。 - 关闭缓冲流 bw。
- 创建一个
properties配置文件 动态的调用配置文件中的方法输出到控制台 配置文件内容如下
classname=Demo.Person
method=showpublic class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//方法一
public void show(){
System.out.println("阿乐在学习");
}
//方法二
public void run(){
System.out.println("阿乐的小屋");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}public class PersonTest02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
//1.创建一个io流读取配置文件
FileInputStream fis = new FileInputStream("prop.properties");
Properties prop = new Properties();
//将配置文件加载到prop中
prop.load(fis);
//关闭流
fis.close();
//prop.get获取键
String classname = (String) prop.get("classname");
String method = (String) prop.get("method");
//3.获取class字节码对象
Class<?> aClass = Class.forName(classname);
//4.获取方法
Method show = aClass.getDeclaredMethod(method);
//4.实例化Person类
Person p = new Person();
//运行方法
show.invoke(p);
}
}- 通过
FileInputStream类创建一个输入流对象fis,用于读取配置文件"prop.properties"。 - 创建
Properties对象prop,将配置文件加载到prop中。这样就可以使用prop.get方法获取键对应的值(类名和方法名)。 - 调用
Class.forName方法获取指定类名classname的 Class 对象aClass。 - 使用
aClass.getDeclaredMethod方法获取指定方法名method的 Method 对象show。 - 实例化
Person类并用show.invoke(p)方法调用指定的方法。