静态初始化块

时间:2010-03-10 20:37:36

标签: java static initialization static-block initialization-block

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

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

为什么我们需要在一个特殊的块中使用这些行:static {...}

14 个答案:

答案 0 :(得分:395)

非静态块:

{
    // 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

答案 1 :(得分:122)

如果它们不在静态初始化块中,它们会在哪里?为了初始化的目的,您如何声明一个仅仅是本地的变量,并将其与字段区分开来?例如,想要怎么写:

public class Foo {
    private static final int widgets;

    static {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        widgets = first + second;
    }
}

如果firstsecond不在一个区块中,它们看起来像是字段。如果它们位于前面没有static的块中,那么它将被视为实例初始化块而不是静态初始化块,因此它将被执行一次构造的实例而不是总共一次。

现在在这种特殊情况下,您可以使用静态方法:

public class Foo {
    private static final int widgets = getWidgets();

    static int getWidgets() {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        return first + second;
    }
}

...但是当您希望在同一个块中分配多个变量时,这不起作用,或者没有(例如,如果您只想记录某些内容 - 或者可能初始化本机库)。

答案 2 :(得分:97)

以下是一个例子:

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

“静态”部分中的代码将在类加载时执行,然后构造类的任何实例(并且在从其他地方调用任何静态方法之前)。这样你就可以确保类资源都可以使用了。

也可以使用非静态初始化程序块。这些行为类似于为类定义的构造函数方法集的扩展。它们看起来就像静态初始化块一样,除了关闭“静态”关键字。

答案 3 :(得分:47)

当您实际上不想将值分配给任何内容时,它也很有用,例如在运行时只加载一些

E.g。

static {
    try {
        Class.forName("com.example.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
    }
}

嘿,还有另一个好处,你可以用它来处理异常。想象一下,getStuff()在这里抛出一个Exception 真正属于一个catch块:

private static Object stuff = getStuff(); // Won't compile: unhandled exception.

然后static初始化程序在这里很有用。你可以在那里处理例外。

另一个例子是事后做的事情,在分配期间无法完成:

private static Properties config = new Properties();

static {
    try { 
        config.load(Thread.currentThread().getClassLoader().getResourceAsStream("config.properties");
    } catch (IOException e) {
        throw new ExceptionInInitializerError("Cannot load properties file.", e);
    }
}

回到JDBC驱动程序示例,任何体面的JDBC驱动程序本身也会使用static初始化程序在DriverManager中注册自己。另请参阅thisthis回答。

答案 4 :(得分:11)

我会说static block只是语法糖。 static阻止你无法做任何其他事情。

重新使用此处发布的一些示例。

可以在不使用static初始化程序的情况下重写此代码。

方法#1:使用static

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

方法#2:没有static

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;
}

答案 5 :(得分:10)

存在一些必须存在的实际原因:

  1. 初始化初始化可能引发异常的static final成员
  2. 使用计算值初始化static final成员
  3. 人们倾向于使用static {}块作为初始化类在运行时内所依赖的东西的便捷方式 - 例如确保加载特定类(例如,JDBC驱动程序)。这可以通过其他方式完成;但是,我上面提到的两件事只能通过像static {}块这样的构造来完成。

答案 6 :(得分:8)

在静态块中构造对象之前,您可以为类执行一次代码。

E.g。

class A {
  static int var1 = 6;
  static int var2 = 9;
  static int var3;
  static long var4;

  static Date date1;
  static Date date2;

  static {
    date1 = new Date();

    for(int cnt = 0; cnt < var2; cnt++){
      var3 += var1;
    }

    System.out.println("End first static init: " + new Date());
  }
}

答案 7 :(得分:7)

认为静态块只能访问静态字段是一种常见的误解。为此,我想在下面的代码中显示我经常在现实项目中使用的代码(在稍微不同的上下文中部分地从another answer复制):

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方法的扩展。

如您所见,静态初始值设定项甚至可以访问private字段aliases。请务必了解static块已有权访问Enum值实例(例如ENGLISH)。这是因为order of initialization and execution in the case of Enum types,就好像在调用static private块之前已使用实例初始化static字段一样:

  1. Enum常量是隐式静态字段。这需要Enum构造函数和实例块,并且首先进行实例初始化。
  2. static按发生顺序阻止和初始化静态字段。
  3. 这个无序初始化(static块之前的构造函数)非常重要。当我们使用类似于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
    

    清楚的是静态初始化实际上可以在构造函数之前发生,甚至在以下情况之后:

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

    有关详细信息,请参阅“Effective Java”一书。

答案 8 :(得分:3)

如果需要在运行时设置静态变量,那么static {...}块非常有用。

例如,如果需要将静态成员设置为存储在配置文件或数据库中的值。

当您想要向静态Map成员添加值时也很有用,因为您无法在初始成员声明中添加这些值。

答案 9 :(得分:2)

所以你有一个静态字段(它也被称为“类变量”,因为它属于类而不是类的实例;换句话说,它与类相关而不是与任何对象相关联)并且你想要初始化它。因此,如果您不想创建此类的实例并且想要操作此静态字段,则可以通过三种方式执行此操作:

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

static int x = 3;

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

static int x;

static {
 x=3;
}

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
 }

}

