当前位置: 首页 > news >正文

【Java】反射与动态代理篇

目录

  • 反射的基本概念
  • 获取 class 对象的三种方式
    • Class.forName("全类名");
    • 类名.class
    • 对象.getClass();
  • 获取类内元素
    • 获取构造方法
    • 获取成员变量
    • 获取成员方法
  • 反射的作用
    • 案例 1:保存信息
    • 案例 2:跟配置文件结合动态创建
  • 动态代理
    • 概述
    • 创建代理对象
    • 案例

反射的基本概念

反射允许对成员变量、成员方法和构造方法的信息进行编程访问

在这里插入图片描述

如图所示,反射可以获取类中的成员变量、成员方法和构造方法并解剖其中的所有信息

获取 class 对象的三种方式

获取对象有三种方式:

  • Class.forName(“全类名”);
  • 类名.class
  • 对象.getClass();

使用的时候分别对应图中的三种阶段
在这里插入图片描述

Class.forName(“全类名”);

全类名 = 包名 + 类名

代码示例:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}

	public static void main(String[] args) throws ClassNotFoundException {
		Class cs = Class.forName("MyReflect.Student");
		System.out.println(cs);
	}
}

结果如下:

class MyReflect.Student

类名.class

代码示例:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}

	public static void main(String[] args) throws ClassNotFoundException {
		Class cs1 = Student.class;
		System.out.println(cs1);
	}
}

结果如下:

class MyReflect.Student

对象.getClass();

代码示例:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}

	public static void main(String[] args) throws ClassNotFoundException {
		Student s = new Student();
		Class cs = s.getClass();
		System.out.println(cs);
	}
}

结果如下:

class MyReflect.Student

由结果可以看出,这三个方式获取的字节码文件对象是相同的

三个方法的使用场景:

  • 第一种是最常用的
  • 第二种一般当做参数进行传递,比如多线程中的锁对象
  • 第三种只有当有了类对象后才可以使用

获取类内元素

在 Java 中可以万物皆对象:

  • 构造方法 → Constructor 对象
  • 字段(成员变量)→ Field 对象
  • 成员方法 → Method 对象

获取构造方法

Class 类中用于获取构造方法的方法;

  • Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
  • Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
  • <T> Constructor<T> getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法对象
  • <T> Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造方法对象

代码示例:

Student 类:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(String name) {
		this.name = name;
	}
	protected Student(int age) {
		this.age = age;
	}
	private Student(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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}
	
}

reflectDemo 类:

package MyReflect;

import java.lang.reflect.Constructor;
public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		// Constructor<?>[] getConstructors()
		Constructor[] cons1 = cs.getConstructors();
		for(Constructor con : cons1) {
			System.out.println(con);
		}
		System.out.println("--------------");
		// Constructor<?>[] getDeclaredConstructors()
		Constructor[] cons2 = cs.getDeclaredConstructors();
		for(Constructor con : cons2) {
			System.out.println(con);
		}
		System.out.println("--------------");
		// <T> Constructor<T> getConstructor(Class<?>... parameterTypes)
		Constructor con3 = cs.getConstructor(String.class);
		System.out.println(con3);
		System.out.println("--------------");
		// <T> Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
		Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
		System.out.println(con4);
	}
}

结果如下:

public MyReflect.Student()
public MyReflect.Student(java.lang.String)
--------------
public MyReflect.Student()
private MyReflect.Student(java.lang.String,int)
protected MyReflect.Student(int)
public MyReflect.Student(java.lang.String)
--------------
public MyReflect.Student(java.lang.String)
--------------
private MyReflect.Student(java.lang.String,int)

获取构造方法中的信息

  • getModifiers() 获取权限修饰符
  • getParameters() 获取所有参数

代码示例:

package MyReflect;

import java.lang.reflect.Constructor;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
        Class cs = Class.forName("MyReflect.Student");
		Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
		int modifiers = con4.getModifiers();
		System.out.println(modifiers);
	}
}

