我首先想从你的问题中强调一件事:
构造函数做同样的事情,当一个类第一次初始化时做所有必要的事情
这是不正确的。当创建一个类的 instance 时,构造函数会进行所有必要的初始化。当类本身首次加载到内存并初始化时,不会执行任何构造函数(除非该类的实例恰好是作为类初始化的一部分创建的)。这种混淆(在初始化类和初始化类的实例之间)可能是您质疑 static 块的实用性的原因。
如果类具有需要复杂初始化的静态成员,则可以使用 static 块。假设您需要某种静态地图(此处的目的无关紧要)。您可以像这样内联声明它:
public static final Map<String, String> initials = new HashMap<String, String>();
但是,如果您想填充一次,则不能使用内联声明来执行此操作。为此,您需要一个 static 块:
public static final Map<String, String> initials = new HashMap<String, String>();
static {
initials.put("AEN", "Alfred E. Newman");
// etc.
}
如果您想更有保护性,可以这样做:
public static final Map<String, String> initials;
static {
Map<String, String> map = new HashMap<String, String>()
map.put("AEN", "Alfred E. Newman");
// etc.
initials = Collections.unmodifiableMap(map);
}
请注意,您不能将initials in-line 初始化为不可修改的映射,因为这样您就无法填充它!您也不能在构造函数中执行此操作,因为只需调用其中一种修改方法(put 等)就会产生异常。
公平地说,这不是您问题的完整答案。 static 块仍然可以通过使用私有静态函数来消除:
public static final Map<String, String> initials = makeInitials();
private static Map<String, String> makeInitials() {
Map<String, String> map = new HashMap<String, String>()
map.put("AEN", "Alfred E. Newman");
// etc.
return Collections.unmodifiableMap(map);
}
不过,请注意,这并没有像您建议的那样用构造函数中的代码替换 static 块!此外,如果您需要以相互关联的方式初始化多个 static 字段,这将不起作用。
static 块难以替换的情况是“主”类需要仅初始化几个其他类。
public class Master {
static {
SlaveClass1.init();
SlaveClass2.init(SlaveClass1.someInitializedValue);
// etc.
}
}
特别是如果您不想将任何依赖硬连接到SlaveClass2 上的SlaveClass1,则需要某种类似这样的主代码。这种东西绝对不属于构造函数。
请注意,还有一个称为实例初始化块的东西。它是在创建每个实例时运行的匿名代码块。 (语法就像static 块,但没有static 关键字。)它对匿名类特别有用,因为它们不能有命名构造函数。这是一个真实的例子。由于(深不可测)GZIPOutputStream 没有构造函数或任何可用于指定压缩级别的 api 调用,并且默认压缩级别为无,因此您需要子类化 GZIPOutputStream 以获得任何压缩。你总是可以写一个显式的子类,但是写一个匿名类会更方便:
OutputStream os = . . .;
OutputStream gzos = new GZIPOutputStream(os) {
{
// def is an inherited, protected field that does the actual compression
def = new Deflator(9, true); // maximum compression, no ZLIB header
}
};