java - Java中的对象在内存中的字段和值遍历
问题描述
我有一个场景,我需要遍历内存中的一个复杂对象。我需要遍历整个树的键和值以及所需的东西。有什么办法吗?它不能序列化为 JSON,这是一个大问题。因此,唯一的方法是在对象加载到内存时对其进行遍历。
对象实际上看起来像这样。它可能有深度嵌套,GSON 或任何其他库都无法将其序列化为 JSON。
解决方案
非常非常复杂。
给定任何对象,获取其所有字段:
Class<?> c = obj.getClass();
var fields = new ArrayList<Field>();
while (c != null) {
for (Field f : c.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) continue;
f.setAccessible(true);
fields.add(f);
}
c = c.getSuperclass();
}
看起来工作量很大,但您也必须使用declaredFields
变体来获取私有字段,并且它不会自动为您遍历超类型层次结构。获得该列表后,您可以使用通常的field.get(obj)
. 然后.. 将相同的算法递归地应用于 THOSE 对象以构建这样的层次结构。
问题:数组
数组需要特别小心。你不能只问它的领域;它没有。因此,你需要这个,使用后field.get(obj)
:
if (a.getClass().isArray()) {
int len = Array.getLength(a);
for (int i = 0; i < len; i++) {
Object actualContent = Array.get(a, i);
}
}
然后将“actualContent”的每个值视为正常值(即在其上递归运行此算法)。
问题:要打印的东西太多了!
是的,它是——一个单独的字符串有一个char[]
内部。您可能想要对已知类型(例如String
.
问题:JDK11+ 发出警告。
是的,最终这将不再被允许,并且如果不连接调试器代理,就无法做到这一点,这是一个更复杂的蠕虫罐。从 JDK14 开始,这仍然有效,即使它会打印警告。
问题:循环/自引用
对象可以在循环中引用自己。例如:
List<Object> list = new ArrayList<Object>();
list.add(list);
// or a cycle:
List<Object> list1 = new ArrayList<Object>();
List<Object> list2 = new ArrayList<Object>();
list1.add(list2);
list2.add(list1);
这将导致您的打印代码永远循环。解决这个问题的方法是跟踪对象身份,如果您发现一个您已经“看到”的对象,根本不要打印它,只需打印一些参考。这还要求您打印每个对象的引用。要获得一个标识对象的数字,请使用System.identityHashCode
,它可以保证很好地尝试为您提供一个唯一的 ID(但不能 100% 保证每个对象都一定会获得一个唯一的 ID)。要检查您是否已经看到了一个对象,请使用IdentityHashMap
,其中地图的值部分无关紧要(如果有,IdentityHashSet
我会告诉您使用它,但没有。只需映射到 "" 并检查相反,密钥的存在)。
考虑所有这些事情,您也可以编写一个深入研究的对象打印机。
或者只是依靠您的 IDE 来执行此操作,对于使您遇到的任何问题,可能有更好的解决方案:我知道!我将递归地打印该对象的全部原始内容!- 但你的问题不包括你为什么想要这个。
推荐阅读
- php - Composer 需求开发问题
- python - 无法从 Python 3.6 读取 excel 文件
- sql - 具有常数值的 Oracle (+) 运算符
- mongodb - 我如何优化这个查询,$ememMatch 需要更多时间来执行
- android - Laravel 5.6 事件广播
- laravel - laravel 中 Connection.php 第 770 行中的 QueryException
- .htaccess - 使用 htaccess 从 URL 中删除单词
- php - Datatables.js 用于请求和检索数据的 JSON 格式是什么?
- javascript - 反应器路由器 dom 不工作
- gnuplot - 将 ytics 四舍五入到最接近的十