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

Java反射和动态代理用法(附10道练习题)

目录

  • 一、什么是反射
  • 二、反射的核心接口和类
  • 三、测试代码 Bean 类和目录结构
    • Person 类
    • 代码目录结构
  • 四、反射的用法
    • 1. 获取 Class 对象
    • 2. 获取构造方法 Constructor 并使用
    • 3. 获取成员变量 Field 并使用
    • 4. 获取成员方法 Method 并使用
  • 五、动态代理与反射
    • 1. 动态代理三要素
      • (1)代理接口
      • (2)代理处理器
      • (3)代理对象的创建
    • 2. 动态代理实现步骤
    • 3. 案例:获取函数的执行时间
  • 六、练习
    • 1. 使用反射获取String类的所有公有方法,并把方法名打印出来。
    • 2. 使用反射创建一个对象,并调用其无参构造方法。
    • 3. 使用反射修改一个对象的私有字段值。
    • 4. 使用反射获取一个ArrayList的所有父类(包括间接父类)。
    • 5. 使用反射调用一个类的静态方法。
    • 6. 使用反射获取某个类的所有公有成员变量,并打印出每个成员变量的名称和类型。
    • 7. 使用反射获取某个类的所有成员方法,并打印出每个方法变量的名称和返回值类型。
    • 8. 使用反射调用一个对象的公有方法,并传递参数。
    • 9. 使用Java的动态代理实现一个简单的日志记录功能。
    • 10. 使用Java的动态代理实现一个简单的权限校验功能。

一、什么是反射

解释一:

Java 反射机制是在运行状态中,对于任意一个,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法。这种动态获取信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

解释二:
Java 反射是指在运行时动态检查和操作类的能力。通过反射,(对于一个对象)程序可以在运行时获取关于类、方法、属性、构造函数等的详细信息,并且可以动态地创建对象、调用方法以及访问和修改字段。反射提供了一种灵活的机制,使得程序可以在编译时不知道确切类型的情况下操作这些类型

二、反射的核心接口和类

Java 反射主要涉及以下几个核心类和接口,它们位于包 java.lang.reflect中:

Class:每个类和接口在 JVM 中都表示为一个 Class 对象。通过 Class 对象,程序可以获取类的全限定名、实现的接口、父类、构造函数、方法、字段等信息。
Constructor:表示类的构造函数。通过 Constructor 对象,程序可以创建类的新实例。
Field:表示类的属性。通过 Field 对象,程序可以获取或修改属性的值。
Method:表示类的方法。通过 Method 对象,程序可以调用方法。

三、测试代码 Bean 类和目录结构

Person 类

Person类有nameage属性,无参构造方法有参构造方法gettersettertoString以及自定义的sayHellosayGoodbye方法。

