【问题标题】:Java: what is static{}?Java:什么是静态{}?
【发布时间】:2010-12-16 18:31:51
【问题描述】:

谁能解释一下下面是什么?

public class Stuff
{
    static
    {
        try
        {
            Class.forName("com.mysql.jdbc.Driver");
        }
        catch ( ClassNotFoundException exception )
        {
            log.error( "ClassNotFoundException " + exception.getMessage( ) );
        }
...
}

这个 static { ...} 有什么作用?

我知道 C++ 中的静态变量,但那是静态块还是什么?

这东西什么时候执行?

【问题讨论】:

标签: java static-initializer


【解决方案1】:

静态块称为class static initializer - 它在类第一次加载时运行(也是唯一一次运行[脚注])。

该特定块的目的是检查MySQL 驱动程序是否在类路径上(如果不在则抛出/记录错误)。


[脚注] 每个加载类的类加载器都会运行一次静态块(因此,如果您有多个彼此不同的类加载器(例如,不委托),它将每个执行一次。

【讨论】:

  • "它在类第一次加载时运行(也是唯一一次运行)。" - 它不是每个加载类的类加载器运行一次吗?我知道技术性,但可能值得一提。
  • 在这种情况下,检查 MySQL 驱动程序并不是所有工作:实例化类会自动将其注册到 DriverManager。这是在后面的 DB connect() 语句中响应 MySQL URL 所必需的。
  • 如果 catch 子句抛出 RunTimeException 而不是仅仅记录错误,加载类 Stuff 将被中止,并且对它的任何引用都会导致运行时出现 NoClassDefFoundError。
【解决方案2】:

静态初始化块的主要用途是在构造函数中执行可能不适合的各种初始化,以便将构造函数和初始化程序一起将新创建的对象置于完全一致的状态以供使用。

例如,与构造函数相比,静态初始化程序不会被继承,并且只会在类被 JRE 加载和初始化时执行一次。在上面的示例中,一旦初始化完成,类变量 foo 的值将是 998877。

另请注意,静态初始化程序是按照它们在源文件中以文本形式出现的顺序执行的。此外,在其中一个块中不能执行的操作有许多限制,例如不使用检查异常、不使用 return 语句或 this 和 super 关键字。

【讨论】:

  • Static 初始化块(与构造函数和普通初始化块相反)与 object 初始化没有任何关系。
【解决方案3】:

我想补充一点,静态变量和静态初始化程序在类加载时按出现顺序执行。因此,如果您的静态初始化程序依赖于某个静态变量,则必须在特定静态块之前对其进行初始化,例如

final static String JDBC_DRIVER = getJdbcDriver( );

static
{
  try
  {
    Class.forName(JDBC_DRIVER);
  }
  catch ( ClassNotFoundException exception )
  {
    log.error( "ClassNotFoundException " + exception.getMessage( ) );
  }
}

在此示例中,getJdbcDriver 将在静态初始化程序之前执行。此外,类中可能有超过 1 个静态初始化程序。再次按出现顺序执行。

我还想在这里提到实例初始化程序的存在,因为第一次看到它们时确实会感到惊讶。它们看起来像类体内的代码块,像这样。

class MyClass
{
  final int intVar;

  {
    intVar = 1;
  }
}

在一般情况下,由于构造函数的原因,它们的使用有些不必要,但它们在实现 Java 版本的闭包时很有用。

【讨论】:

    【解决方案4】:

    每当第一次加载类时,都会执行静态初始化程序块。如果更高级别的某事第一次在相关课程上执行Class#forName("yourpackage.YourClass")new YourClass(),则可能会发生这种情况。

    巧合的是,不错的 JDBC 驱动程序内部也有类似的东西。他们即使用静态初始化块在DriverManager 中注册自己:

    static {
        DriverManager.registerDriver(new ThisDriver());
    }
    

    这样每当您执行Class.forName("somepackage.ThisDriver") 时,您将有效地在DriverManager 中注册驱动程序,以便您之后可以从中获得连接。

    【讨论】:

      【解决方案5】:

      除了以上所有之外,在使用类构造函数和类初始化器方面还有一点区别。我们知道构造函数通常用于初始化对象,如果我们有静态变量,则通常使用静态块来在加载类时初始化它们。

      当我们有静态变量和静态块时,静态变量首先初始化,然后是块。

      当类首次加载时,静态块在类构造函数之前初始化。

      【讨论】:

        【解决方案6】:

        静态初始化块

        • 是正常的代码块

        • 用大括号括起来{}

        • 前面是静态关键字

          class Foo {
              static {
                  // initialization code goes here:
                  doSomething();
              }
          }
          
        • 类可以有任意数量的静态初始化块

        • 它们可以出现在类主体中的任何位置

        • 它们在代码中按出现顺序调用

        有一个静态初始化块的替代方案:

        • 写一个私有静态方法
        • 并将其分配给静态类变量

        这种方法的优点是可以稍后调用静态方法来重新初始化类变量。

        class Foo {
            public static int myVar = initializeClassVariable();
        
            private static int initializeClassVariable() {
                // initialization code goes here:
                int v = 255;
                return v;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2013-09-18
          • 2012-01-12
          • 2017-08-13
          • 1970-01-01
          • 2010-10-16
          • 2023-03-10
          • 2011-01-21
          • 2023-03-29
          相关资源
          最近更新 更多