结果如下:

2

输出结果 2 为常量字段值,其他修饰符的常量字段值如下图所示:
在这里插入图片描述

Constructor 类中用于创建对象的方法:

  • T newInstance(Object...initargs) 根据指定的构造方法创建对象
  • setAccessible(boolean flag) 设置为 true,表示取消访问检查

代码示例:

package MyReflect;

import java.lang.reflect.Constructor;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
		con4.setAccessible(true);
		Student s = (Student) con4.newInstance("张三",18);
		System.out.println(s);
	}
}

结果如下:

Student{name = 张三, age = 18}

创建 s 对象用的是 private Student(String name,int age) ,由于是用 private 修饰的,所以创建对象的时候会被禁止访问,这时候就需要在创建对象前使用 con4.setAccessible(true); 将访问权限打开

获取成员变量

获取成员变量的方法:

  • Field[] getFields() 返回所有公共成员变量对象的数组
  • Field[] getDeclaredFields() 返回所有成员变量对象的数组
  • Field getField(String name) 返回单个公共成员变量对象
  • Field getDeclaredField(String name) 返回单个成员变量对象

代码示例:

Student 类:

package MyReflect;

public class Student {
	private String name;
	private int age;
	public String gender;
	
	public Student() {}
	public Student(String name,int age,String gender) {
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	
	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 String getGender() {return gender;}
	public void setGender(String name) {this.gender = gender;}
	
	public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";}
	
}

reflectDemo 类:

package MyReflect;

import java.lang.reflect.Field;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		// Field[] getFields()
		Field[] fields1 = cs.getFields();
		for(Field field : fields1) {
			System.out.println(field);
		}
		System.out.println("--------------");
		// Field[] getDeclaredFields()
		Field[] fields2 = cs.getDeclaredFields();
		for(Field field : fields2) {
			System.out.println(field);
		}
		System.out.println("--------------");
		// Field getField(String name)
		Field fields3 = cs.getField("gender");
		System.out.println(fields3);
		System.out.println("--------------");
		// Field getDeclaredField(String name)
		Field fields4 = cs.getDeclaredField("name");
		System.out.println(fields4);
	}
}

结果如下:

public java.lang.String MyReflect.Student.gender
--------------
private java.lang.String MyReflect.Student.name
private int MyReflect.Student.age
public java.lang.String MyReflect.Student.gender
--------------
public java.lang.String MyReflect.Student.gender
--------------
private java.lang.String MyReflect.Student.name

获取成员变量中的信息

  • getModifiers() 获取权限修饰符
  • getName() 获取成员变量的名字
  • getType() 获取成员变量的类型
package MyReflect;

import java.lang.reflect.Field;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		Field name = cs.getDeclaredField("name");
		System.out.println(name);
		
		int modifiers = name.getModifiers();
		System.out.println(modifiers);
		
		String n = name.getName();
		System.out.println(n);
		
		Class<?> type = name.getType();
		System.out.println(type);
	}
}

结果如下:

private java.lang.String MyReflect.Student.name
2
name
class java.lang.String

Field 类中用于创建对象的方法

  • void set(Object obj, Object value) 为指定对象的成员变量赋值
  • Object get(Object obj) 获取指定对象中成员变量的值

代码示例:

package MyReflect;

import java.lang.reflect.Field;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("反射.Student");
		Field name = cs.getDeclaredField("name");
		System.out.println(name);
		Student s = new Student("张三",18,"男");
		name.setAccessible(true);
		name.set(s, "李四");
		String value = (String)name.get(s);
		System.out.println(value);
	}
}

结果如下:

private java.lang.String MyReflect.Student.name
李四

获取成员方法

获取成员方法的方法

  • Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
  • Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的
  • Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共成员方法对象
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象

代码示例:

Student 类:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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 something) {
		System.out.println("在吃" + something);
	}
	
	public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
	
}

