Java中静态块的必要性
我发现,在Java中,有一个称为static block
的function,其中包括在首次加载类时执行的代码(我不明白“加载”是什么意思,是否意味着初始化? 是否有任何理由做一个静态块内的初始化位,而不是在构造函数? 我的意思是,即使构造函数也做同样的事情,当一个类首次被初始化时,做所有必要的事情。 有没有什么静态块完成哪个构造函数不能?
如果一个类有静态成员,需要复杂的初始化, 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 { HashMap<String, String> map = new HashMap<String, String>() map.put("AEN", "Alfred E. Newman"); // etc. initials = Collections.unmodifiableMap(map); }
请注意,您无法将initials
初始化为不可修改的地图,因为您无法填充它! 你也不能在构造函数中这样做,因为简单地调用其中一个修改方法( put
等)将会产生一个exception。
公平地说,这不是你的问题的完整答案。 static
块仍然可以通过使用私有静态函数来消除:
public static final Map<String, String> initials = makeInitials(); private static Map<String, String> makeInitials() { HashMap<String, String> map = new HashMap<String, String>() map.put("AEN", "Alfred E. Newman"); // etc. return Collections.unmodifiableMap(map); }
但是,请注意,这不是用你所build议的构造函数中的代码replacestatic
块!
一个static
块会被replace的尴尬情况将是一个“主”类,需要初始化其他几个类一次。
public class Master { static { SlaveClass1.init(); SlaveClass2.init(SlaveClass1.someInitializedValue); // etc. } }
特别是如果你不想在SlaveClass2
上硬连接任何依赖到SlaveClass2
, SlaveClass1
需要这样的主代码。 这种东西绝对不属于构造函数。
请注意,也有一些称为实例初始化程序块 。 它是创build每个实例时运行的匿名代码块。 (语法就像是一个static
块,但没有static
关键字。)对于匿名类来说,它是特别有用的,因为它们不能有命名的构造函数。 这是一个真实世界的例子。 由于GZIPOutputStream
没有一个构造函数或任何可以指定压缩级别的API调用,而且默认的压缩级别是none,所以您需要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 } };
构造函数在创build类的实例时被调用。
当一个类加载器加载这个类定义时,调用静态块,以便我们可以初始化这个类的静态成员。 我们不应该从构造函数初始化静态成员,因为它们是类定义而不是对象的一部分
如果我们初始化一个类,静态初始化器将运行,这不需要我们实例化一个类。 但构造函数仅在我们创build类的实例时才运行。
例如:
class MyClass { static { System.out.println("I am static initializer"); } MyClass() { System.out.println("I am constructor"); } static void staticMethod() { System.out.println("I am static method"); } }
如果我们运行:
MyClass.staticMethod();
输出:
I am static initializer I am static method
我们从来没有创build实例,所以构造函数没有被调用,但是调用了静态初始化器。
如果我们创build一个类的实例,静态initilizer和构造函数都会运行。 没有惊喜。
MyClass x = new MyClass();
输出:
I am static initializer I am constructor
请注意,如果我们运行:
MyClass x;
输出:(空)
声明variablesx
不需要MyClass
被初始化,所以静态初始化器不会运行。
静态初始化器在加载类时运行,即使您从不创build任何该types的对象。
- 并不是所有的类都被实例化。 构造函数可能永远不会被调用。 它甚至可能是私人的。
- 运行构造函数之前,您可能希望访问该类的静态字段。
- 静态初始化程序只在加载类时运行一次。 为您实例化的每种types的对象调用构造函数。
你不能用一个构造函数来初始化静态variables,或者至less你不应该这样做 ,它不会特别有用。
特别是当你试图初始化需要重要逻辑来产生的静态常量时,这真的应该发生在一个静态块中,而不是一个构造函数。
他们是两个不同的东西。 您使用构造函数初始化类的一个实例,静态初始化块将在加载类时初始化静态成员。
静态块与构造函数不同。 基本上有两个不同的概念。
静态块初始化时,类加载到内存中,这意味着当JVM读取u'r字节码。 初始化可以是任何东西,它可以是variables初始化或任何应该由该类的所有对象共享的其他东西
而构造函数只为该对象初始化variables。
当你不得不做一些动作,即使没有创build实例时,静态块也是非常有用的。 例如,用非静态值初始化一个静态variables。
当你想初始化静态字段时,静态块很有用。