【问题标题】:How can I create an instance of class without invoking constructor of this class? [closed]如何在不调用此类的构造函数的情况下创建类的实例? [关闭]
【发布时间】:2014-08-22 13:12:40
【问题描述】:

在某些情况下,我们可以在不调用实例类的构造函数的情况下创建实例。知道这些情况是什么(非 Reflection API)吗?

【问题讨论】:

  • 您好,您可以编辑并提供一些您发现此错误的代码吗?
  • 这是不可能的,因为构造函数是一个对象。
  • 我不知道。怎么可能允许这种情况发生?
  • 也许您考虑使用 Guava 调用像 Sets.newHashSet() 这样的构造函数的静态方法?
  • 你看的例子可以在code.google.com/p/objenesis找到,当这里没有公共构造函数时,你想绕过构造函数代码,或者设置final字段

标签: java


【解决方案1】:

这是破坏系统的可靠方法,但至少它不会调用构造函数。使用Unsafe#allocateInstance(Class)

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class Example {
    private String value = "42";
    public static void main(String[] args) throws Exception {
        Example instance = (Example) unsafe.allocateInstance(Example.class);
        System.out.println(instance.value);
    }

    static Unsafe unsafe;
    static {
        try {

            Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
            singleoneInstanceField.setAccessible(true);
            unsafe = (Unsafe) singleoneInstanceField.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印出来的

null

表示Example默认构造函数没有被调用。

【讨论】:

  • 经历 Unsafe 实在是太恶心了 ;)
  • 反射通常被认为是不好的,不安全就是邪恶的。
  • 有趣的是:如果value被声明为final,它被初始化,然后"42"将被打印出来。
  • @icza 不完全是。如果你用instance.value 打印出来,它会打印"42",因为它是一个常量表达式并且在编译时被替换了。但如果你使用Field 实例动态检索它,它实际上将是null
  • @icza 同样,如果你声明它final 并在构造函数中初始化它,你会再次看到它null
【解决方案2】:

涉及基于非构造函数的对象创建的两个“常见”情况是deserializationclone()

【讨论】:

  • 你能详细说明他们如何不使用构造函数吗?据我所知,每个实例都必须通过构造函数创建,所有这些都是隐藏它。
  • @JeroenVannevel 不,他们不会隐藏它。这是 JVM 的魔法,构造函数永远不会被调用(这很容易测试)。
  • @JeroenVannevel 克隆不是由本机方法实现的吗? stackoverflow.com/questions/6825982/…
  • @barbara 这不是“正确”答案是什么意思?要么您说这是不对的(那么您应该解释原因),要么您知道自己问题的答案并且在浪费人们的时间。
  • @icza 如果 OP 这样做就好了。
【解决方案3】:

我能想象的唯一情况是序列化和 JNI。

使用序列化,您可以通过反序列化输入流中的整个对象状态来创建新对象。在这种情况下没有调用构造函数。

使用 JNI,有 AllocObject 函数,它为新对象分配空间,也无需调用构造函数。

编辑:对clone() 的调用可能被视为另一种情况,但这取决于方法的实现方式。

【讨论】:

    【解决方案4】:

    创建对象实例的方法有 4 种

    1) 通过使用new关键字的构造函数-构造函数应该是可访问的

    2)通过serialization/deserialization-类应该是Serializable;

    3)通过clone()方法-该方法要实现标记接口Cloneable

    4) 通过反射——不用new关键字就可以访问构造函数并创建实例。反射最好的地方在于它可以用来为私有构造函数实例化对象,提供一个不会弄乱 SecurityManager

    【讨论】:

    • 第五元素:JNI
    • JNI 是我认为的最佳方式。尤其是当你熟悉 C 时。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 2017-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多