据我所知,“静态初始化块”是用来设置静态字段的值,如果它不能在一行中完成。

但我不明白为什么我们需要一种特殊的积木。例如,我们将一个字段声明为静态(没有赋值)。然后写几行代码,生成并赋值给上面声明的静态字段。

为什么我们需要这些行在一个特殊的块,如:static{…}?


当前回答

所以你有一个静态字段(它也被称为“类变量”,因为它属于类而不是类的实例;换句话说,它与类而不是与任何对象相关联),你想初始化它。所以如果你不想创建这个类的实例,你想操纵这个静态字段,你可以用三种方式来做:

1-在声明变量时初始化它:

static int x = 3;

2-有一个静态初始化块:

static int x;

static {
 x=3;
}

有一个类方法(静态方法)来访问类变量并初始化它: 这是上述静态块的替代方案;你可以写一个私有静态方法:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

为什么要用静态初始化块而不是静态方法呢?

这取决于你的项目需要什么。但是你必须知道静态初始化块只被调用一次,类方法的唯一优点是,如果你需要重新初始化类变量,它们可以被重用。

假设在程序中有一个复杂的数组。你初始化它(例如使用for循环),然后这个数组中的值将在整个程序中改变,但在某个时候你想重新初始化它(回到初始值)。在这种情况下,您可以调用私有静态方法。如果你不需要在程序中重新初始化这些值,你可以只使用静态块,不需要静态方法,因为你以后不会在程序中使用它。

注意:静态块是按照它们在代码中出现的顺序调用的。

示例1:

class A{
 public static int a =f();

// this is a static method
 private static int f(){
  return 3;
 }

// this is a static block
 static {
  a=5;
 }

 public static void main(String args[]) {
// As I mentioned, you do not need to create an instance of the class to use the class variable
  System.out.print(A.a); // this will print 5
 }

}

示例2:

class A{
 static {
  a=5;
 }
 public static int a =f();

 private static int f(){
  return 3;
 }

 public static void main(String args[]) {
  System.out.print(A.a); // this will print 3
 }

}

其他回答

我认为静态块只是语法糖。你不能用静态块做什么,也不能用其他任何东西。

重复使用这里发布的一些例子。

这段代码可以在不使用静态初始化器的情况下重写。

方法1:使用静态

private static final HashMap<String, String> MAP;
static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

方法2:无静电

private static final HashMap<String, String> MAP = getMap();
private static HashMap<String, String> getMap()
{
    HashMap<String, String> ret = new HashMap<>();
    ret.put("banana", "honey");
    ret.put("peanut butter", "jelly");
    ret.put("rice", "beans");
    return ret;
}
static int B,H;
static boolean flag = true;
static{
    Scanner scan = new Scanner(System.in);
    B = scan.nextInt();
    scan.nextLine();
    H = scan.nextInt();

    if(B < 0 || H < 0){
        flag = false;
        System.out.println("java.lang.Exception: Breadth and height must be positive");
    } 
}

非静态块:

{
    // Do Something...
}

每次构造类的实例时调用。静态块只被调用一次,当类本身初始化时,不管你创建了多少这种类型的对象。

例子:

public class Test {

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

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

    public static void main(String[] args) {
        Test t = new Test();
        Test t2 = new Test();
    }
}

这个打印:

Static
Non-static block
Non-static block

认为静态块只能访问静态字段是一种常见的误解。为此,我想展示下面一段我在现实项目中经常使用的代码(部分复制自另一个略有不同的上下文的答案):

public enum Language { 
  ENGLISH("eng", "en", "en_GB", "en_US"),   
  GERMAN("de", "ge"),   
  CROATIAN("hr", "cro"),   
  RUSSIAN("ru"),
  BELGIAN("be",";-)");

  static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
  static { 
    for (Language l:Language.values()) { 
      // ignoring the case by normalizing to uppercase
      ALIAS_MAP.put(l.name().toUpperCase(),l); 
      for (String alias:l.aliases) ALIAS_MAP.put(alias.toUpperCase(),l); 
    } 
  } 

  static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase
    return ALIAS_MAP.containsKey(value.toUpper()); 
  } 

  static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language l = ALIAS_MAP.get(value); 
    if (l == null) throw new IllegalArgumentException("Not an alias: "+value); 
    return l; 
  } 

  private List<String> aliases; 
  private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
  } 
} 

这里,初始化式用于维护一个索引(ALIAS_MAP),将一组别名映射回原始enum类型。它的目的是作为枚举本身提供的内置valueOf方法的扩展。

如您所见,静态初始化器甚至可以访问私有字段别名。重要的是要理解静态块已经可以访问Enum值实例(例如ENGLISH)。这是因为在Enum类型的情况下,初始化和执行的顺序,就像静态私有字段在调用静态块之前已经被实例初始化一样:

Enum常量是隐式静态字段。这需要Enum构造函数和实例块,并且实例初始化也首先发生。 静态块和初始化静态字段的出现顺序。

注意这种乱序初始化(构造函数在静态块之前)非常重要。当我们用类似于Singleton的实例初始化静态字段时(做了简化)也会发生:

public class Foo {
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  static { System.out.println("Static Block 2"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

我们看到的输出如下:

Static Block 1
Constructor
Static Block 2
In Main
Constructor

明确的是静态初始化实际上可以发生在构造函数之前,甚至之后:

简单地在主方法中访问Foo,会导致类被加载并开始静态初始化。但是作为静态初始化的一部分,我们再次调用静态字段的构造函数,之后它恢复静态初始化,并完成从主方法中调用的构造函数。相当复杂的情况,我希望在正常的编码中我们不需要处理。

有关这方面的更多信息,请参阅《Effective Java》一书。

有几个实际的原因,它必须存在:

初始化静态final成员,其初始化可能引发异常 用计算值初始化静态final成员

人们倾向于使用静态{}块作为一种方便的方式来初始化类在运行时所依赖的东西——比如确保特定的类被加载(例如JDBC驱动程序)。这可以通过其他方式来实现;然而,我上面提到的两件事只能用静态{}块这样的构造来完成。