写点什么

利用 Java 反射处理 private 变量

作者:FunTester
  • 2021 年 12 月 15 日
  • 本文字数:3103 字

    阅读完需:约 10 分钟

在 Java 基础中,private 是一个访问权限最严格的修饰符。但是在我们工作当中,使用第三方 jar 包的时候甚至使用 JDK 里面的工具类的时候,经常会遇到一些 private 修饰变量,我们想访问甚至修改这个变量的时候就显得比较麻烦。


这个时候我们需要通过 Java 反射方案来实现我们访问和修改 private 修饰的变量。

核心 API

java.lang.reflect.Field类中有一个java.lang.reflect.AccessibleObject#setAccessible(boolean)方法可以设置反射访问变量的时候跳过权限检查。


这个 API 不仅可以访问对象变量,也可以访问静态变量。

封装类

这个是 Groovy 写的,对 JDK 的反射相关 API 进行了封装,其中有些异常并没有处理。


package com.funtester.utils
import com.funtester.base.exception.FailException
import java.lang.reflect.Fieldimport java.lang.reflect.InvocationTargetExceptionimport java.lang.reflect.Method
/** * 私有变量访问工具类,可用于final修饰的变量 */class PriUtil {
/*** * 获取私有成员变量的值 * */ static <F> F get(Object instance, String name, Class<F> f) { try { Field field = instance.getClass().getDeclaredField(name); field.setAccessible(true); // 参数值为true,禁止访问控制检查
return (F) field.get(instance); } catch (NoSuchFieldException | IllegalAccessException e) { FailException.fail("获取${instance.toString()}私有变量$name 失败 ${e.getMessage()}"); } }
/*** * 设置私有成员变量的值 * */ static void set(Object instance, String fileName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = instance.getClass().getDeclaredField(fileName); field.setAccessible(true); field.set(instance, value); }
/*** * 访问私有方法 * */ static <F> F call(Object instance, String name, Class<F> r, Class[] parameterTypes, Object[] params) { Method method = instance.getClass().getDeclaredMethod(name, parameterTypes); method.setAccessible(true); return (F) method.invoke(instance, params); }
/** * 获取static变量 * @param c * @param name * @param f * @return */ static <F> F get(Class c, String name, Class<F> f) { Field[] fields = c.getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); if (field.getType() == f && field.getName().equals(name)) return (F) field.get(c); } } catch (Exception e) { FailException.fail("获取${c.name}私有变量$name 失败 ${e.getMessage()}"); } }
/** * 设置static变量 * @param c * @param name * @param f */ static void set(Class c, String name, Object f) { Field[] fields = c.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals(name)) field.set(c, f) } }
/** * 调用私有static方法 * @param c * @param name * @param r * @param parameterTypes * @param params * @return */ static <F> Object call(Class c, String name, Class<F> r, Class[] parameterTypes, Object[] params) { try { Method method = c.getMethod(name, parameterTypes); method.setAccessible(true); return (F) method.invoke(null, params); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { FailException.fail("执行${c.name}私有方法$name 失败,参数 ${e.getMessage()}") } }
}
复制代码

测试类

这里我简单写了一个测试类,一个成员变量,一个类变量。


/** * 反射访问private测试类 */public class PriBase {
private String name = "FunTester";
private static String cname = "CFunTester";
}
复制代码

测试脚本

首先我测试一下非静态变量,测试脚本如下:


import com.funtester.frame.SourceCodeimport com.funtester.utils.PriUtil
class PriTest extends SourceCode{
public static void main(String[] args) { PriBase base = new PriBase() PriUtil.set(base,"name","修改后name") String get = PriUtil.get(base, "name", String.class) output(get) PriBase base1 = new PriBase() String get1 = PriUtil.get(base1, "name", String.class) output(get1) }}
复制代码


控制台输出:


INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16INFO-> main   ###### #     #  #    # ####### ######  #####  ####### ######  #####  #      #     #  ##   #    #    #       #         #    #       #    #  ####   #     #  # #  #    #    ####    #####     #    ####    #####  #      #     #  #  # #    #    #            #    #    #       #   #  #       #####   #    #    #    ######  #####     #    ######  #    #
INFO-> main 修改后nameINFO-> main FunTester
Process finished with exit code 0
复制代码


其次我们测试一下静态变量,测试脚本如下:


import com.funtester.frame.SourceCodeimport com.funtester.utils.PriUtil
class PriTest extends SourceCode{
public static void main(String[] args) { PriUtil.set(PriBase.class,"cname","修改后name") String get = PriUtil.get(PriBase.class, "cname", String.class) output(get) }}
复制代码


控制台输出:


INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16INFO-> main   ###### #     #  #    # ####### ######  #####  ####### ######  #####  #      #     #  ##   #    #    #       #         #    #       #    #  ####   #     #  # #  #    #    ####    #####     #    ####    #####  #      #     #  #  # #    #    #            #    #    #       #   #  #       #####   #    #    #    ######  #####     #    ######  #    #
INFO-> main 修改后name
Process finished with exit code 0
复制代码


完美实现我们的需求,以后再也不用管什么访问权限了,哈哈哈~~~

欢迎关注 FunTester,Have Fun ~ Tester !

发布于: 19 小时前阅读数: 10
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
利用Java反射处理private变量