reflectDemo 类:

package MyReflect;

import java.lang.reflect.Method;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		// Method[] getMethods()
		Method[] methods1 = cs.getMethods();
		for(Method method : methods1) {
			System.out.println(method);
		}
		System.out.println("---------------------");
		// Method[] getDeclaredMethods()
		Method[] methods2 = cs.getDeclaredMethods();
		for(Method method : methods2) {
			System.out.println(method);
		}
		System.out.println("---------------------");
		// Method getMethod(String name, Class<?>... parameterTypes)
		Method method3 = cs.getMethod("sleep");
		System.out.println(method3);
		System.out.println("---------------------");
		// Method getDeclaredMethod(String name, Class<?>... parameterTypes)
		Method method4 = cs.getDeclaredMethod("eat",String.class);
		System.out.println(method4);
		
	}
}

结果如下:

public java.lang.String MyReflect.Student.getName()
public java.lang.String MyReflect.Student.toString()
public void MyReflect.Student.sleep()
public void MyReflect.Student.setName(java.lang.String)
public int MyReflect.Student.getAge()
public void MyReflect.Student.setAge(int)
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
---------------------
public java.lang.String MyReflect.Student.getName()
public java.lang.String MyReflect.Student.toString()
public void MyReflect.Student.sleep()
public void MyReflect.Student.setName(java.lang.String)
public int MyReflect.Student.getAge()
public void MyReflect.Student.setAge(int)
private void MyReflect.Student.eat(java.lang.String)
---------------------
public void MyReflect.Student.sleep()
---------------------
private void MyReflect.Student.eat(java.lang.String)

获取成员变量中的信息

  • getModifiers() 获取权限修饰符
  • getName() 获取成员方法的名字
  • getParameters() 获取成员方法的形参

在 Student 类中的 eat 方法 throws Throwable

代码示例:

package MyReflect;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class reflectDemo {
	public static void main(String[] args) throws Throwable {
		Class cs = Class.forName("MyReflect.Student");
		Method method = cs.getDeclaredMethod("eat",String.class);
		System.out.println(method);
		
		Parameter[] parameters = method.getParameters();
		for(Parameter parameter : parameters) {
			System.out.println(parameter);
		}
		
		Class[] exceptionTypes = method.getExceptionTypes();
		for(Class exceptionType : exceptionTypes) {
			System.out.println(exceptionType);
		}
	}
}

结果如下:

private void MyReflect.Student.eat(java.lang.String) throws java.io.IOException
java.lang.String arg0
class java.io.IOException

Method 类中用于运行方法的方法

  • Object invoke(Object obj, Object... args) 运行方法
    • 参数一:用 obj 对象调用该方法
    • 参数二:调用方法时传递的参数(若无参数则不写)
    • 返回值:方法的返回值(若无返回值则不写)

代码示例:

package MyReflect;

import java.lang.reflect.Method;

public class reflectDemo {
	public static void main(String[] args) throws Exception {
		Class cs = Class.forName("MyReflect.Student");
		Method method = cs.getDeclaredMethod("eat",String.class);
		System.out.println(method);
		
		method.setAccessible(true);
		Student s = new Student();
		method.invoke(s, "汉堡包");
	}
}

结果如下:

private void MyReflect.Student.eat(java.lang.String) throws java.io.IOException
在吃汉堡包

假设 eat 方法改为:

private String eat(String something) throws IOException{
	System.out.println("在吃" + something);
	return "真好吃!";
}

reflectDemo 类:

package MyReflect;

import java.lang.reflect.Method;

public class reflectDemo {
	public static void main(String[] args) throws Exception {
		Class cs = Class.forName("MyReflect.Student");
		Method method = cs.getDeclaredMethod("eat",String.class);
		System.out.println(method);
		
		method.setAccessible(true);
		Student s = new Student();
		Object result = method.invoke(s, "汉堡包");
		System.out.println(result);
	}
}

结果如下:

