首页 > 解决方案 > Java中的对象在内存中的字段和值遍历

问题描述

我有一个场景,我需要遍历内存中的一个复杂对象。我需要遍历整个树的键和值以及所需的东西。有什么办法吗?它不能序列化为 JSON,这是一个大问题。因此,唯一的方法是在对象加载到内存时对其进行遍历。

在此处输入图像描述

对象实际上看起来像这样。它可能有深度嵌套,GSON 或任何其他库都无法将其序列化为 JSON。

标签: javajsonobject

解决方案


非常非常复杂。

给定任何对象,获取其所有字段:

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 来执行此操作,对于使您遇到的任何问题,可能有更好的解决方案:我知道!我将递归地打印该对象的全部原始内容!- 但你的问题不包括你为什么想要这个。


推荐阅读