在Java中分解对象

5
我有一个非常大的对象,当然我知道它的类。我正在从中提取一些值,但是由于它非常大,我不知道我要找的值在哪个列表或者哪个位置。
有没有一种方式可以创建某种对象分解路由,并搜索每个部分以查找我期望的值,因为它被隐藏在对象的某个地方,我无法在Eclipse中找到它;它太嵌套了。
我考虑使用反射来遍历对象类的所有字段,并在每个字段内部搜索该值(列表内的字段(列表的列表等))。还有其他想法吗?

1
看看这个是否有帮助:https://dev59.com/TnVC5IYBdhLWcg3wbQbA - Alberto Zaccagni
3
听起来你需要对这个对象进行重构和重新设计。 - Oded
既然你在这里问,我就假设“询问发明者”的答案已经被追求过或者不可能了? - Carl
@Oded 那不是一个选项,虽然是个好主意。 - ant
1
我不明白这个。你是想在源代码中查找某个字符串,还是在运行时查找某个未知字段的值?如果是前者,请使用 Eclipse 的搜索功能。 - Tim Büthe
显示剩余2条评论
11个回答

6

我猜您只想找到特定的值并跟踪其来源。而且,您希望在调试时完成这一切。我建议两个选项。

选项1 使用JSON - 将对象序列化为json字符串并在结果上进行手动文本搜索。您需要json.jar(或任何其他解析器)。

    try {
       System.out.println(new JSONObject(new YourHugeObject()).toString(5));
    } catch (JSONException e) {
      log(e);
    }

这将会产生类似于这样的结果。 (我通过创建一个带有一些嵌套字段、列表和映射的对象来模拟这个过程)

{
 "ct": {
      "a": 1,
      "b": "sdf",
      "f": 12,
      "nested": {
           "key1": {
                "kk": "kk",
                "ssdf": 123
           },
           "onemorekey": {
                "kk": "kk",
                "ssdf": 123
           }
      }
 },
 "doubleProp": 12.2,
 "lngprop": 1232323,
 "strProp": "123",
 "stringlist": [
      "String1",
      "String2",
      "String3"
 ]
}

选项2 将对象转换/序列化为XML。使用XStream,这将是所有可用解析器中最简单的。只需两行代码即可实现。

    XStream stream = new XStream();
    System.out.println(stream.toXML(new YourHugeObject()));

这将会产生以下结果:

<com.kmg.jsontools.test.JSTest>
<stringlist>
 <string>String1</string>
 <string>String2</string>
 <string>String3</string>
</stringlist>
<strProp>123</strProp>
<doubleProp>12.2</doubleProp>
<lngprop>1232323</lngprop>
<ct>
 <a>1</a>
 <b>sdf</b>
 <f>12.0</f>
 <nested>
   <entry>
     <string>key1</string>
     <com.kmg.jsontools.test.Type1>
       <kk>kk</kk>
       <ssdf>123</ssdf>
     </com.kmg.jsontools.test.Type1>
   </entry>
   <entry>
     <string>onemorekey</string>
     <com.kmg.jsontools.test.Type1>
       <kk>kk</kk>
       <ssdf>123</ssdf>
     </com.kmg.jsontools.test.Type1>
   </entry>
 </nested>
</ct>
</com.kmg.jsontools.test.JSTest>

以上两种方法中,你可以将结果输出到控制台或文件中,然后手动检查。另外,你也可以使用反射(reflection)的方式,不过这种方法需要编写大量代码,并且在测试时也需要投入相当多的时间。


4
使用来自Jakarta-Commons包中的ToStringBuilder,即org.apache.commons.lang.builder包,如何?
System.out.println ( ToStringBuilder.reflectionToString( YOUR_OBJECT ) );

2
创建一些静态方法,假设:
Map<String, Object> DebugHelper.breakDown(Object bean)

将其实现为数据挖掘,例如使用org.apache.pivot.beans.BeanAdapter可以帮助您将bean视为普通映射,或者(如您所写-您需要深入挖掘)使用递归将所有属性分组为单个大映射。

另一个有用的签名:

Map<String, Object> DebugHelper.breakDown(Object bean, String lookFor)

第二个参数用于在第一个方法的Map中查找值的子字符串。

为什么这很有用?因为您可以使用Eclipse的检查器在调试的任何时候计算此方法的结果。


你能否举个例子来说明你所指的静态方法是什么?我并不是很明白你的意思,在大多数情况下。 - ant
@c0mrade,这是链接:http://ideone.com/BjygK 通过将其放置到DebugHelper.breakDown(someInstance,“3.14”)中-您可以找到具有递归挖掘的bean属性,该属性返回Pi的值(例如)。您可以根据需要更改挖掘策略。请注意,这使用了Apache的常见bean utils。 - Dewfy

1

我认为你需要使用反射来获取数据。这里是一些代码,应该可以帮助你入门:

static String search(Object o, final String search) {
    try {
        inspect(new HashSet<Object>(), o, new StringWriter() {
            public void write(String s) {
                System.out.println(s);
                if (s.indexOf(search) >= 0) {
                    throw new RuntimeException(s) {
                        public Throwable fillInStackTrace() { return null; } };
                }
            }
        });
        return "not found";
    } catch (Exception e) {
        return e.getMessage();
    }
}

