实例初始化器与构造函数有何不同?


81

换句话说,为什么需要实例初始化器?与构造函数相比,编写实例初始值设定项有什么区别或优势?


2
实例初始化程序很少见(除非您从热衷于双括号习惯的人那里碰到代码)。
汤姆·哈特芬

Answers:


108

这似乎很好地解释了这一点:

在以下情况下,实例初始化器是实例变量初始化器的有用替代方法:

  • 初始化程序代码必须捕获异常,或者

  • 执行实例变量初始化程序无法表达的计算。当然,您始终可以在构造函数中编写此类代码。

但是在具有多个构造函数的类中,您将不得不在每个构造函数中重复执行代码。使用实例初始化器,您只需编写一次代码,无论使用什么构造函数创建对象,都将执行该代码。实例初始化器在匿名内部类中也很有用,匿名内部类根本不能声明任何构造函数。

来自:JavaWorld Java中的对象初始化


17
另一方面,您可以在一个构造函数中编写一次代码,而只需从所有其他构造函数中调用它即可。但是匿名内部类很有意义。
塔德乌斯·科佩克

11
您可以从其他构造函数调用它-但是您将再次重复该调用。如果添加新的构造函数,则必须记住将调用添加到其中。实例初始化器并非如此。
塔隆克斯

5
@talonx,我同意您关于遗忘的说法,但是利用默认行为同样危险。当支持者阅读旧版代码中的构造函数时,并不总是会记住检查可能的实例初始化程序。而显式使用的init()将脱颖而出。
阿萨巴

1
@ javamonkey79:您是说如果我为类选择了构造函数而不是实例初始化器,那么使用匿名类的唯一实例初始化器是有用的?
realPK 2014年

4
@Assambar您不能在init()方法中分配最终字段,但可以在初始化程序块中。
Timmos 2014年

22

就对象生命周期而言,没有区别。两者都在构造时被调用,从逻辑上讲,初始化程序块可以视为构造的一部分。

从语义上讲,初始化器是一个不错的工具,其原因如下:

通过将初始化逻辑放在要初始化的变量旁边,初始化器可以提高代码的可读性:

   public class Universe {
       public int theAnswer;
       {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;
       }

       // a bunch of other vars
   }

   public class Universe {
       public int theAnswer;

       // a bunch of other vars

       public Universe() {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;

         // other constructor logic
       }
   }

无论使用哪个构造函数,都将调用初始化程序。

初始化程序可以在匿名内部类中使用,而构造函数则不能。


3
从技术上讲,您拥有的是“实例变量初始化程序”而不是“实例初始化程序”(直接嵌套在类中的块)。参见JLS第3节8.6。
汤姆·哈特芬

实例初始化块没有名称所示这里。是不是 您已使用name标记实例初始化程序代码theAnswer。那是对的吗?或者我想念一些东西。
RBT

1
@RBTtheAnswer是一个声明的实例变量。它是在匿名初始化程序块中初始化的。注意变量声明后的分号。
ykaganovich's

9

当您有许多构造函数并希望为每个构造函数执行一些通用代码时,可以使用实例初始化器,因为所有构造函数都将调用它。


4

我通常会避免使用实例初始化程序的习惯用法-它提供的超过变量初始化程序的唯一真正优势是异常处理。

而且,由于init方法(可从构造函数调用)也可以执行异常处理并集中化构造函数设置代码,但具有可以对构造函数参数值进行操作的优点,因此我想说实例初始化器是多余的,因此可以避免。


1
您将必须在所有构造函数中手动调用init方法。
斯蒂芬

2
@Alex很好,但是由于大多数具有多个构造函数的类具有相同的逻辑,因此它们总是会相互调用。至少在我的大多数课堂上。
CPerkins 2013年

3

当我们使用匿名内部类时,可以看到实例构造方法优于构造方法真正优点

匿名内部类不能具有构造函数(因为它们是匿名的) 因此它们非常适合实例初始化器


1

在创建对象时,如果要执行实例变量的初始化,则应使用构造函数,除了初始化活动以外,如果要在创建对象时执行任何活动,则应转到实例块。

我们不能用实例块替换构造函数,因为构造函数可以接受参数,而实例块不能接受参数。

我们不能用构造函数替换实例块,因为一个类可以包含多个构造函数。如果我们想用构造函数替换实例块,那么在每个构造函数中我们都必须编写实例块代码,因为在运行时我们不会期望调用哪个构造函数,这会不必要地增加重复代码。

范例:

class MyClass{

    static int object_count = 0;

    MyClass(){
        object_count++;
    }

    MyClass(int i){

        object_count++;
    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

输出: 2

class MyClass{

    static int object_count = 0;

    {
        object_count++;
    }

    MyClass(){

    }

    MyClass(int i){     

    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

输出: 2


0

初始化程序是在构造函数之间共享代码的方式,如果将初始化程序与变量声明一起使用,它会使代码更具可读性。

Java编译器将初始化程序块复制到每个构造函数中。因此,该方法可用于在多个构造函数之间共享代码块。 Oracle文档

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.