静态初始化块

据我所知,“静态初始化块”用于设置静态字段的值,如果不能在一行中完成的话。 但我不明白为什么我们需要一个特殊的块。例如,我们将一个字段声明为静态(没有值赋值)。然后编写几行代码,生成并为上面声明的静态字段赋值。 为什么我们需要在一个特殊的块中使用这些行:
static {...}
?     
已邀请:
非静态块:
{
    // 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 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;
    }
}
如果
first
second
不在一个区块中,它们看起来就像是田地。如果它们位于前面没有
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;
    }
}
...但是当您希望在同一个块中分配多个变量时,这不起作用,或者没有(例如,如果您只想记录某些内容 - 或者可能初始化本机库)。     
这是一个例子:
  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");
  }
“静态”部分中的代码将在类加载时执行,然后构造类的任何实例(并且在从其他地方调用任何静态方法之前)。这样您就可以确保类资源都可以使用了。 也可以使用非静态初始化程序块。这些行为类似于为类定义的构造函数方法的扩展。它们看起来就像静态初始化块一样,除了关闭“static”关键字。     
当您实际上不希望将值赋给任何内容时,它也很有用,例如在运行时仅加载一些类。 例如。
static {
    try {
        Class.forName("com.example.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
    }
}
嘿,还有另一个好处,你可以用它来处理异常。想象一下,
getStuff()
在这里抛出一个真正属于catch块的
Exception
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
中注册自己。另见这个和这个答案。     
我会说
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;
}
    
存在一些必须存在的实际原因: 初始化初始化可能引发异常的
static final
成员 使用计算值初始化
static final
成员 人们倾向于使用
static {}
块作为初始化类在运行时内所依赖的东西的便捷方式 - 例如确保加载特定类(例如,JDBC驱动程序)。这可以通过其他方式完成;但是,我上面提到的两件事只能通过像
static {}
块这样的结构来完成。     
在静态块中构造对象之前,您可以为类执行一次代码。 例如。
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());
  }
}
    
认为静态块只能访问静态字段是一种常见的误解。为此,我想在下面的代码中显示我经常在现实项目中使用的代码(在稍微不同的上下文中从另一个代码部分复制):
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
)。这是因为在
Enum
类型的情况下初始化和执行的顺序,就像在调用
static
块之前已经用实例初始化了
static private
字段:
Enum
常量是隐式静态字段。这需要Enum构造函数和实例块,并且首先也要进行实例初始化。
static
按发生顺序阻止和初始化静态字段。 这个无序初始化(
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方法中调用的构造函数。相当复杂的情况,我希望在正常编码中我们不必处理。 有关这方面的更多信息,请参阅“有效Java”一书。     
如果需要在运行时设置静态变量,则
static {...}
块非常有用。 例如,如果需要将静态成员设置为存储在配置文件或数据库中的值。 当您想要向静态
Map
成员添加值时也很有用,因为您无法在初始成员声明中添加这些值。     
所以你有一个静态字段(它也称为“类变量”,因为它属于类而不是类的实例;换句话说,它与类相关而不是与任何对象相关联)并且您想要初始化它。因此,如果您不想创建此类的实例并且想要操作此静态字段,则可以通过三种方式执行此操作: 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
 }

}
    
作为补充,像@Pointy说   “静态”部分中的代码将在类加载时执行   在构建类的任何实例之前(以及之前)   从其他地方调用任何静态方法)。 它应该将
System.loadLibrary("I_am_native_library")
添加到静态块中。
static{
    System.loadLibrary("I_am_a_library");
}
它将保证在将相关库加载到内存之前不会调用本机方法。 根据oracle的loadLibrary:   如果使用相同的库名称多次调用此方法,   第二次和后续调用将被忽略。 所以非常出乎意料的是,放置System.loadLibrary并不是为了避免多次加载库。     
首先需要了解您的应用程序类本身在运行时被实例化为
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”到控制台。请注意,我没有实例化任何类。     
static block用于任何技术以动态方式初始化静态数据成员,或者我们可以说静态数据成员的动态初始化正在使用静态块。因为非静态数据成员初始化我们有构造函数但我们没有我们可以动态初始化静态数据成员的任何地方
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时它将在类加载时加载解决方案类和静态块加载。因此我们能够动态初始化该静态数据成员.. }     

要回复问题请先登录注册