private static void inspect(HashSet<Object> ignore, Object o, Writer w)
        throws Exception {
    if (o == null) {
        return;
    }
    for (Class<?> c = o.getClass(); c != null ; c = c.getSuperclass()) {
        if (c.isArray()) {
            int len = Array.getLength(o);
            for (int i=0; i<len; i++) {
                inspect(ignore, Array.get(o, i), w);
            }
        } else if (c.isPrimitive()) {
            w.write(o.toString());
        } else {
            for (Field f : c.getDeclaredFields()) {
                if (!Modifier.isStatic(f.getModifiers())) {
                    f.setAccessible(true);
                    if (f.getType().isPrimitive()) {
                        w.write(f.getName() + ": " + f.get(o));
                    } else {
                        if (!ignore.contains(o)) {
                            ignore.add(o);
                            w.write(f.getName());
                            inspect(ignore, f.get(o), w);
                        }
                    }
                }
            }
        }
    }
}

1
如果这个类是可序列化的,我通常使用XStream将对象转储为XML。
如果不是,这里有一些代码可以帮助你开始反射模式。它不像你需要的那样递归,但可以很容易地进行修改。
public static void debugPrint(Object o1)
{
    if(o1 == null)
    {
        System.out.println(o1);
        return;
    }

    Class c = o1.getClass();
    do
    {
        printFields(c, o1);
        c = c.getSuperclass();
    }
    while(c != null);
}

private static void printFields(Class c, Object o1)
{
    Field[] fields = c.getDeclaredFields();
    for(Field field : fields)
    {
        printField(field, o1);
    }
}

private static void printField(Field field, Object o1)
{
    try
    {
        if(Modifier.isFinal(field.getModifiers())) return;  //Skip this guy, he's trouble!

        field.setAccessible(true);

        Object val = field.get(o1);

        System.out.println(field.getName() + ":" + toString(val));
    }
    catch(IllegalAccessException ex)
    {
        System.out.println("Could not access field" + field.getName());
        ex.printStackTrace();
    }
}

private static String toString(Object o)
{
    if(o instanceof Object[])
        return Arrays.toString((Object[])o);
    else
        return String.valueOf(o);
}

0

免责声明:这段代码是暴力且非常丑陋的。我只想用这些基本思路演示如何实现 OP 想要的功能:

  1. 反射
  2. 递归
  3. Field#setAccessible
  4. 针对数组、IterableEnumeration、原始包装类和 String 的特殊情况

@Thomas Mueller's answer 包含了其中一些元素,但我的 #3 和 #4 更加健壮。而他的代码似乎无法处理我扭曲的测试类。然而我可能仍然错过了一些边角案例。请不要把这个发给我的 Java 导师。

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

public class BreakDown
{
    public static void main(String[] args)
    {
        printFields(new TestClass(), "");
    }

    private static void printFields(final Object obj, final String prefix)
    {
        if( basics.contains(obj.getClass()) )
        {
            System.out.println(prefix + obj);
            return;
        }
        for( final Field f : obj.getClass().getDeclaredFields() )
        {
            try
            {
                try
                {
                    printOneField(f, obj, prefix);
                    continue;
                }
                catch( SecurityException e ) {}
                catch( IllegalAccessException e ) {}
                AccessController.doPrivileged(new PrivilegedAction<Void>()
                {
                    public Void run()
                    {
                        try
                        {
                            printOneField(f, obj, prefix);
                        }
                        catch( Exception e )
                        {
                            e.printStackTrace();
                        }
                        return null;
                    }
                });
            }
            catch( Exception e )
            {
                e.printStackTrace();
            }
        }
    }

    private static void printOneField(Field f, Object obj, String prefix) throws Exception
    {
        f.setAccessible(true);
        System.out.println(prefix + "  |" + f.getName() + ":" + (obj = f.get(obj)));
        if( obj == null )
            return;

        if( obj.getClass().isArray() )
        {
            for( int i = 0; i < Array.getLength(obj); ++i )
                printObj(Array.get(obj, i), prefix, f.getName());
        }
        else if( obj instanceof Iterable )
        {
            Iterator<?> it = ((Iterable<?>)obj).iterator();
            for( ; it.hasNext(); )
                printObj(it.next(), prefix, f.getName());
        }
        else if( obj instanceof Enumeration<?> )
        {
            Enumeration<?> e = (Enumeration<?>)obj;
            for( ; e.hasMoreElements(); )
                printObj(e.nextElement(), prefix, f.getName());
        }
        else if( !basics.contains(obj.getClass()) )
            printFields(obj, prefix + "  |" + f.getName() + ":" + obj.getClass().getCanonicalName());
    }

    private static void printObj(Object o, String prefix, String name)
    {
        printFields(o, "  " + prefix + "  |" + name + ":[(" + o.getClass().getSimpleName() + ")");
    }

