Java中静态块的必要性

Sex*_*ast 48 java static initialization

我发现在Java中,有一个叫做的功能static block,它包括在第一次加载类时执行的代码(我不明白'加载'意味着什么,它是否意味着初始化?).是否有任何理由在静态块内而不是在构造函数中执行初始化位?我的意思是,即使构造函数做同样的事情,在第一次初始化类时也要做所有必要的事情.什么是静态块完成哪些构造函数不能?

Ted*_*opp 42

如果类具有需要复杂初始化的静态成员,则static块是要使用的工具.假设您需要某种静态地图(目的与此无关).您可以像这样在线声明它:

public static final Map<String, String> initials = new HashMap<String, String>();
Run Code Online (Sandbox Code Playgroud)

但是,如果要填充一次,则无法使用内联声明进行填充.为此,你需要一个static块:

public static final Map<String, String> initials = new HashMap<String, String>();
static {
    initials.put("AEN", "Alfred E. Newman");
    // etc.
}
Run Code Online (Sandbox Code Playgroud)

如果你想要更加保护,你可以这样做:

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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,您无法将内嵌初始化initials为不可修改的地图,因为您无法填充它!您也无法在构造函数中执行此操作,因为只调用其中一个修改方法(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);
}
Run Code Online (Sandbox Code Playgroud)

但请注意,这并不是static像你提议的那样用构造函数中的代码替换块!

一个static块难以替换的情况将是一个"主"类,需要初始化几个其他类一次.

public class Master {
    static {
        SlaveClass1.init();
        SlaveClass2.init(SlaveClass1.someInitializedValue);
        // etc.
    }
}
Run Code Online (Sandbox Code Playgroud)

特别是如果你不想硬线的任何依赖到staticSlaveClass2,需要某种形式的主代码这样.这种东西绝对不属于构造函数.

请注意,还有一个称为实例初始化块的东西.它是在创建每个实例时运行的匿名代码块.(语法就像一个SlaveClass1块,但没有static关键字.)它对匿名类特别有用,因为它们不能有命名构造函数.这是一个真实的例子.由于(不可思议)static没有构造函数或任何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
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 静态初始化程序的另一个典型用法是为具有本机方法的类加载本机库,如下所示:`static {System.loadLibrary("foo"); }` (2认同)
  • @Cupidvogel - 由于变量被声明为"static"和"final"(因此无法更改),语言将不允许您在构造函数中执行此操作. (2认同)

ssk*_*ssk 17

在创建类的实例时调用构造函数.

当类加载器加载此类定义时,将调用静态块,以便我们可以初始化此类的静态成员.我们不应该从构造函数初始化静态成员,因为它们是类定义而不是对象的一部分


Aka*_*all 8

如果我们初始化一个类,静态初始化程序将运行,这不需要我们实例化一个类.但是只有在我们创建类的实例时才运行构造函数.

例如:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们运行:

MyClass.staticMethod();
Run Code Online (Sandbox Code Playgroud)

输出:

I am static initializer
I am static method
Run Code Online (Sandbox Code Playgroud)

我们从未创建过实例,因此不会调用构造函数,而是调用静态初始化程序.

如果我们创建一个类的实例,则静态initilizer和构造函数都会运行.没有惊喜.

MyClass x = new MyClass();
Run Code Online (Sandbox Code Playgroud)

输出:

I am static initializer
I am constructor
Run Code Online (Sandbox Code Playgroud)

请注意,如果我们运行:

MyClass x;
Run Code Online (Sandbox Code Playgroud)

输出:(空)

声明变量x不需要MyClass初始化,因此静态初始化程序不会运行.

  • 你误解了正在发生的事情。静态块_are_在类初始化时运行。只是简单地声明一个变量`MyClass x` 不需要加载或初始化该类。Java 足够聪明,知道它可以推迟加载和初始化类,直到真正需要有关它的某些信息(除了它的名称)。(顺便说一句,简单地引用类的静态字段也会导致类被初始化。您不需要创建实例或调用方法。) (2认同)

Mar*_*ers 6

即使您从未创建该类型的任何对象,静态初始化程序也会在加载类时运行.

  • 并非所有类都要实例化.可能永远不会调用构造函数.它甚至可能是私人的.
  • 您可能希望在运行构造函数之前访问该类的静态字段.
  • 静态初始化程序仅在加载类时运行一次.为您实例化的该类型的每个对象调用构造函数.


Lou*_*man 5

您不能使用构造函数初始化静态变量 - 或者至少您可能不应该这样做,并且它不会特别有用.

特别是当您尝试初始化需要生成重要逻辑的静态常量时,实际上应该在静态块中发生,而不是构造函数.