静态初始化块和常规静态初始化之间的区别

11

就像标题所说,究竟有什么区别呢?

public static String myString = "Hello World!";

public static String myString;

static {
    myString = "Hello World";
}

除了结构外,还有其他重要的区别吗?

9个回答

10

就您的示例而言,没有任何区别。但正如您所看到的:

public static String myString = "Hello World!";

只能使用表达式初始化变量。然而,在静态初始化块(JLS 8.7)中,可以执行任意数量的语句。例如,可以这样做:

static
{
    myString = "Hello";
    myString += " ";
    myString += "World";
}

就您的例子而言,显然没有必要这样做,但是有可能需要对变量进行初始化,所需的代码不止一个表达式,可能需要多个语句,因此Java引入了静态初始化器。


6

静态{...}块使您有机会完成比在字段声明中更多的工作。

例如,您可以填写地图的一些详细信息:

private static final Map<String, String> data = new HashMap<String, String>();

static {
    data.put("A", "Hello");
    data.put("B", "There");
    data.put("C", "You");
}

有时在实例化之前,您可能还需要获取数据(从文件、数据库等):
public class Foo {
    private static final Person person;

    static {
        InputStream personData = Foo.class.getResourceAsStream("something.txt");
        person = new Person(personData);
    }
    ...
}

3

静态初始化块和常规静态初始化的区别。

对于变量初始化,两者是相同的。

但是,如果我们想要连接数据库仅一次或加载任何操作仅一次。则在静态块中编写代码,因为它只会执行一次,当第一个类被加载时,无论您创建该类型的多少个对象。

编辑:

您还可以构建类似的块:

{
    // Do Something...
}

例子:

public class Demo {

    static{
        System.out.println("Static");
    }

    {
        System.out.println("Non-static block");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        Demo demo2 = new Demo();
    }
}

输出:

静态

非静态块

非静态块


2

在您的第一个示例中,变量在同一行声明并初始化。在第二个示例中,变量首先被声明,然后再进行初始化。在第二种情况下,您可能会在到达问题的初始化块之前进行任意数量的其他静态变量和块初始化。考虑以下情况:

public static String myString = "Hello World!";
public static String yourString = myString;
static {
    System.out.println(myString);
    System.out.println(yourString);
}

vs:

public static String myString ;
public static String yourString = myString;

static {
    myString = "Hello World";
}

static {
    System.out.println(myString);
    System.out.println(yourString);
}

第一个示例的输出:

你好,世界
你好,世界

第二个示例的输出:

你好,世界
null

2

继Scott Stanchfield所说的,你可以使用Collections.unmodifiableXXX()方法来确保安全性,尽管像Google Guava这样的库可能会使其不那么必要。请考虑以下内容:

public static final Map<String, String> CAPITALS;
static {
    Map<String, String> map = new HashMap<>(); //Java 7.
    map.put("NY", "Albany");
    map.put("MD", "Annapolis");
    map.put("VA", "Richmond");
    map.put("CT", "Hartford");
    // 46 more states
    CAPITALS = Collections.unmodifiableMap(map);
}

当然,一个52行的静态代码块可能会让人感到迷惑,所以你可以将静态代码块转换为静态方法。
public static final Map<String, String> CAPITALS = capitals();
private static Map<String, String> capitals() {
    Map<String, String> map = new HashMap<>(); //Java 7.
    map.put("NY", "Albany");
    map.put("MD", "Annapolis");
    map.put("VA", "Richmond");
    map.put("CT", "Hartford");
    // 46 more states
    return Collections.unmodifiableMap(map);
}

差别在于风格不同。你也可以直接使用数据库表进行操作。

2

使用静态块可以改变初始化的顺序,与声明的顺序不同。


1

静态变量存储一个在定义该变量的类的所有实例(或非实例)之间共享的值。

静态块是在类第一次加载时执行的代码段。

“就范围而言,静态块只能在同一类中访问”,而“静态变量可以从任何类访问”。


1

通常静态变量的值在该类的所有实例(或非实例)之间共享,而静态块是代码的一部分,在加载类时执行。在功能上没有区别。


1

非常惊讶的是,这里并没有明确回答最重要的特性。

是的,你可以有多行代码。但这有什么意义呢?你也可以在内联静态初始化赋值中只使用一次工厂方法调用。(static public final MyType mySPVar = factoryCreateObject();

所以让我们关注static public final变量:

但如果那个方法(或你的任何多行代码)抛出Exception甚至是Throwable呢?

  • Checked Exceptions(不是从RuntimeException派生的)
  • 或者可以被视为Unchecked Exceptions
    • 派生自RuntimeException
    • 或者派生自其他Throwable,比如Error
最重要的初始化块(静态和非静态)方面是能够进行本地化的个体异常处理,这仍然赋予了其他方法无法提供的独占/特权访问。
通过独占/特权访问,我解决了以下问题:
- 在静态变量初始化程序之后,成为运行在此类内部的第一个代码,因此我们有一个真正纯净的状态。 - 是唯一可以分配最终变量的代码块(除非您使用Reflection)。 - 不必担心本地化同步,因为 - 每个特定类的静态初始化程序方法从不并行运行。 - 在正常的Java条件下(没有Reflection或Bytecode Weaving/Injection魔术;与普通Java相反),保证每个静态初始化程序只运行一次。
为了反对这里的其他答案: 关于 clean code, 静态和公共可访问字段应该是final的 - 如果不是甚至是不变的。 这就是初始化块发挥最大作用的地方。
现在让我们看看其他情况:
对于任何其他场景,例如
  • (可以)稍后更改变量(非final)
  • 或者只能通过方法访问它(变量不公开,与Getter方法一起使用)
不同的解决方案更好:
  • 要更改变量,请使用Setter方法来控制变量/类/对象的状态
  • 对于仅通过方法访问变量,也可以使用惰性初始化,类似于if(myObj == null) {myObj = new Something();} return myObj;(考虑同步!)
补充: 所有这些谈论都考虑(静态)类变量,但这里写的大多数观点也与(非静态)对象成员变量密切相关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接