Java中静态块的必要性

时间:2012-12-21 17:56:57

标签: java static initialization

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

10 个答案:

答案 0 :(得分:40)

如果某个类具有需要复杂初始化的静态成员,则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等)将生成异常。

公平地说,这不是你问题的完整答案。使用私有静态函数仍然可以消除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);
}

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

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

public class Master {
    static {
        SlaveClass1.init();
        SlaveClass2.init(SlaveClass1.someInitializedValue);
        // etc.
    }
}

特别是如果你不想在SlaveClass2上将任何依赖强硬连接到SlaveClass1,那么需要某种类似的主代码。这种东西绝对不属于构造函数。

请注意,还有一种叫做实例初始化程序块的东西。它是在创建每个实例时运行的匿名代码块。 (语法就像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
    }
};

答案 1 :(得分:17)

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

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

答案 2 :(得分: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");
    }
}

如果我们跑:

MyClass.staticMethod();

输出:

I am static initializer
I am static method

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

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

MyClass x = new MyClass();

输出:

I am static initializer
I am constructor

请注意,如果我们运行:

MyClass x;

输出:(空)

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

答案 3 :(得分:6)

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

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

答案 4 :(得分:5)

你不能用构造函数初始化静态变量 - 或者至少你可能不应该,并且它不会特别有用。

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

答案 5 :(得分:3)

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

答案 6 :(得分:3)

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

答案 7 :(得分:3)

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

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

而构造函数仅初始化该对象的变量。

答案 8 :(得分:2)

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

答案 9 :(得分:1)

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

考虑以下课程

public class Part{

  String name;
  static String producer;

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

  static {
    producer = "Boeing";
  }

}
从此类创建的

对象将生产者设置为Boeing,但其名称根据传递的参数而不同。例如

Part engine = new Part("JetEngine");
Part Wheel = new Part("JetWheel");