56

我发现在Java中,有一个特性叫做static block,它包含了在类第一次加载时执行的代码(我不明白“加载”是什么意思,是不是已经初始化了?)。是否有任何理由在静态块内而不是在构造函数中进行初始化位?我的意思是,即使是构造函数也做同样的事情,在类第一次初始化时做所有必要的事情。静态块有什么构造函数不能完成的吗?

4

11 回答 11

49

我首先想从您的问题中强调一件事:

构造函数做同样的事情,当一个类第一次初始化时做所有必要的事情

这是不正确的。创建类的实例时,构造函数会执行所有必要的初始化。当类本身首次加载到内存并初始化时,不会执行任何构造函数(除非该类的实例恰好是作为类初始化的一部分创建的)。这种混淆(在初始化类和初始化类的实例之间)可能是您质疑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);
}

请注意,您不能将initialsin-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.
    }
}

特别是如果您不想将任何依赖硬连接到SlaveClass2on 中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
    }
};
于 2012-12-21T18:05:36.067 回答
18

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

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

于 2012-12-21T17:58:52.407 回答
9

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

例如:

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

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

如果我们创建一个类的实例,静态初始化程序和构造函数都会运行。没有惊喜。

MyClass x = new MyClass();

输出:

I am static initializer
I am constructor

请注意,如果我们运行:

MyClass x;

输出:(空)

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

于 2012-12-21T19:39:56.370 回答
7

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

  • 并非所有类都需要实例化。构造函数可能永远不会被调用。它甚至可能是私人的。
  • 您可能希望在运行构造函数之前访问类的静态字段。
  • 静态初始化程序仅在加载类时运行一次。为您实例化的该类型的每个对象调用构造函数。
于 2012-12-21T17:58:41.680 回答
5

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

尤其是当您尝试初始化需要大量逻辑来生成的静态常量时,这确实应该发生在静态块中,而不是构造函数中。

于 2012-12-21T17:58:32.873 回答
3

它们是两个不同的东西。您使用构造函数来初始化类的一个实例,静态初始化块在加载类时初始化静态成员。

于 2012-12-21T17:58:41.813 回答
3

当您必须执行一些操作时,即使仍然没有创建任何实例,静态块也很有用。例如,用于初始化具有非静态值的静态变量。

于 2012-12-21T17:59:45.977 回答
3

静态块与构造函数做不同的事情。基本上有两个不同的概念。

静态块在类加载到内存时初始化,这意味着当 JVM 读取你的字节码时。初始化可以是任何东西,它可以是变量初始化或任何其他应该由该类的所有对象共享的东西

而构造函数只为该对象初始化变量。

于 2012-12-21T18:03:00.990 回答
2

当您要初始化静态字段时,静态块很有用。

于 2012-12-21T18:39:01.677 回答
1

当您必须执行一些操作时,静态块对构造函数很有用,即使仍然没有创建实例。例如,用于初始化具有非静态值的静态变量。

于 2018-02-25T13:21:40.823 回答
1

您可以理解静态块的一种方法是;它充当构造函数。但是,两者的区别在于 静态块实例化类或静态变量,而构造函数用于实例化对象变量

考虑下面的类

public class Part{

  String name;
  static String producer;

  public Part(String name){
      this.name = name;
  }

  static {
    producer = "Boeing";
  }

}

从此类创建的对象将生产者设置为波音,但它们的名称因传递的参数而异。例如

Part engine = new Part("JetEngine");
Part Wheel = new Part("JetWheel");
于 2018-08-05T21:31:55.223 回答