static {...}
这样的特殊块内呢?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 int widgets = 0; static{widgets = 2;}
- Weishi Zprivate static int widgets = 0; static{widgets = 2;}
我发现'='赋值按顺序进行,这意味着先放置的'='将首先被分配。上面的例子将为'widgets'赋值为2。(附注:不知道评论只能在5分钟内编辑...) - Weishi Z以下是一个示例:
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”。
当你实际上不想将值分配给任何东西时,例如在运行时仅加载某个类时,它也非常有用。
例如:
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
中注册自己。另请参见此和此答案。
我认为静态块
只是语法糖。你不能使用静态
块做任何其他事情。
以下是一些已发布的示例,可以重复使用此代码。
可以重写此代码段,而不使用静态
初始化程序。
方法#1:使用静态
private static final HashMap<String, String> MAP;
static {
MAP.put("banana", "honey");
MAP.put("peanut butter", "jelly");
MAP.put("rice", "beans");
}
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构造函数和实例块以及实例初始化先发生。
Enum
常量。这也需要Enum构造函数和实例块,以及实例初始化首先发生。static
块按出现顺序进行静态字段初始化。static
块之前)很重要。当我们像单例模式一样使用实例来初始化静态字段时,也会发生这种情况(做了简化):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
aliases
的访问权限并不意味着静态块可以访问非静态成员。aliases
是通过values()
方法返回的Language
值来访问的。
正如您所提到的,枚举变量在此时已经可用是不寻常的 - 普通类的非静态成员在这种情况下是不可访问的。 - Ignaziostatic final Foo Inst1;
static final Foo Inst2;
static{
Inst1 = new Foo("Inst1");
Inst2 = new Foo("Inst2");
}
static {
System.out.println("Inst1: " + Inst1.member);
System.out.println("Inst2: " + Inst2.member);
}
private final String member;
private Foo(String member){
this.member = member;
}
}上述代码与枚举示例没有区别,仍允许在静态块中访问实例变量。 - Swami PR如果你有一个静态字段(也称为“类变量”,因为它属于类而不是类的实例;换句话说,它与类相关联而不是与任何对象相关联),并且你想要初始化它。所以如果你不想创建这个类的实例并且想操作这个静态字段,有三种方法:
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
}
}
static {...}
块非常有用。例如,如果您需要将静态成员设置为存储在配置文件或数据库中的值。当您想要向静态Map
成员添加值时也很有用,因为您不能在初始成员声明中添加这些值。
{...}
与static {...}
之间的区别(在这种情况下,Jon Skeet 的回答肯定更好)。 - David T.