使用包括块初始化程序的匿名类初始化静态ArrayList字段

时间:2018-06-28 20:39:14

标签: java arraylist initialization

我正在玩javaDeathmatch游戏,遇到一个我无法回答的问题。你能帮我吗?

$sql = 'INSERT INTO [dbo].[Server] (ServerName, UniqueID) VALUES (@foo, @bar)'
$command.CommandText = $sql
$command.Parameters.AddWithValue('@foo', $stringA[$i])
$command.Parameters.AddWithValue('@bar', $stringB[$i])
$Command.ExecuteReader()

在这种情况下,当我们运行JVM时,将加载该类并首先创建并初始化其静态成员'NAMES'.ArrayList也通过包括块初始化程序的匿名类进行初始化。 但是问题是我们在“ this”引用中添加了“ John”并打印了NAMES,因此显示为空。 如果我们以这种方式进行更改,则代码将正确运行:

public class DeathMatch {
    private static final List<String> NAMES = new ArrayList<>() {{
        add("John");
        System.out.println(NAMES);
    }};

    public static void main(String[] args) {
        //Nothing in particular
    }
}

代替:

System.out.println(this);

为什么会这样?

3 个答案:

答案 0 :(得分:3)

使用“双花括号”初始化时,将创建带有外部花括号的ArrayList匿名子类,而内部的一对花括号代表实例初始化程序,在其中调用add和打印NAMES。但是在构造ArrayList时,ArrayList尚未完成构造,并且尚未分配给NAMES。变量NAMES仍具有其默认值null

虽然语法看起来很漂亮,但是仅仅为了易于初始化,通常不值得创建一个匿名子类。

相反,如果必须静态完成此操作,则将列表内容的初始化移动到静态初始化程序块,以使NAMES在被引用时已被初始化。

private static final List<String> NAMES = new ArrayList<String>();
static {
    NAMES.add("John");
    System.out.println(NAMES);
}

答案 1 :(得分:1)

new ArrayList<>() {{
    add("John");
    System.out.println(NAMES);
}};

本质上可以认为是

new MyList();

其中MyList定义为:

class MyList extends ArrayList<String> {
    public MyList() {
        super();
        add("John");
        System.out.println(NAMES);
    }
}

事件的顺序进行:

  1. 调用构造函数
  2. 将构造函数返回的对象分配给名为“ NAMES”的静态字段

因此,由于System.out.println在构造函数中发生 ,因此用NAMES进行调用意味着分配尚未发生并且不起作用,而是使用{{ 1}}有效。


您应该选择以下一种(取决于Java版本和元素数):

this

后跟一个用于打印的静态块:

private static final List<String> NAMES = Arrays.asList("John");
private static final List<String> NAMES = List.of("John");
private static final List<String> NAMES = Collections.singletonList("John");

答案 2 :(得分:0)

List NAMES尚未完成初始化的原因是,您为null获得了NAMES,但在使用{{时得到了[John] 1}}关键字,如果您在this中进行打印,则会产生所需的输出。