Java中{}和{{}}初始化的区别

3
public class A{
}

A a = new A(){{
    final int x = 1; // IT HAS TO BE FINAL HERE. WHY?
}};

A aa = new A(){
    int x = 1; // THIS NEED NOT BE FINAL. WHY?
    final int y = 1; // STILL FINAL IS ALLOWED HERE. WHY?
    public int getX(){
        return x;
    }
};

有人可以回答片段中提到的问题吗?

谢谢。

5个回答

4

外部一组{}声明了一个匿名子类。

内部一组声明了该子类中的一个初始化块。

通过下面的例子,可以更容易地理解正在发生的事情:

List<String> strings = new ArrayList<String>() {{
    add("string");
    add("another string");
}};

你的意思是:我希望有一个 List 的子类,在初始化时调用方法 add
这与以下情况类似:
List<String> strings = new ArrayList<String>();
strings.add("string");
strings.add("another string");

2
内部 {} 是一个初始化块,但不是 静态 的。如果它是静态的,它将在其前面有关键字 static - 但是你不能在匿名内部类中有一个静态初始化程序。 - Óscar López

3
A a = new A(){{
    final int x = 1; // IT HAS TO BE FINAL HERE. WHY?

不需要。

这两个的区别在于,第一种情况下,您正在编写用于初始化双括号中每个对象的代码。那个 x 是它的局部变量(与类 A 的对象无关)。

在第二种情况下,您正在定义类的主体。 x 将是其成员变量。如果它是 static,则为类变量。如果是 final,则为(基本上是)常量。


有没有一本Java书籍可以详细解释Java语言的细节? - user855
@ajay 是的,它被称为Java语言规范 - Matt Ball

2
我想知道,为什么你想使用第一个版本的代码(带有{{}})。其中声明的变量x根本没有用处,它只在定义它的块作用域中是局部的,并且由于它在匿名类中,您无法在代码中引用它。
而且,无论如何,第一个版本的x声明不需要是final,它可以编译得很好,无论是否使用final。

1
你会使用双括号版本,因为它是一个习语:http://c2.com/cgi/wiki?DoubleBraceInitialization。个人认为这不是一个很好的习惯用法;它掩盖了相当多的开销。 - Karl Knechtel

1

第一个实例A a = new A(){{...在声明中有一个初始化块。另一种写法是这样的:

A a = new A()
{
    {
        final int x = 1; // This is inside an initialisation block
    }        
};

更改格式使其更加明显。


为什么静态初始化块不允许在那里放置 static final int x = 1? - user855
1
第一个实例没有静态初始化块,它有一个实例初始化块。您可以在块中放置引用实例的内容(这就是双括号初始化技巧的工作原理)。 - Nathan Hughes
内部 {} 是一个初始化块,但不是 _静态的_。如果它是静态的,那么它在之前会有关键字 static - 但是你不能在匿名内部类中拥有一个静态初始化器。 - Óscar López
@ajay:那里声明的变量是局部变量。就像你不能在方法内定义静态变量一样,它也不能放在这里。 - Nathan Hughes

1
在您的第一个示例中,内部大括号创建了一个称为实例初始化程序的东西。这是一种初始化变量和在对象构造时调用实例方法的晦涩方式。因此,第一个示例创建了A的匿名子类,并在初始化程序的范围内创建了一个局部变量。它不必是final。
在第二个示例中,您正在创建A的匿名子类,但不创建实例初始化程序,您创建的变量是匿名子类的实例变量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接