private java.lang.String MyReflect.Student.eat(java.lang.String) throws java.io.IOException
在吃汉堡包
真好吃!

反射的作用

  • 获取一个类里面所有的信息,获取到之后,再执行其他的业务逻辑
  • 结合配置文件,动态的创建对象并调用方法

案例 1:保存信息

对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中

Student 类:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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;}
}

reflectDemo 类:

package MyReflect;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;

public class reflectDemo {
	public static void main(String[] args) throws Exception {
		Student s1 = new Student("张三",18);
		
		saveObject(s1);
		
	}
	
	public static void saveObject(Object obj) throws IOException, IllegalAccessException {
		
		// 获取字节码文件的对象 
		Class cs = obj.getClass();
		
		// 创建IO流
		BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
		
		// 获取所有成员变量
		Field[] fields = cs.getDeclaredFields();
		for(Field field : fields) {
			field.setAccessible(true);
			// 获取成员变量的名字
			String name = field.getName();
			// 获取成员变量的值
			Object value = field.get(obj);
			// 写出数据
			bw.write(name + " = " + value);
			bw.newLine();
		}
		bw.close();
	}
}

结果如下:

name = 张三
age = 18

案例 2:跟配置文件结合动态创建

反射可以跟配置文件结合的方式,动态的创建对象,并调用方法

prop.properties 配置文件:

classname = MyReflect.Student
method = study

Student 类:

package MyReflect;

public class Student {
	private String name;
	private int age;
	
	public Student() {}
	public Student(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 study() {
		System.out.println("正在学习!");
	}
}

reflectDemo 类:

package MyReflect;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class reflectDemo {
	public static void main(String[] args) throws Exception {
		
		// 读取配置文件中的信息
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream("prop.properties");
		prop.load(fis);
		fis.close();
		System.out.println(prop);
		
		// 获取全类名和方法名
		String className = (String) prop.get("classname");
		String methodName = (String) prop.get("method");
		
		System.out.println(className);
		System.out.println(methodName);
		
		// 利用反射创建对象并运行方法
		Class cs = Class.forName(className);
		
		// 获取构造方法
		Constructor con = cs.getDeclaredConstructor();
		Object o = con.newInstance();
		System.out.println(o);
		
		// 获取成员方法并运行
		Method method = cs.getDeclaredMethod(methodName);
		method.setAccessible(true);
		method.invoke(o);
	}
}

结果如下:

{classname=MyReflect.Student, method=study}
MyReflect.Student
study
MyReflect.Student@136432db
正在学习!

动态代理

概述

特点:无入侵式的给代码增加额外的功能

为什么要有代理?

  • 代理是为了在不改变原有对象代码的前提下,实现功能增强、访问控制、简化复杂操作和方便测试,提升代码的灵活性、可维护性和可测试性(对象如果嫌身上干的事太多了,可以通过代理来转移部分职责)

代理是什么样的?

  • 代理是一个与目标对象实现相同接口并包裹其引用的类,分为静态编写和动态生成两种形式,用于控制访问或增强功能(对象有什么方法想被代理,代理就一定要有对应的方法)

创建代理对象

java.lang.reflect.Proxy 类:提供了为对象产生代理对象的方法

  • public static Object newProxyInstance(ClasLoader loader, Class<?>[] interfaces, InvocationHandler h)
    • 参数一:用于指定哪个类加载器,去生成类加载器的代理类
    • 参数二:指定接口,这些接口用于指定生成的代理长什么样,有哪些方法
    • 用来指定生成的代理对象要做什么

InvocationHandler是一个接口,其中的方法是:

  • public Object invoke(Object proxy, Method method, Object[] args)
    • 参数一:代理的对象(一般不用)
    • 参数二:要运行的方法
    • 参数三:调用方法时传递的实参
  • 使用匿名内部类去写

案例

大明星坤坤准备开个唱跳演唱会,但是他不能直接去向粉丝收取演唱会的费用,这个时候公司就会给坤坤分配一个代理——经纪人,让经纪人来收取费用

坤坤 BigStar 类:

public class BigStar implements firm{
	private String name;
	