答案 10 :(得分:0)

作为补充,像@Pointy说的那样

  

“静态”部分中的代码将在类加载时执行   在构建类的任何实例之前(以及之前)   从其他地方调用任何静态方法。

应该将<style> * { box-sizing: border-box; } .header { border: .5px solid #2f363e; padding: 15px; } .menu2 { width: 14.28%; float: left; padding: 15px; border: .5px solid #2f363e; height: 150px; color: #2f363e; } .main { width: 75%; float: left; padding: 15px; border: .5px solid #2f363e; } </style> <style> /* The Modal (background) */ .modal { display: none; /* Hidden by default */ position: fixed; /* Stay in place */ z-index: 1; /* Sit on top */ padding-top: 100px; /* Location of the box */ left: 0; top: 0; width: 100%; /* Full width */ height: 100%; /* Full height */ overflow: auto; /* Enable scroll if needed */ background-color: rgb(0,0,0); /* Fallback color */ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ } /* Modal Content */ .modal-content { background-color: #fefefe; margin: auto; padding: 20px; border: 1px solid #888; width: 80%; } /* The Close Button */ .close { color: #aaaaaa; float: right; font-size: 28px; font-weight: bold; z-index: 1; } .close:hover, .close:focus { color: #000; text-decoration: none; cursor: pointer; z-index: 1; } </style> 添加到静态块中。

System.loadLibrary("I_am_native_library")

在相关库加载到内存之前,它将保证不会调用本机方法。

根据loadLibrary from oracle

  

如果使用相同的库名称多次调用此方法,   第二次和后续的调用都会被忽略。

非常出乎意料的是,不使用System.loadLibrary来避免多次加载库。

答案 11 :(得分:0)

首先需要了解您的应用程序类本身在运行时被实例化为java.class.Class个对象。这是运行静态块的时候。所以你实际上可以这样做:

public class Main {

    private static int myInt;

    static {
        myInt = 1;
        System.out.println("myInt is 1");
    }

    //  needed only to run this class
    public static void main(String[] args) {
    }

}

它会将“myInt is 1”打印到控制台。请注意,我没有实例化任何类。

答案 12 :(得分:0)

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");
    } 
}

答案 13 :(得分:-1)

静态块用于任何技术以动态方式初始化静态数据成员,或者我们可以说静态数据成员的动态初始化正在使用静态块。因为非静态数据成员初始化我们有构造函数但是我们没有任何我们可以动态初始化静态数据成员的地方

Eg:-class Solution{
         // static int x=10;
           static int x;
       static{
        try{
          x=System.out.println();
          }
         catch(Exception e){}
        }
       }

     class Solution1{
      public static void main(String a[]){
      System.out.println(Solution.x);
        }
        }

现在我的static int x将动态初始化..Bcoz当编译器转到Solution.x时它将在类加载时加载解决方案类和静态块加载。因此我们能够动态初始化该静态数据成员..

}