我修改了对象转储方法以避免循环引用导致 StackOverflow 错误。这就是我最终的结果:
//returns all fields of the given object in a string
public static String dumpFields(Object o, int callCount, ArrayList excludeList)
{
//add this object to the exclude list to avoid circual references in the future
if (excludeList == null) excludeList = new ArrayList();
excludeList.add(o);
callCount++;
StringBuffer tabs = new StringBuffer();
for (int k = 0; k < callCount; k++)
{
tabs.append("\t");
}
StringBuffer buffer = new StringBuffer();
Class oClass = o.getClass();
if (oClass.isArray()) {
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("[");
for (int i = 0; i < Array.getLength(o); i++)
{
if (i < 0) buffer.append(",");
Object value = Array.get(o, i);
if (value != null)
{
if (excludeList.contains(value))
{
buffer.append("circular reference");
}
else if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class)
{
buffer.append(value);
}
else
{
buffer.append(dumpFields(value, callCount, excludeList));
}
}
}
buffer.append(tabs.toString());
buffer.append("]\n");
}
else
{
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("{\n");
while (oClass != null)
{
Field[] fields = oClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
if (fields[i] == null) continue;
buffer.append(tabs.toString());
fields[i].setAccessible(true);
buffer.append(fields[i].getName());
buffer.append("=");
try
{
Object value = fields[i].get(o);
if (value != null)
{
if (excludeList.contains(value))
{
buffer.append("circular reference");
}
else if ((value.getClass().isPrimitive()) || (value.getClass() == java.lang.Long.class) || (value.getClass() == java.lang.String.class) || (value.getClass() == java.lang.Integer.class) || (value.getClass() == java.lang.Boolean.class))
{
buffer.append(value);
}
else
{
buffer.append(dumpFields(value, callCount, excludeList));
}
}
}
catch (IllegalAccessException e)
{
System.out.println("IllegalAccessException: " + e.getMessage());
}
buffer.append("\n");
}
oClass = oClass.getSuperclass();
}
buffer.append(tabs.toString());
buffer.append("}\n");
}
return buffer.toString();
}
该方法最初是这样调用的:
System.out.println(dumpFields(obj, 0, null);
所以,基本上我添加了一个 excludeList ,其中包含所有之前检查过的对象。现在,如果一个对象包含另一个对象并且该对象链接回原始对象,则它不应该沿着该链进一步跟随该对象。
但是,我的逻辑似乎有缺陷,因为我仍然陷入无限循环。有谁知道为什么会这样?
编辑:
我仍然收到 StackOverflow 错误
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at java.lang.reflect.Field.copy(Field.java:127)
at java.lang.reflect.ReflectAccess.copyField(ReflectAccess.java:122)
at sun.reflect.ReflectionFactory.copyField(ReflectionFactory.java:289)
at java.lang.Class.copyFields(Class.java:2739)
at java.lang.Class.getDeclaredFields(Class.java:1743)
at com.gui.ClassName.dumpFields(ClassName.java:627)
我更新的方法:
public static String dumpFields(Object o, int callCount, IdentityHashMap idHashMap)
{
callCount++;
//add this object to the exclude list to avoid circual references in the future
if (idHashMap == null) idHashMap = new IdentityHashMap();
idHashMap.put(o, o);
//setup string buffer and add fields
StringBuffer tabs = new StringBuffer();
for (int k = 0; k < callCount; k++)
{
tabs.append("\t");
}
StringBuffer buffer = new StringBuffer();
Class oClass = o.getClass();
if (oClass.isArray()) {
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("[");
for (int i = 0; i < Array.getLength(o); i++)
{
if (i < 0) buffer.append(",");
Object value = Array.get(o, i);
if (value != null)
{
if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class)
{
buffer.append(value);
}
else
{
buffer.append(dumpFields(value, callCount, idHashMap));
}
}
}
buffer.append(tabs.toString());
buffer.append("]\n");
}
else
{
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("{\n");
while (oClass != null)
{
Field[] fields = oClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
if (fields[i] == null) continue;
buffer.append(tabs.toString());
fields[i].setAccessible(true);
buffer.append(fields[i].getName());
buffer.append("=");
try
{
Object value = fields[i].get(o);
if (value != null)
{
if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else if ((value.getClass().isPrimitive()) || (value.getClass() == java.lang.Long.class) || (value.getClass() == java.lang.String.class) || (value.getClass() == java.lang.Integer.class) || (value.getClass() == java.lang.Boolean.class))
{
buffer.append(value);
}
else
{
buffer.append(dumpFields(value, callCount, idHashMap));
}
}
}
catch (IllegalAccessException e)
{
System.out.println("IllegalAccessException: " + e.getMessage());
}
buffer.append("\n");
}
oClass = oClass.getSuperclass();
}
buffer.append(tabs.toString());
buffer.append("}\n");
}
return buffer.toString();
}
编辑2:
您的解决方案似乎非常好。不幸的是,即使我只在一个只有 4 个字段的小类上使用过它,我现在也遇到了 OutOfMemory 错误。这是我最终得到的代码:
//returns all fields of the given object in a string
public static String dumpFields(Object start)
{
class CallLevel
{
public Object target;
public int level;
public CallLevel(Object target, int level)
{
this.target = target;
this.level = level;
}
}
//create a work list
List<CallLevel> workList = new ArrayList<CallLevel>();
workList.add(new CallLevel(start, 0));
//add this object to the exclude list to avoid circual references in the future
IdentityHashMap idHashMap = new IdentityHashMap();
StringBuffer buffer = new StringBuffer();
while (!workList.isEmpty())
{
CallLevel level = workList.remove(workList.size() - 1);
Object o = level.target;
//add this object to the exclude list to avoid circual references in the future
idHashMap.put(o, o);
//setup string buffer and add fields
StringBuffer tabs = new StringBuffer();
int callCount = level.level;
for (int k = 0; k < callCount; k++)
{
tabs.append("\t");
}
callCount++;
Class oClass = o.getClass();
if (oClass.isArray()) {
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("[");
for (int i = 0; i < Array.getLength(o); i++)
{
if (i < 0) buffer.append(",");
Object value = Array.get(o, i);
if (value != null)
{
if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class)
{
buffer.append(value);
}
else
{
workList.add(new CallLevel(value, callCount));
}
}
}
buffer.append(tabs.toString());
buffer.append("]\n");
}
else
{
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("{\n");
while (oClass != null)
{
Field[] fields = oClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
if (fields[i] == null) continue;
buffer.append(tabs.toString());
fields[i].setAccessible(true);
buffer.append(fields[i].getName());
buffer.append("=");
try
{
Object value = fields[i].get(o);
if (value != null)
{
if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else if ((value.getClass().isPrimitive()) || (value.getClass() == java.lang.Long.class) || (value.getClass() == java.lang.String.class) || (value.getClass() == java.lang.Integer.class) || (value.getClass() == java.lang.Boolean.class))
{
buffer.append(value);
}
else
{
workList.add(new CallLevel(value, callCount));
}
}
}
catch (IllegalAccessException e)
{
System.out.println("IllegalAccessException: " + e.getMessage());
}
buffer.append("\n");
}
oClass = oClass.getSuperclass();
}
buffer.append(tabs.toString());
buffer.append("}\n");
}
}
return buffer.toString();
}
这么小的对象不应该导致 OutOfMemory 错误。
有任何想法吗?
编辑3:
改写版:
public static String dumpFields(Object start)
{
class CallLevel
{
public Object target;
public int level;
public CallLevel(Object target, int level)
{
this.target = target;
this.level = level;
}
}
//create a work list
List<CallLevel> workList = new ArrayList<CallLevel>();
workList.add(new CallLevel(start, 0));
//create an identity map for object comparison
IdentityHashMap idHashMap = new IdentityHashMap();
//setup a string buffer to return
StringBuffer buffer = new StringBuffer();
while (!workList.isEmpty())
{
CallLevel level = workList.remove(workList.size() - 1);
Object o = level.target;
//add this object to the exclude list to avoid circual references in the future
idHashMap.put(o, o);
//set string buffer for tabs
StringBuffer tabs = new StringBuffer();
int callCount = level.level;
for (int k = 0; k < callCount; k++)
{
tabs.append("\t");
}
//increment the call count for future calls
callCount++;
//set the class for this object
Class oClass = o.getClass();
//if this is an array, dump it's elements, otherwise dump the fields of this object
if (oClass.isArray()) {
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("[");
for (int i = 0; i < Array.getLength(o); i++)
{
if (i < 0) buffer.append(",");
Object value = Array.get(o, i);
if (value != null)
{
if (value.getClass().isPrimitive())
{
buffer.append(value);
}
else if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else
{
workList.add(new CallLevel(value, callCount));
}
}
}
buffer.append(tabs.toString());
buffer.append("]\n");
}
else
{
buffer.append("\n");
buffer.append(tabs.toString());
buffer.append("{\n");
while (oClass != null)
{
Field[] fields = oClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
//make sure this field exists
if (fields[i] == null) continue;
//ignore static fields
if (!Modifier.isStatic(fields[i].getModifiers()))
{
buffer.append(tabs.toString());
fields[i].setAccessible(true);
buffer.append(fields[i].getName());
buffer.append("=");
try
{
Object value = fields[i].get(o);
if (value != null)
{
if (fields[i].getType().isPrimitive())
{
buffer.append(value);
}
else if (idHashMap.containsKey(value))
{
buffer.append("circular reference");
}
else
{
workList.add(new CallLevel(value, callCount));
}
}
}
catch (IllegalAccessException e)
{
System.out.println("IllegalAccessException: " + e.getMessage());
}
buffer.append("\n");
}
}
oClass = oClass.getSuperclass();
}
buffer.append(tabs.toString());
buffer.append("}\n");
}
}
return buffer.toString();
}
我假设 getClass().isPrimitive() 仍然适用于数组索引,但我可能错了。如果是这样,您将如何处理?此外,其他 getClass() == Integer 等检查对我来说似乎是不必要的,因为 isPrimitive() 检查应该解决这个问题,对吧?
无论如何,在一个简单的对象上使用时,我仍然会遇到内存不足错误:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3209)
at java.lang.String.<init>(String.java:215)
at java.lang.StringBuffer.toString(StringBuffer.java:585)
at com.gui.ClassName.dumpFields(ClassName.java:702)
at com.gui.ClassName.setTextArea(ClassName.java:274)
at com.gui.ClassName.access$8(ClassName.java:272)
at com.gui.ClassName$1.valueChanged(ClassName.java:154)
at javax.swing.JList.fireSelectionValueChanged(JList.java:1765)
at javax.swing.JList$ListSelectionHandler.valueChanged(JList.java:1779)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:167)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:147)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:194)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:388)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:398)
at javax.swing.DefaultListSelectionModel.setSelectionInterval(DefaultListSelectionModel.java:442)
at javax.swing.JList.setSelectedIndex(JList.java:2179)
at com.gui.ClassName$1.valueChanged(ClassName.java:138)
at javax.swing.JList.fireSelectionValueChanged(JList.java:1765)
at javax.swing.JList$ListSelectionHandler.valueChanged(JList.java:1779)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:167)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:137)
at javax.swing.DefaultListSelectionModel.setValueIsAdjusting(DefaultListSelectionModel.java:668)
at javax.swing.JList.setValueIsAdjusting(JList.java:2110)
at javax.swing.plaf.basic.BasicListUI$Handler.mouseReleased(BasicListUI.java:2783)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:273)
at java.awt.Component.processMouseEvent(Component.java:6263)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3255)
at java.awt.Component.processEvent(Component.java:6028)
at java.awt.Container.processEvent(Container.java:2041)
at java.awt.Component.dispatchEventImpl(Component.java:4630)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4460)