	public BigStar() {}
	public BigStar(String name) {
		this.name = name;
	}
	
	public String sing(String name) {
		System.out.println(this.name + "开始唱歌");
		System.out.println(this.name + "正在唱" + name);
		
		System.out.println(this.name + "结束唱歌");
		return "唱歌部分结束,接下来是跳舞";
	}
	
	public String dance() {
		System.out.println(this.name + "正在跳舞");
		System.out.println(this.name + "结束跳舞");
		return "演唱会结束了";
	}
}

公司安排经纪人 firm 接口:

public interface firm {
	public String sing(String name);
	public String dance();
}

代理经纪人 ProxyUtil 类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {
	public static firm creatProxy(BigStar bigstar) {
		firm starProxy = (firm) Proxy.newProxyInstance(
            	// 用当前类的类加载器去加载代理
				ProxyUtil.class.getClassLoader(),
				// 可以有多个接口
            	new Class[] {firm.class},
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						if("sing".equals(method.getName())) {
							System.out.println("演唱会费用已收完");
						}else if("dance".equals(method.getName())) {
							System.out.println("准备开始跳舞");
						}
                        // 这里的invoke方法是反射获取成员方法中的方法
						return method.invoke(bigstar, args);
					}
				});
		return starProxy;
	}
}

演唱会 Concert 类:

public class Concert {
	public static void main(String[] args) {
        // 获取代理的对象
		BigStar bigstar = new BigStar("坤坤");
		firm firmProxy = ProxyUtil.creatProxy(bigstar);
        
		String song = firmProxy.sing("及你太美");
		System.out.println(song);
		String dance = firmProxy.dance();
		System.out.println(dance);
	}
}

结果如下:

演唱会费用已收完
坤坤开始唱歌
坤坤正在唱及你太美
坤坤结束唱歌
唱歌部分结束,接下来是跳舞
准备开始跳舞
坤坤正在跳舞
坤坤结束跳舞
演唱会结束了

相关文章:

  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加列宽调整功能,示例Table14_03可调整列宽的固定表头表格
  • 机器学习与深度学习算法及工具在图像分类中的应用总结
  • 移动Android和IOS自动化中常见问题
  • NPM安装与配置全流程详解(2025最新版)
  • Nginx的流式响应配置详解
  • 【每日学点HarmonyOS Next知识】状态变量、动画UI残留、Tab控件显示、ob前缀问题、文字背景拉伸
  • 【网络编程】HTTP网络编程
  • 前后端+数据库的项目实战--学生信息管理系统-易
  • Cesium 入门教程(基于 vue3)
  • Linux学习(十五)(故障排除(ICMP,Ping,Traceroute,网络统计,数据包分析))
  • 如何查看redis的缓存时间
  • js 使用 Web Workers 来实现一个精确的倒计时,即使ios手机锁屏或页面进入后台,倒计时也不会暂停。
  • 每天一篇《目标检测》文献(二)
  • Java线程详解
  • 【Linux】线程控制
  • 第四章:深入理解框架之 GUI 模块
  • ARM64异常处理技术
  • 我与红队:一场网络安全实战的较量与成长
  • Android DUKPT - 3DES
  • 文件上传漏洞(条件竞争)
  • 马上评|家长抱婴儿值护学岗,如何避免“被自愿”?
  • 黑龙江省政府副秘书长许振宇,拟任正厅级领导
  • 董军在第六届联合国维和部长级会议上作大会发言
  • 证监会:2024年依法从严查办证券期货违法案件739件,作出处罚决定592件、同比增10%
  • 陕西省安康市汉阴县县长陈永乐已任汉阴县委书记
  • 市场监管总局召开平台企业支持个体工商户发展座谈会