java - Java 中的构造函数保证了哪些属性?

标签 java security inheritance concurrency constructor

我曾经认为,从直觉上讲,Java 中的构造函数就是创建对象的东西,在其构造函数返回之前,没有任何东西可以触及该对象。然而,我一次又一次地被证明是错误的:

  1. 共享this可以泄露未初始化的对象
  2. uninitialized objects can be leaked by a subclass accessing it from the finalizer
  3. uninitialized objects can be leaked to another thread before they're fully constructed

所有这些事实都违背了我认为构造函数是什么的直觉。

我再也不能自信地说出构造函数在 Java 中的实际作用,或者它的用途。如果我用所有最终字段制作一个简单的 DTO,那么我可以理解构造函数的用途是什么,因为这与 C 中的结构完全相同,只是它不能被修改.除此之外,我不知道在 Java 中可以可靠地使用哪些构造函数。它们只是一种约定/语法糖吗? (即,如果只有工厂为您初始化对象,您将只有 X x = new X(),然后修改 x 中的每个字段,使它们没有默认值——考虑到上面的 3 个事实,这几乎等同于 Java 的实际情况)

我可以说出两个实际上由构造函数保证的属性:如果我执行 X x = new X(),那么我知道 xX 但不是 X 的子类,并且其最终字段已完全初始化。您可能会想说您知道 X 的构造函数已完成并且您有一个有效的对象,但如果您将 X 传递给另一个线程,这是不正确的 - the other thread may see the uninitialized version (即你刚才所说的与调用工厂的保证没有什么不同)。构造函数实际上保证了哪些其他属性?

最佳答案

All of these facts violate my intuition of what I thought a constructor is.

他们不应该。构造函数完全按照您的想法行事。

1: uninitialized objects can be leaked by sharing this

3: uninitialized objects can be leaked to another thread before they're fully constructed

this 的泄漏问题,在构造函数中启动线程,以及存储一个新构造的对象,多个线程在没有同步的情况下访问它,这些都是围绕非 final 的初始化重新排序的问题(和非 volatile )字段。但是初始化代码还是由构造函数完成的。构造对象的线程可以完整地看到对象。这是关于这些更改何时在其他线程中可见,这不是语言定义所保证的。

You might be tempted to say that you know that constructor of X finished and you have a valid object, but this is untrue if you pass X to another thread - the other thread may see the uninitialized version (i.e what you just said is no different than the guarantees of calling a factory).

这是正确的。同样正确的是,如果您有一个未同步的对象并且您在一个线程中改变它,其他线程可能会或可能不会看到该改变。这就是线程编程的本质。即使是构造函数也无法避免正确同步对象的需要。

2: uninitialized objects can be leaked by a subclass accessing it from the finalizer

本文档讨论的是终结器以及在对象被垃圾收集后不正确地访问对象的能力。通过破解子类和终结器,您可以生成一个未正确构造的对象,但这样做是一个主要的破解。对我来说,这不会以某种方式挑战构造函数的作用。相反,它展示了现代、成熟的 JVM 的复杂性。该文档还展示了如何编写代码来解决此问题。

What properties are guaranteed by constructors in Java?

根据定义,一个构造函数:

  1. 为对象分配空间。
  2. 将对象中的所有实例变量设置为其默认值。这包括对象父类(super class)中的实例变量。
  3. 为对象分配参数变量。
  4. 处理任何显式或隐式构造函数调用(在构造函数中调用 this() 或 super())。
  5. 初始化类中的变量。
  6. 执行构造函数的其余部分。

就您的 3 个问题而言,#1 和 #3 同样是关于何时非最终和非 volatile 字段的初始化被构造对象的线程以外的线程看到。不保证这种没有同步的可见性。

#2 问题展示了一种机制,如果在执行构造函数时抛出异常,您可以覆盖 finalize 方法来获取和不正确构造的对象。构造函数点 1-5 已发生。通过 hack,您可以绕过 6 的一部分。我想这是否会挑战构造函数的身份,这是旁观者的看法。

关于java - Java 中的构造函数保证了哪些属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16652966/

相关文章:

python - 如何访问其基类中的类字段值?

javascript - 这是一个可继承的 Promise 吗?

javascript - 如何制作一个可以多次继承的对象?

java - 为什么我不能在 Java 中屏蔽长数据类型的 32 位

java - 从首选项设置屏幕返回时刷新 MainActivity

php - CSRF 代币 - 如何正确实现?

ios - 在 iPhone 应用程序中安全地存储远程数据库凭据

java - 如何解决尝试在空对象引用上调用虚拟方法 'void android.widget.TextView.setText(java.lang.CharSequence)'

javax.xml.bind.UnmarshalException : unexpected element (uri :"", 本地 :"Group")

security - 访问构建 Dockerfile 所需的 secret /私有(private)文件?