我有一个场景,我需要遍历内存中的复杂对象。我需要遍历整个树的键和值以及所需的内容。有什么办法可以做到吗?它无法序列化为 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 变体来获取私有(private)字段,并且它不会自动为您遍历父类(super class)型层次结构。获得该列表后,您可以使用通常的 field.get(obj)
获取该字段的对象集。然后..将相同的算法递归地应用于这些对象以构建这样的层次结构。
问题:数组
数组需要特别小心。你不能只向它询问它的字段;你还可以向它询问它的字段。它不会有。因此,在使用 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
)进行一些instanceof检查和“硬编码”自定义打印机。
问题: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
我会告诉您使用那个,但没有。只需映射到“”并检查 key 是否存在)。
考虑所有这些事情,您也可以编写一个深入的对象打印机。
或者只是依靠您的 IDE 来完成此操作,对于任何让您遇到的问题,可能都有更好的解决方案:我知道!我将递归地打印该对象的全部原始内容! – 但你的问题不包括你为什么想要这个。
关于java - Java中对象在内存中的字段和值遍历,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61069630/