Rom*_*man 252 java static initialization static-block initialization-block
据我所知,"静态初始化块"用于设置静态字段的值,如果不能在一行中完成的话.
但我不明白为什么我们需要一个特殊的块.例如,我们将一个字段声明为静态(没有值赋值).然后编写几行代码,生成并为上面声明的静态字段赋值.
为什么我们需要在一个特殊的块这样的行这样的:static {...}?
Fre*_*old 410
在非静态块:
{
// Do Something...
}
Run Code Online (Sandbox Code Playgroud)
每次构造类的实例时都会调用它.在静态块只被调用一次,当类本身初始化,无论你如何创建该类型的许多对象.
例:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这打印:
Static
Non-static block
Non-static block
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 128
如果它们不在静态初始化块中,它们会在哪里?为了初始化的目的,您如何声明一个仅仅是本地的变量,并将其与字段区分开来?例如,你想怎么写:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果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;
}
}
Run Code Online (Sandbox Code Playgroud)
...但是当您希望在同一个块中分配多个变量时,这不起作用,或者没有(例如,如果您只想记录某些内容 - 或者可能初始化本机库).
Poi*_*nty 100
这是一个例子:
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");
}
Run Code Online (Sandbox Code Playgroud)
"静态"部分中的代码将在类加载时执行,然后构造类的任何实例(并且在从其他地方调用任何静态方法之前).这样您就可以确保类资源都可以使用了.
也可以使用非静态初始化程序块.这些行为类似于为类定义的构造函数方法集的扩展.它们看起来就像静态初始化块一样,除了关闭"static"关键字.
Bal*_*usC 48
当您实际上不想将值分配给任何内容时,它也很有用,例如在运行时只加载一些类.
例如
static {
try {
Class.forName("com.example.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
}
}
Run Code Online (Sandbox Code Playgroud)
嘿,还有另一个好处,你可以用它来处理异常.试想一下,getStuff()在这里抛出Exception它真的属于一个catch块:
private static Object stuff = getStuff(); // Won't compile: unhandled exception.
Run Code Online (Sandbox Code Playgroud)
那么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);
}
}
Run Code Online (Sandbox Code Playgroud)
回到JDBC驱动程序示例,任何体面的JDBC驱动程序本身也会利用static初始化程序在其中注册自己DriverManager.另见这个和这个答案.
use*_*893 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");
}
Run Code Online (Sandbox Code Playgroud)
方法#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;
}
Run Code Online (Sandbox Code Playgroud)
D.S*_*ley 10
存在一些需要存在的实际原因:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
认为静态块只能访问静态字段是一种常见的误解.为此,我想在下面的代码片段中展示我经常在现实生活中使用的代码(在稍微不同的上下文中从另一个答案部分复制):
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这里初始化器用于维护索引(ALIAS_MAP),以将一组别名映射回原始枚举类型.它旨在作为Enum自身提供的内置valueOf方法的扩展.
如您所见,静态初始化程序甚至可以访问该private字段aliases.重要的是要理解该static块已经可以访问Enum值实例(例如ENGLISH).这是因为在类型的情况下初始化和执行Enum的顺序,就像在调用块static private之前已经用实例初始化字段一样static:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我们看到的是以下输出:
Static Block 1
Constructor
Static Block 2
In Main
Constructor
Run Code Online (Sandbox Code Playgroud)
清楚的是,静态初始化实际上可以在构造函数之前发生,甚至在:
只需在main方法中访问Foo,就会导致加载类并启动静态初始化.但是作为静态初始化的一部分,我们再次调用静态字段的构造函数,之后它恢复静态初始化,并完成从main方法中调用的构造函数.相当复杂的情况,我希望在正常编码中我们不必处理.
有关这方面的更多信息,请参阅" Effective Java " 一书.
| 归档时间: |
|
| 查看次数: |
194084 次 |
| 最近记录: |