代码之家  ›  专栏  ›  技术社区  ›  non sequitor

“这个”如何摆脱Java中的构造函数?

  •  18
  • non sequitor  · 技术社区  · 15 年前

    我听说这是在非线程安全代码中发生的,因为 构建 对象,但我真的不知道这个概念,即使在读过戈茨的书之后。我想巩固我对这段代码气味的理解,因为我可能会这样做,甚至没有意识到。请在您的解释中提供代码以使其保持不变,谢谢。

    6 回复  |  直到 12 年前
        1
  •  16
  •   Jon Skeet    15 年前

    非常简单的例子:

    public class Test
    {
        private static Test lastCreatedInstance;
    
        public Test()
        {
            lastCreatedInstance = this;
        }
    }
    
        2
  •  23
  •   KLE rslite    15 年前

    示例:在构造函数中,创建一个事件侦听器 内部阶级 (它隐式引用当前对象),并将其注册到侦听器列表中。
    =>这样,对象就可以被另一个线程使用,即使它没有完成其构造函数的执行。

         public class A {
    
          private boolean isIt;
          private String yesItIs;
    
          public A() {
            EventListener el = new EventListener() { ....};
            StaticListeners.register(el);
            isIt = true;
            yesItIs = "yesItIs";
          }
         }
    

    稍后可能发生的另一个问题是:对象A可以被完全创建,所有线程都可以使用,由另一个线程使用…除了该线程可以看到已创建的实例之外,具有“yesitis”值的yesitis,但不是 isIt !信不信由你,这可能发生!发生的是:

    = & gt; 同步只是阻塞线程的一半,另一半是线程间可见性 .

    Java选择的原因是性能:如果所有数据都将与所有线程共享,则线程间可见性会降低性能,因此只保证同步数据被共享…

        3
  •  7
  •   Bombe    15 年前

    这就是双重检查锁定不起作用的原因。天真的代码

    if(obj == null)
    {
      synchronized(something)
      {
         if (obj == null) obj = BuildObject(...);
      }
    } 
    // do something with obj
    

    不安全,因为对局部变量的赋值可能发生在构造的其余部分(构造函数或工厂方法)之前。因此,螺纹1可以位于 BuildObject 步骤:当线程2进入同一个块时,检测到非空 obj ,然后继续操作不完整的对象(线程1已在中间调用中调度完毕)。

        4
  •  5
  •   Michael Borgwardt    15 年前
    public class MyClass{
        String name;    
    
        public MyClass(String s)
        {
            if(s==null)
            {
                throw new IllegalArgumentException();
            }
            OtherClass.method(this);
            name= s;
        }
    
        public getName(){ return name; }
    }
    

    在上述代码中, OtherClass.method() 传递的实例 MyClass 在当时还未完全建成,即尚未履行合同 name 属性不为空。

        5
  •  2
  •   John Vint    15 年前

    史蒂夫·吉勒姆对双重检查锁具为什么坏的看法是正确的。如果线程A进入该方法,而obj为空,则该线程将开始创建对象的实例并将其赋给obj。线程B可能在线程A仍在实例化该对象(但尚未完成)时进入,然后将该对象视为非空,但该对象的字段可能尚未初始化。部分构造的物体。

    但是,如果您允许关键字this转义构造函数,则可能会出现相同类型的问题。假设您的构造函数创建了一个分叉线程的对象实例,该对象接受您的对象类型。现在您的对象可能尚未完全初始化,也就是说,您的某些字段可能为空。通过在构造函数中创建的对象对对象的引用,现在可以将您作为非空对象引用,但可以获取空字段值。

    再解释一下:

    构造函数可以初始化类中的每个字段,但如果允许“this”在创建任何其他对象之前进行转义,则当其他线程(如果为1)查看时,它们可以为空(或默认的初始值)。它们不是最终的或2个。它们没有被宣布为易变的

        6
  •  0
  •   msangel    12 年前
    public class Test extends SomeUnknownClass{
        public Test(){
            this.addListner(new SomeEventListner(){
                @Override
                void act(){}
            });
        }
    }
    

    在这个操作之后,someEventListner的instanse将像一个普通的内部类一样有一个到测试对象的链接。

    更多示例如下: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html