    private static final Set<Class<?>> basics = new HashSet<Class<?>>();
    static
    {
        basics.add(Integer.class);
        basics.add(Long.class);
        basics.add(Double.class);
        basics.add(Float.class);
        basics.add(Boolean.class);
        basics.add(Character.class);
        basics.add(Byte.class);
        basics.add(Void.class);
        basics.add(Short.class);
        basics.add(String.class);
    }
}

class TestClass
{
    public TestClass()
    {
        if( ++count_s < 3 )
            list.add(new TestClass());
    }

    private static int count_s = 0; 
    public final String a = "a";
    final TestClass2 obj = new TestClass2();
    final TestClass2[] objArray = new TestClass2[]{new TestClass2(), new TestClass2()};
    private final int b = count_s;
    private final boolean[] c = new boolean[]{true, false};
    private final List<TestClass> list = new ArrayList<TestClass>();
}

class TestClass2
{
    private static int count_s = 0; 
    private final float d = 1.5f * (++count_s);
}

输出结果为:

  |count_s:3
  |a:a
  |obj:TestClass2@6e1408
  |obj:TestClass2  |count_s:9
  |obj:TestClass2  |d:1.5
  |objArray:[LTestClass2;@5483cd
    |objArray:[(TestClass2)  |count_s:9
    |objArray:[(TestClass2)  |d:3.0
    |objArray:[(TestClass2)  |count_s:9
    |objArray:[(TestClass2)  |d:4.5
  |b:0
  |c:[Z@19ee1ac
    |c:[(Boolean)true
    |c:[(Boolean)false
  |list:[TestClass@1befab0]
    |list:[(TestClass)  |count_s:3
    |list:[(TestClass)  |a:a
    |list:[(TestClass)  |obj:TestClass2@13c5982
    |list:[(TestClass)  |obj:TestClass2  |count_s:9
    |list:[(TestClass)  |obj:TestClass2  |d:6.0
    |list:[(TestClass)  |objArray:[LTestClass2;@1186fab
      |list:[(TestClass)  |objArray:[(TestClass2)  |count_s:9
      |list:[(TestClass)  |objArray:[(TestClass2)  |d:7.5
      |list:[(TestClass)  |objArray:[(TestClass2)  |count_s:9
      |list:[(TestClass)  |objArray:[(TestClass2)  |d:9.0
    |list:[(TestClass)  |b:1
    |list:[(TestClass)  |c:[Z@14b7453
      |list:[(TestClass)  |c:[(Boolean)true
      |list:[(TestClass)  |c:[(Boolean)false
    |list:[(TestClass)  |list:[TestClass@c21495]
      |list:[(TestClass)  |list:[(TestClass)  |count_s:3
      |list:[(TestClass)  |list:[(TestClass)  |a:a
      |list:[(TestClass)  |list:[(TestClass)  |obj:TestClass2@1d5550d
      |list:[(TestClass)  |list:[(TestClass)  |obj:TestClass2  |count_s:9
      |list:[(TestClass)  |list:[(TestClass)  |obj:TestClass2  |d:10.5
      |list:[(TestClass)  |list:[(TestClass)  |objArray:[LTestClass2;@c2ea3f
        |list:[(TestClass)  |list:[(TestClass)  |objArray:[(TestClass2)  |count_s:9
        |list:[(TestClass)  |list:[(TestClass)  |objArray:[(TestClass2)  |d:12.0
        |list:[(TestClass)  |list:[(TestClass)  |objArray:[(TestClass2)  |count_s:9
        |list:[(TestClass)  |list:[(TestClass)  |objArray:[(TestClass2)  |d:13.5
      |list:[(TestClass)  |list:[(TestClass)  |b:2
      |list:[(TestClass)  |list:[(TestClass)  |c:[Z@15b7986
        |list:[(TestClass)  |list:[(TestClass)  |c:[(Boolean)true
        |list:[(TestClass)  |list:[(TestClass)  |c:[(Boolean)false
      |list:[(TestClass)  |list:[(TestClass)  |list:[]

0
也许是一个完全不同的方法。尝试将您的对象(分解后)放入内存数据存储器(参见http://www.google.de/search?hl=en&q=java+inmemory+data+store+search),然后使用其搜索功能。在执行测试后,您应该能够感受到它在您的情况下是否表现更好。如果您需要频繁读取对象,也许使用内存数据库会更好。

0

0

一些提示 -

1 ) 您可以使用 shift 和单击变量 / 方法 / 类,这将正确地将您带到它。

2 ) 右键单击 - 打开调用层次结构将向您显示变量被调用的确切位置。

3 ) Command 和 O 将向您显示类的大纲,以便您可以快速导航

4 ) 快速类型层次结构将向您显示变量在类的总体方案中的位置和方式。

希望这有所帮助


0
通常打印出一个大对象的值可以帮助你仔细查看它们。在Java和C++中,我编写了函数来打印对象的所有内容,并使用递归处理嵌入式对象。调试对象变量的硬拷贝虽然有点老派,但对于非常复杂的情况,它会带来额外的发现。

通常打印出一个大对象的值会有所帮助,这样你就可以仔细地查看它们。 - ant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接