【问题标题】:Constructors called due to static initialization由于静态初始化而调用的构造函数
【发布时间】:2013-05-12 14:16:38
【问题描述】:

我正在阅读 Bruce Eckel 第 4 版的 Thinking in Java。在初始化和清理一章中,第 189 页第二段中的第一个要点提到:

尽管它没有显式使用 static 关键字,但构造函数实际上是一个静态方法。

我有以下代码:

class Bar {
    Bar() {
        System.out.println("Bar Creation");
    }
}

class Foo {
    static int x = 10;
    static Bar b = new Bar();

    Foo() {
        System.out.println("Foo Creation");
    }
}

public class Test { 
    public static void main(String[] args) {
        System.out.println(Foo.x);
    }
}

如果它说的是真的应该调用 Foo 的构造函数。我看不到以下代码会发生这种情况。

输出是:

Bar Creation
10

有人能解释一下这是什么意思吗?

我已尽力引用这本书。我认为该陈述之前或之后的部分与该问题的上下文中的该陈述没有太大关系。

谢谢,

判断

【问题讨论】:

  • 感谢您的所有 cmets。我完全理解,除非创建对象,否则不会调用构造函数。那么书中的说法是什么意思。

标签: java static constructor static-initialization


【解决方案1】:

没有理由仅仅因为您提到了课程就调用Foo()。类加载时调用像static Bar b = new Bar(); 这样的静态初始化器;静态方法由您的代码调用。

我猜这本书的意思是构造函数就像静态方法,因为 dispatch 是静态的:也就是说,没有办法继承和覆盖构造函数,以及调用站点对于构造函数或静态方法,总是引用在编译时确定的某个特定类。

(构造函数的这种静态性是“工厂”对象在其实例方法中构造对象的动机。)

【讨论】:

  • 感谢您的解释。
【解决方案2】:

仅仅是因为您还没有创建Foo 的新实例来访问它的静态字段x

如果你创建了new Foo(),构造函数代码就会被调用。由于 x 是一个静态成员字段,不需要创建持有者类的实例来访问该字段。

访问 x 的另一种方式是 new Foo().x,但不需要创建新的 Object。

打印“Bar Creation”的原因是,分配了Foo 声明的静态字段。初始化规范见JLS 12.4.1

【讨论】:

    【解决方案3】:

    除非你在 Foo 类上调用 new(),否则不会调用构造函数。由于 new 关键字在内部调用构造函数。您只是使用类的名称访问该类的静态字段,而无需创建实例。

    【讨论】:

      【解决方案4】:

      在创建新对象后调用构造函数。我不会称它为静态方法,因为它必须有一个实例,并且可以通过默认 this 访问该实例@

      public class Main {
          public Main() {
              // static methods do not have a `this`
              System.out.println("Main called" + this.getClass());
          }
      
          public static void main(String... ignore) {
              new Main();
          }
      }
      

      如果反编译字节码,可以看到static方法

      $ javap -c -p -cp . Main
      Compiled from "Main.java"
      public class Main {
        public Main();
          Code:
             0: aload_0       
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             7: new           #3                  // class java/lang/StringBuilder
            10: dup           
            11: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
            14: ldc           #5                  // String Main called 
            16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            19: aload_0       
            20: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
            23: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
            26: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            29: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            32: return        
      
        public static void main(java.lang.String...);
          Code:
             0: new           #11                 // class Main
             3: dup           
             4: invokespecial #12                 // Method "<init>":()V
             7: pop           
             8: return        
      }
      

      注意:static 方法有不同的修饰符。

      【讨论】:

        【解决方案5】:

        您更新了 Foo,而不是将 Bar 更新为 Foo 中的静态字段。您可以访问类中的静态变量,而无需构造它的实例。

        【讨论】:

          【解决方案6】:

          仅当您使用new 关键字时才会调用构造函数。由于您不执行new Foo(),因此不会调用构造函数。

          【讨论】:

            猜你喜欢
            • 2011-02-15
            • 2021-03-07
            • 1970-01-01
            • 1970-01-01
            • 2016-11-27
            • 2016-08-17
            • 2013-04-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多