Java 构造对象时对对象的最终引用

Java 构造对象时对对象的最终引用,java,Java,假设我引用了某个类,它是最终的,如: public final Mycalss ref; MyClass有几个属性(不是最终属性)。现在,当我像这样构造MyClass对象时: ref = new MyClass( some arguments); 这是否意味着其他线程可以安全地看到Myclass的内容,因为它是最终引用的?进行引用final意味着一旦将对象分配给该引用,就无法将其他对象分配给该引用 但是,即使引用声明为final,也可以更改同一对象的值 比如: public final My

假设我引用了某个类,它是最终的,如:

public final Mycalss ref;
MyClass有几个属性(不是最终属性)。现在,当我像这样构造MyClass对象时:

ref = new MyClass( some arguments);

这是否意味着其他线程可以安全地看到Myclass的内容,因为它是最终引用的?

进行引用
final
意味着一旦将对象分配给该引用,就无法将其他对象分配给该引用

但是,即使引用声明为
final
,也可以更改同一对象的值

比如:

public final Myclass ref;
ref = new Myclass();

//this is not allowed
ref = new MyClass();   //since you have already assigned an object to your final reference

//this is allowed
ref.setSomeProperty("abc"):
有关螺纹和最终螺纹的答案,请参阅以下网址中的@Erwin Bolwidt答案。

final字段的使用模型很简单:设置final字段 对于该对象的构造函数中的对象;不要写一封信 指在另一个位置构造的对象 线程可以在对象的构造函数完成之前看到它。如果这 然后当另一个线程看到该对象时 线程将始终看到正确构造的版本 对象的最终字段。它还将看到任何对象或对象的版本 最终字段引用的数组,这些字段至少是最新的 正如最后的字段一样

(我的重点补充)

这是JLS第17节“线程和锁”的一部分,Java内存模型规范也是其中的一部分


是的,你说得对。这是一种安全构造对象的正确方法。

如果要使类的实例在其属性不可变的情况下对所有类可见,则应将属性声明为private,而不是创建任何setter,并使用Singleton模式(你可以在google上找到它,基本上你的类的构造函数是私有的,在你的类中你有一个字段是静态的,并且是你的类的唯一实例,你创建了一个getInstance()方法,它是静态的,并且返回当前实例和/或实例化它(如果它为null)

该类将如下所示:

public class A {
   private static A instance;

   private A() {}

   public static synchronized A getInstance() {
      if (instance == null) {
         instance = new A();
      }
      return instance;
   }
}

正如其他人所说的
final
意味着只能对该变量进行一次赋值。然而,在多线程环境中,如果线程A在线程B进行赋值之前访问对象,线程A将看到
null
。这意味着线程A可以看到变量的两个不同值


此外,您还应该记住关于-即,一个线程对对象所做的更改可能对另一个线程不可见。我建议您阅读同步和
volatile
关键字-。

final意味着该对象不能被重新引用。因为它的组件不是final的,所以它的值可能会发生变化,但这取决于设置是否有设置程序。其他线程是否可以看到您的对象,与它是否为最终对象无关。只要您不修改MyClass的字段,它是安全的。如果您修改它们,
final
的保证将是不够的。@assylias Yes-确切地说。assylias的意思是:如果您在assing之后修改它们将它们添加到最终字段。在将它们分配到最终字段之前修改它们是安全的。但是您询问了“何时构造它”(在标题中)和“何时构造”(在问题中),因此在这种情况下,它是安全的。这是不正确的。“生成最终引用与访问您的对象的其他线程没有关系。”安全发布引用是Java中
final
关键字的基本用法之一。请参阅我的答案。这并没有回答以下问题:“这是否意味着其他线程可以安全地查看Myclass的内容?”…是的。但是OP询问不同线程更改(可变)是否安全对象的字段-不是。您可以保证所有线程都会看到同一个实例,并且哪个实例不会更改。但是如果应用程序要正确实现线程安全,您仍然需要同步对对象内容的任何更新(可能是通过在对象上同步)。OP在哪里要求这样做?“这是否意味着其他线程可以安全地看到Myclass的内容,因为它是最终引用的?”我认为您误解了问题中的某些内容。旁白:最简单的技术是确保线程之间共享的对象是完全不可变的。您几乎总能解决相同的问题并实现这一点-这并不总是容易的(或自然的)但是在Java中要这样做。@Paul OP问“这是否意味着其他线程可以安全地看到Myclass的内容”,答案是肯定的,因为使用final可以保证安全发布。如果你开始变异Myclass的字段,你就失去了这一保证。@Erwin Boldwidt好吧,他说“看到内容”"他还说,对象的字段不是最终字段。这意味着,如果它们不是最终字段,那么它们可能会发生更改-可能来自不同的线程。否则,为什么不将这些字段也设置为最终字段呢?我不认为这有什么问题,如果它的任何属性都没有被修改,我会缺少什么吗?只是广告而已d在getInstance()上“已同步”方法,如果这是您所指的,那么尽快进行向下投票有点苛刻。编辑了我的OP。这是因为如果线程T1构造了一个,而T2来调用getInstance,它可能会看到一个指向一个部分构造的对象的非空引用。而且我看不到使用单例的必要性-OP从未提到他想要一个M的单例yClass…如果
A
是可变的(这种情况下)将其作为一个单例公开将是一场噩梦:任何访问
实例的代码段可能会做出随机影响其他代码段的更改……我明白,这就是为什么我在getInstance方法上添加了synchronized。也就是说,如果a的每个属性都有0个setter,并且是私有的,那么对象实例化后是可变的。他是否需要单例模式有待讨论,