使用Objenesis创建静态内部类的实例时出现java.lang.InstantiationError

时间:2019-09-16 16:26:08

标签: java reflection objenesis

我正在尝试创建一个实用程序方法,该方法应该能够深克隆任何对象。 ({Object.clone()仅在实现Cloneable的对象上起作用,我听说它还是有缺陷的。)

我正在使用Objenesis来创建对象的新实例,而无需使用构造函数。

但是,当尝试克隆JFrame时,出现以下异常:
(使用该类,因为我认为这应该是一个很好且复杂的测试)

java.lang.InstantiationError: [Ljava.util.concurrent.ConcurrentHashMap$Node;
    at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)

我对任何解决方案持开放态度,不一定仅限于Objenesis。

我的代码:

private static ObjenesisStd OBJENESIS = new ObjenesisStd();

@SuppressWarnings("unchecked")
public static <T> T clone(T object, boolean deep){
    if(object == null){
        return null;
    }else{
        try {
            T clone = (T) OBJENESIS.newInstance(object.getClass());
            List<Field> fields = ReflectionUtil.getAllFieldsInHierarchy(object.getClass());
            for(Field field : fields){
                boolean isAccessible = field.isAccessible();
                boolean isFinal = ReflectionUtil.isFinal(field);
                field.setAccessible(true);
                ReflectionUtil.setFinal(field, false);
                Class<?> type = field.getType();
                if(!deep || type.isPrimitive() || type == String.class){
                    field.set(clone, field.get(object));
                }else{
                    field.set(clone, clone(field.get(object), true));
                }
                field.setAccessible(isAccessible);
                ReflectionUtil.setFinal(field, isFinal);
            }
            return clone;
        } catch (Throwable e) {
            e.printStackTrace();
            //throw new RuntimeException("Failed to clone object of type " + object.getClass(), e);
            return null;
        }
    }
}


public static void main(String[] args) {
    GetterSetterAccess access = new GetterSetterAccess(JFrame.class);
    JFrame frame = new JFrame("Test Frame");
    for(String attr : access.getAttributes()){
        System.out.println(attr + " " + access.getValue(frame, attr));
    }

    System.out.println("----------------------------------------------");
    frame = clone(frame, true);


    for(String attr : access.getAttributes()){
        System.out.println(attr + " " + access.getValue(frame, attr));
    }
}

编辑:可以使其与已接受的答案一起使用,并提供其他一些修复程序:

  • 避免克隆原始类型的包装器(Integer.class等)
  • 避免使用克隆类(Class.class类的对象)
  • 将克隆的对象存储在Map中并重用它们,因此,如果对象A对对象B的引用以及对象B对对象A的引用,则不会陷入无限循环。我还使用了一个检查完全相等(==)的Map,而不是使用equals()
  • 创建了一个自定义的异常类,该类将被传递,而不是在每个级别上引发新的异常(导致大量的深度触发)。

1 个答案:

答案 0 :(得分:2)

我终于明白了。您的代码无法处理数组。因此,由于实例化“ [Ljava.util.concurrent.ConcurrentHashMap $ Node;”而失败。这是节点数组。

但是,我会主张,实际上,您不应该这样做。您将得到相当复杂的代码。根据您要执行的操作,可以使用Jackson或XStream进行编组/解组来执行复制。

如果您真的想继续该路径,则在对clone方法进行空检查之后,您将需要类似的内容。

    if(object.getClass().isArray()) {
        int length = Array.getLength(object);
        Object array = Array.newInstance(object.getClass().getComponentType(), length);
        for (int i = 0; i < length; i++) {
            Array.set(array, i, clone(Array.get(object, i), true));
        }
        return (T) array;
    }