public class Person {private String name;private int age;public Person() {this.name = "unknown";this.age = 0;}public Person(String name, int age) {this.name = name;this.age = age;}// 省略getter()和setter()@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}// public类型public void sayHello() {System.out.println("Hello, my name is " + this.name);}// private类型private void sayGoodbye() {System.out.println("Goodbye, my name is " + this.name);}}

代码目录结构

在这里插入图片描述

四、反射的用法

1. 获取 Class 对象

Class 对象包含了类的结构信息,是反射的入口点。获取 Class 对象有三种方法,如下所示:

1. 类.class
2. 对象.getClass()
3. Class.forName()

public class Main {public static void main(String[] args) {// 方式1: 类.class语法Class<?> cls1 = Person.class;System.out.println(cls1);               // class PersonSystem.out.println(cls1.getName());     // Person// 方式2: 对象.getClass()Class cls2 = new Person().getClass();System.out.println(cls2.getName());     // Person// 方式3: 使用静态方法Class.forName(),需要捕获ClassNotFoundExceptiontry {Class<?> cls3 = Class.forName("Person");System.out.println(cls3.getName()); // Person} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

2. 获取构造方法 Constructor 并使用

cls.getDeclaredConstructor()获取 Class 对象的所有构造方法
cls.getConstructor()获取 Class 对象的公有构造方法
constructor.newInstance()使用 Class 构造方法创建对象

import java.lang.reflect.Constructor;public class Main {public static void main(String[] args) {try {// 获取Person类的Class对象Class cls = Class.forName("Person");// Class personClass = Person.class;// Class personClass = new Person().getClass();// 获取无参构造方法Constructor<?> noArgsConstructor = cls.getConstructor();// 创建对象Object person1 = noArgsConstructor.newInstance();// 重写了Person的toString方法直接打印即可System.out.println(person1); // Person{name='unknown', age=0}// 获取带参数的构造方法Constructor<?> paramArgsConstructor = cls.getConstructor(String.class, int.class);// 创建对象,并传递参数Object person2 = paramArgsConstructor.newInstance("Alice", 30);System.out.println(person2); // Person{name='Alice', age=30}} catch (Exception e) {e.printStackTrace();}}
}

3. 获取成员变量 Field 并使用

cls.getDeclaredField(name)获取 Class 对象的所有成员变量
cls.getField(name)获取 Class 对象的所有公有成员变量
field.setAccessible(true)设置 Class 对象的属性值可访问
field.get()获取 Class 对象的属性值
field.set()设置 Class 对象的属性值

import java.lang.reflect.Field;public class Main {public static void main(String[] args) {try {// 获取Person类的Class对象Class cls = Class.forName("Person");// Class personClass = Person.class;// Class personClass = new Person().getClass();// 创建Person对象Person person = new Person("Alice",30);// 获取name字段Field nameField = cls.getDeclaredField("name");// 设置可访问性,因为name是私有的nameField.setAccessible(true);// 获取name字段的值String name = (String) nameField.get(person);System.out.println("Name: " + name);        // Name: Alice// 获取age字段Field ageField = cls.getDeclaredField("age");// 设置可访问性,因为age是私有的ageField.setAccessible(true);// 获取age字段的值int age = ageField.getInt(person);System.out.println("Age: " + age);          // Age: 30// 修改age字段的值ageField.setInt(person, 31);// 再次获取age字段的值,验证修改是否成功age = ageField.getInt(person);System.out.println("Updated Age: " + age);  // Updated Age: 31} catch (Exception e) {e.printStackTrace();}}
}

4. 获取成员方法 Method 并使用

cls.getDeclaredMethod(name)获取 Class 对象的所有成员方法
cls.getMethod(name)获取 Class 对象的公有成员方法
method.setAccessible(true)设置 Class 对象的方法可访问
method.invoke()调用 Class 对象的成员方法

import java.lang.reflect.Method;public class Main {public static void main(String[] args) {try {// 获取Person类的Class对象Class<?> cls = Class.forName("Person");// 创建Person对象Person person = (Person) cls.getDeclaredConstructor(String.class, int.class).newInstance("Alice", 30);//或 Person person =new Person("Alice", 30);// 获取sayHello方法Method sayHelloMethod = cls.getMethod("sayHello");// 调用sayHello方法sayHelloMethod.invoke(person);// 获取sayGoodbye方法Method sayGoodbyeMethod = cls.getDeclaredMethod("sayGoodbye");// 设置可访问性,因为sayGoodbye是私有的sayGoodbyeMethod.setAccessible(true);// 调用sayGoodbye方法sayGoodbyeMethod.invoke(person);} catch (Exception e) {e.printStackTrace();}}
}

五、动态代理与反射

Java 动态代理是 Java 语言中一种用于在运行时创建代理实例的机制,它允许拦截并处理对任何对象的调用

通过动态代理,可以在不修改原始对象的情况下,对其方法进行增强或添加额外的行为。可以在方法执行前后进行一些操作,比如日志记录、性能监测、事务管理等。

1. 动态代理三要素

在Java中,要实现动态代理,需要满足以下必备条件:

(1)代理接口

必须有一个或多个接口。动态代理只能为接口创建代理实例,不能为类创建代理。

(2)代理处理器

需要实现java.lang.reflect.InvocationHandler接口,该接口包含一个invoke方法,用于处理所有对代理对象的方法调用。

(3)代理对象的创建

使用java.lang.reflect.Proxy类的newProxyInstance方法来创建代理对象。该方法需要以下三个参数:

  • ClassLoader:用于加载代理类的类加载器
  • Class<?>[] interfaces:代理类要实现的接口数组
  • InvocationHandler:处理代理实例上的方法调用的调用处理器

2. 动态代理实现步骤

(1)定义一个或多个接口,声明需要代理的方法。
(2)创建一个实现InvocationHandler接口的类,重写invoke方法以定义如何处理方法调用。
(3)使用Proxy.newProxyInstance方法创建代理对象,传入相应的类加载器接口数组调用处理器实例。

3. 案例:获取函数的执行时间

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义一个简单的接口,其中 sayHello 方法接收一个字符串参数
interface Hello {void sayHello(String message);void sayGoodBye(String message);
}// 实现这个接口
class HelloImpl implements Hello {// 实现接口中的 sayHello 方法,并设置默认消息@Overridepublic void sayHello(String message) {try {// 打印接收到的消息,模拟耗时操作System.out.println("Hello " + message);Thread.sleep(300);} catch (InterruptedException e) {// 异常处理e.printStackTrace();}}@Overridepublic void sayGoodBye(String message) {try {// 打印接收到的消息,模拟耗时操作System.out.println("GoodBye " + message);Thread.sleep(300);} catch (InterruptedException e) {// 异常处理e.printStackTrace();}}}// 实现 InvocationHandler 接口
class TimeInvocationHandler implements InvocationHandler {private final Object target; // 目标对象// 构造函数,接收目标对象public TimeInvocationHandler(Object target) {this.target = target;}@Override// 处理代理实例的所有方法调用public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 判断方法名,只对 sayHello 方法进行时间测量if ("sayHello".equals(method.getName())) {long startTime = System.currentTimeMillis(); // 记录方法开始执行的时间Object result = method.invoke(target, args); // 调用目标对象的方法long endTime = System.currentTimeMillis(); // 记录方法结束执行的时间System.out.println("成员方法 " + method.getName() + " 花费了 " + (endTime - startTime) + " 毫秒.");return result; // 返回方法的执行结果} else {// 对于 sayGoodBye 方法,直接调用目标对象的方法,不进行时间测量return method.invoke(target, args);}}
}public class Main {public static void main(String[] args) {// 创建目标对象Hello hello = new HelloImpl();// 创建调用方法的处理器TimeInvocationHandler timeInvocationHandler = new TimeInvocationHandler(hello);// 创建一个代理实例Hello proxyInstance = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(),  // 类加载器hello.getClass().getInterfaces(),   // 代理类要实现的接口timeInvocationHandler               // 调用方法的处理器);// 使用代理对象调用方法,实际上会执行处理器的invoke方法proxyInstance.sayGoodBye("Python");// GoodBye PythonproxyInstance.sayHello("Java");// Hello Java// 成员方法 sayHello 花费了 305 毫秒.}
}

六、练习

1. 使用反射获取String类的所有公有方法,并把方法名打印出来。

import java.lang.reflect.Method;public class Main {public static void main(String[] args) {Class<String> cls = String.class;Method[] methods = cls.getMethods();for (Method method : methods) {System.out.println(method.getName());}}
}

2. 使用反射创建一个对象,并调用其无参构造方法。

import java.lang.reflect.Constructor;public class Main {public static void main(String[] args) {Class<String> cls = String.class;try {Constructor<String> constructor = cls.getConstructor(String.class);// 指定了String就可以不用ObjectString s = constructor.newInstance("Hello world!");System.out.println(s);} catch (Exception e) {e.printStackTrace();}}
}

3. 使用反射修改一个对象的私有字段值。

import java.lang.reflect.Field;class Employee {private int age;public Employee() {}public Employee(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}private void printAge() {System.out.println(this.age);}
}public class Main {public static void main(String[] args) {Employee employee = new Employee(30);Class cls = employee.getClass();try {Field ageField = cls.getDeclaredField("age");ageField.setAccessible(true);int age = ageField.getInt(employee);System.out.println(age);                // 30ageField.setInt(employee,31);System.out.println(employee.getAge());  // 31} catch (Exception e) {e.printStackTrace();}}
}

4. 使用反射获取一个ArrayList的所有父类(包括间接父类)。

import java.util.ArrayList;public class Main {public static void main(String[] args) {Class cls = ArrayList.class;while (cls != null) {System.out.println(cls.getName());cls = cls.getSuperclass();}}
}

5. 使用反射调用一个类的静态方法。

import java.lang.reflect.Method;class MyMath {public static <T extends Number> T add(T a, T b) {if (a instanceof Integer) {return (T) Integer.valueOf(a.intValue() + b.intValue());} else if (a instanceof Double) {return (T) Double.valueOf(a.doubleValue() + b.doubleValue());} else {throw new IllegalArgumentException("Unsupported number type");}}
}public class Main {public static void main(String[] args) {Class cls = MyMath.class;try {// 由于泛型擦除,需要指定方法的确切参数类型Method method = cls.getDeclaredMethod("add", Number.class, Number.class);// 静态方法必须指定nullObject invoke = method.invoke(null, 1, 2);System.out.println(invoke); // 3} catch (Exception e) {e.printStackTrace();}}
}

6. 使用反射获取某个类的所有公有成员变量,并打印出每个成员变量的名称和类型。

import java.lang.reflect.Field;class People {public int id;public int age;private String name;
}public class Main {public static void main(String[] args) {Class<People> cls = People.class;Field[] PeopleFields = cls.getFields();for (Field peopleField : PeopleFields) {System.out.println(peopleField.getName() + " => " + peopleField.getType());}//id => int//age => int}
}

7. 使用反射获取某个类的所有成员方法,并打印出每个方法变量的名称和返回值类型。

import java.lang.reflect.Method;class People {public void printHello() {System.out.println("Hello, Java");}public String getHello(String Hello) {return Hello + ", Java";}private int getMoney() {return 0;}}public class Main {public static void main(String[] args) {Class<People> cls = People.class;Method[] methods = cls.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName() + " => " + method.getReturnType());}//printHello => void//getHello => class java.lang.String//getMoney => int}
}

8. 使用反射调用一个对象的公有方法,并传递参数。

import java.lang.reflect.Method;class MyMath {public int add(int a, int b) {return a + b;}
}public class Main {public static void main(String[] args) {Class<MyMath> myMathClass = MyMath.class;try {Method addMethod = myMathClass.getMethod("add", int.class, int.class);MyMath myMath = new MyMath(); // 创建 MyMath 类的实例Object invoke = addMethod.invoke(myMath, 1, 2); // 传递 MyMath 类的实例System.out.println(invoke); // 3} catch (Exception e) {e.printStackTrace();}}
}

9. 使用Java的动态代理实现一个简单的日志记录功能。

题目描述:创建一个接口Operation,包含一个方法execute(String message)。实现该接口的类OperationImpl。使用动态代理为OperationImpl添加日志记录功能,即在执行execute方法前后打印日志。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface Operation {void execute(String message);
}class OperationImpl implements Operation {@Overridepublic void execute(String message) {System.out.println("执行操作:" + message);}
}class LoggingHandler implements InvocationHandler {private Object target;public LoggingHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开始执行方法:" + method.getName());Object result = method.invoke(target, args);System.out.println("方法执行结束:" + method.getName());return result;}
}public class Main {public static void main(String[] args) {Operation operation = new OperationImpl();Operation proxyInstance = (Operation) Proxy.newProxyInstance(Operation.class.getClassLoader(),   // operation.getClass().getInterfaces(),new Class[]{Operation.class},       // operation.getClass().getInterfaces(),new LoggingHandler(operation));proxyInstance.execute("Hello, World!");//开始执行方法:execute//执行操作:Hello, World!//方法执行结束:execute}
}

10. 使用Java的动态代理实现一个简单的权限校验功能。

题目描述:创建一个接口UserService,包含一个方法login(String username, String password)。实现该接口的类UserServiceImpl。使用动态代理为UserServiceImpl添加权限校验功能,即只有当用户名为"admin"且密码为"123456"时,才允许执行login方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface UserService {boolean login(String username, String password);
}class UserServiceImpl implements UserService {@Overridepublic boolean login(String username, String password) {System.out.println(username + " 登录成功!");return true;}
}class AuthHandler implements InvocationHandler {private Object target;public AuthHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("login")) {String username = (String) args[0];String password = (String) args[1];if ("admin".equals(username) && "123456".equals(password)) {return method.invoke(target, args);} else {System.out.println("权限校验失败,用户名或密码错误!");return false;}}return method.invoke(target, args);}
}public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserService proxyInstance = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},new AuthHandler(userService));proxyInstance.login("admin", "123456"); // 权限校验通过,执行login方法//输出: admin 登录成功!proxyInstance.login("user", "123456"); // 权限校验失败//输出: 权限校验失败,用户名或密码错误!}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C++ 入门基础:开启编程之旅
  • C语言------指针讲解(2)
  • Facebook未来展望:数字社交平台的进化之路
  • 类与对象-继承-继承语法
  • idea中使用maven
  • react基础样式控制
  • Dav_笔记9:Using Indexes and Clusters之1
  • ROS2 humble使用nav2_map_server保存地图报错:Failed to spin map subscription
  • qt 下拉列表变更事件
  • leetcode-383.赎金信
  • 阿里ChatSDK使用,开箱即用聊天框
  • 前端面试题日常练-day92 【Less】
  • JVM OutOfMemoryError异常模拟
  • C语言经典程序100案例
  • 编程从零基础到进阶(更新中)
  • codis proxy处理流程
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • HTTP请求重发
  • java 多线程基础, 我觉得还是有必要看看的
  • JavaScript的使用你知道几种?(上)
  • Kibana配置logstash,报表一体化
  • Laravel 菜鸟晋级之路
  • oldjun 检测网站的经验
  • spring + angular 实现导出excel
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 阿里研究院入选中国企业智库系统影响力榜
  • 电商搜索引擎的架构设计和性能优化
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 京东美团研发面经
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 深入 Nginx 之配置篇
  • 说说动画卡顿的解决方案
  • 算法之不定期更新(一)(2018-04-12)
  • 通过几道题目学习二叉搜索树
  • 我是如何设计 Upload 上传组件的
  • 异常机制详解
  • 智能网联汽车信息安全
  • 阿里云ACE认证学习知识点梳理
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​浅谈 Linux 中的 core dump 分析方法
  • # windows 运行框输入mrt提示错误:Windows 找不到文件‘mrt‘。请确定文件名是否正确后,再试一次
  • #100天计划# 2013年9月29日
  • #Linux(帮助手册)
  • #数据结构 笔记一
  • (a /b)*c的值
  • (一一四)第九章编程练习
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .net core Redis 使用有序集合实现延迟队列
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...