Java:静态初始化块什么时候有用?


91

static块内初始化之间有什么区别:

public class staticTest {

    static String s;
    static int n;
    static double d;

    static {
        s = "I'm static";
        n = 500;
        d = 4000.0001;
    }
    ...

以及单独的静态初始化:

public class staticTest {

    static String s = "I'm static";
    static int n    = 500;
    static double d = 4000.0001;

    ....

1
您仅在静态初始化块中使用分配,因此当然可以使用静态变量分配来完成分配。您是否尝试过查看是否需要执行非赋值语句?
白金天青

这是加载类或加载本机库的好地方。
qrtt1 2012年

1
请注意,应避免使用静态变量,因此静态初始化块通常不是一个好主意。如果您发现自己经常使用它们,那么可能会遇到麻烦。
Bill K

Answers:


106

静态初始化块允许更复杂的初始化,例如使用条件:

static double a;
static {
    if (SomeCondition) {
      a = 0;
    } else {
      a = 1;
    }
}

或者,当需要的不仅仅是构造时:使用构建器创建实例时,除了创建静态字段之外,还必须进行异常处理或其他工作。

静态初始化块也在内联静态初始化器之后运行,因此以下内容有效:

static double a;
static double b = 1;

static {
    a = b * 4; // Evaluates to 4
}

3
做“ b = a * 4;” 仅当在a之前声明b时,内联才会成为问题,在您的示例中情况并非如此。
乔治·霍金斯

1
@GeorgeHawkins我只是在尝试说明静态初始化程序在内联初始化程序之后运行,而不是等效不能内联完成。但是,我同意你的意思,并已将示例更新为(希望)更加清楚。
Rich O'Kelly 2012年

1
只是为了好玩,我可能会指出,您的第一个示例可能同样容易为“ static double a = someCondition?0:1;”。并不是说您的例子不是很好,我只是说... :)
Bill K

18

典型用法:

private final static Set<String> SET = new HashSet<String>();

static {
    SET.add("value1");
    SET.add("value2");
    SET.add("value3");
}

没有静态初始化程序,您将如何做?


2
答案:番石榴:) +1
保罗·贝洛拉

2
另一个没有附加库的答案:创建一个静态方法来封装的初始化SET并使用变量initializer(private final static Set<String> SET = createValueSet())。如果您有5套地图和2张地图static怎么办?您将所有地图都转储到一个块中怎么办?
TWiStErRob '16

14

您可以在内部使用try / catch块,static{}如下所示:

MyCode{

    static Scanner input = new Scanner(System.in);
    static boolean flag = true;
    static int B = input.nextInt();
    static int H = input.nextInt();

    static{
        try{
            if(B <= 0 || H <= 0){
                flag = false;
                throw new Exception("Breadth and height must be positive");
            }
        }catch(Exception e){
            System.out.println(e);
        }

    }
}

PS:从这里引用!


11

初始化期间的异常处理是另一个原因。例如:

static URL url;
static {
    try {
        url = new URL("https://blahblah.com");
    }
    catch (MalformedURLException mue) {
        //log exception or handle otherwise
    }
}

这对于烦人的抛出检查异常(如上述)的构造函数很有用,或者对于可能更容易发生异常的更复杂的初始化逻辑而言,则很有用。


5

有时,您要做的不仅仅是将值分配给静态变量。由于您不能在类主体中放置任意语句,因此可以使用静态初始化程序块。


4

在您的示例中,没有区别。但是初始值通常比在单个表达式中轻松表达的值更复杂(例如,它的List<String>内容最好用for-loop表示;或者它Method可能不存在,因此需要异常处理程序),和/或静态字段需要以特定顺序设置。


4

static块可用于初始化单例实例,以防止使用同步 getInstance()方法。


3

从技术上讲,没有它您可以逃脱。有些人更喜欢多行初始化代码进入静态方法。使用静态初始化程序进行相对简单的多语句初始化,我感到非常高兴。

当然,我几乎总是使自己的静态final对象指向不可修改的对象。


3

静态关键字(无论是变量还是块)都属于该类。因此,当调用该类时,将执行这些变量或块。因此,大多数初始化将在static关键字的帮助下完成。由于它属于类本身,因此该类可以直接访问它,而无需创建该类的实例。

让我们举个例子,有一个鞋类,其中有多个变量,例如颜色,尺寸,品牌等。这里,如果鞋类制造公司只有一个品牌,那么我们应该将其初始化为静态变量。因此,当调用鞋类并在那时制造不同类型的鞋(通过创建该类的实例)时,每次创建新鞋时颜色和尺寸都会占用内存,但在这里,品牌是所有鞋的共同属性,这样一来,无论制造多少鞋,它都会占据一次内存。

例:

    class Shoe {
    int size;
    String colour;
    static String brand = "Nike";

    public Shoe(int size, String colour) {
        super();
        this.size = size;
        this.colour = colour;
    }

    void displayShoe() {
        System.out.printf("%-2d %-8s %s %n",size,colour, brand);
    }

    public static void main(String args[]) {
        Shoe s1 = new Shoe(7, "Blue");
        Shoe s2 = new Shoe(8, "White");

        System.out.println("=================");
        s1.displayShoe();
        s2.displayShoe();
        System.out.println("=================");
    }
}

1

我们使用构造函数来初始化我们的实例变量(非静态变量,属于对象而不是类的变量)。

如果您要初始化类变量(静态变量)并且想要在不创建对象的情况下进行操作(构造函数只能在创建对象时调用),则需要静态块。

static Scanner input = new Scanner(System.in);
static int widht;
static int height;

static
{
    widht = input.nextInt();
    input.nextLine();
    height = input.nextInt();
    input.close();

    if ((widht < 0) || (height < 0))
    {
        System.out.println("java.lang.Exception: Width and height must be positive");
    }
    else
    {
        System.out.println("widht * height = " + widht * height);
    }
}

在静态初始值设定项中读取stdin是一个非常糟糕的主意。而且System.out.println("B * H");是毫无用处的。答案本身还很模糊。OP没有提到构造函数或实例变量。
shmosel

这只是一个示例,它说明什么是静态初始化程序以及如何使用它。OP没有要求构造函数或实例变量,但是为了教给他静态构造函数与构造函数的区别,他需要知道这一点。否则,他会说“为什么不使用构造函数来初始化我的静态变量?”
迈克尔

0

静态代码块不仅可以使用指令来初始化字段,还可以以不同的声明顺序初始化字段,还可以用于条件初始化。

进一步来说,

static final String ab = a+b;
static final String a = "Hello,";
static final String b = ", world";

将不起作用,因为a和b在ab之后声明。

但是我可以使用静态初始化。克服这一点。

static final String ab;
static final String a;
static final String b;

static {
  b = ", world";
  a = "Hello";
  ab = a + b;
}

static final String ab;
static final String a;
static final String b;

static {
  b = (...) ? ", world" : ", universe";
  a = "Hello";
  ab = a + b;
}

3
尽管您说的是正确的,但它并没有证明必须使用静态初始化程序块。您可以将ab声明移到的声明下方b
gawi '02

0

如果希望在首次使用类之前初始化指定的类静态类型,则静态初始化块很有用。后续使用不会调用任何静态初始化块。它与实例初始化程序的直接相反,后者初始化实例成员。


0

当您想在类加载时评估任何表达式时,可以使用static块,但请记住:

您必须在静态块中处理异常,这意味着您不能从静态块引发异常。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.