为什么使用java实例初始值设定项?

为什么使用java实例初始值设定项?,java,Java,Java中“实例初始化器”的含义是什么? 我们不能把那块代码放在构造函数的开头吗?你确实可以把代码放在每个构造函数的开头。然而,这正是实例初始值设定项的要点:它的代码应用于所有构造函数,如果您有许多构造函数,并且有一些对所有构造函数都通用的代码,这将非常方便 (如果您刚开始编程,您可能不知道可以为同一个类创建多个构造函数(只要它们采用不同的参数);这称为构造函数重载。如果您只有一个构造函数,那么实例初始值设定项确实不是很有用(编辑:除非您以创造性的方式滥用它,如其他答案所示)。您可以在声明匿名类

Java中“实例初始化器”的含义是什么?

我们不能把那块代码放在构造函数的开头吗?

你确实可以把代码放在每个构造函数的开头。然而,这正是实例初始值设定项的要点:它的代码应用于所有构造函数,如果您有许多构造函数,并且有一些对所有构造函数都通用的代码,这将非常方便


(如果您刚开始编程,您可能不知道可以为同一个类创建多个构造函数(只要它们采用不同的参数);这称为构造函数重载。如果您只有一个构造函数,那么实例初始值设定项确实不是很有用(编辑:除非您以创造性的方式滥用它,如其他答案所示)。

您可以在声明匿名类时使用实例初始值设定项,例如,在执行时

List mylist=new ArrayList(){{add(“a”);add(“b”);add(“c”);};

在这里,您可以初始化对象,即使无法向构造函数添加任何内容(因为该类是匿名的)。

我经常使用它们,通常用于在一条语句中创建和填充映射(而不是使用丑陋的静态块):

private static final Map code=new HashMap(){
{
放置(“A”、“Alpha”);
付诸表决(“B”,“B”,“B”);
}
};
一个有趣且有用的修饰是在一条语句中创建一个不可修改的映射:

private static final Map<String, String> CODES = 
    Collections.unmodifiableMap(new HashMap<String, String>() {
    {
        put("A", "Alpha");
        put("B", "Bravo");
    }
});
private静态最终映射代码=
Collections.unmodifiableMap(新的HashMap(){
{
放置(“A”、“Alpha”);
付诸表决(“B”,“B”,“B”);
}
});
比使用静态块和处理单个赋值到final等更整洁

还有一个提示:不要害怕创建简化实例块的方法:

private static final Map<String, String> CODES = new HashMap<String, String>() {
    {
        put("Alpha");
        put("Bravo");
    }

    void put(String code) {
        put(code.substring(0, 1), code);
    }
};
private static final Map code=new HashMap(){
{
看跌期权(“阿尔法”);
付诸表决(“布拉沃”);
}
作废放置(字符串代码){
put(code.substring(0,1),code);
}
};

因为这里的所有代码示例都使用匿名类,所以我将这个(有点可怕的)类组合在一起,演示如何在“适当”的环境中使用实例初始值设定项类。您可以使用它们在初始化时执行复杂处理或处理异常。请注意,这些块是在运行构造函数之前运行的,但在运行子类中的初始值设定项之前运行构造函数:

import java.util.Scanner;

public  class InstanceInitializer {
    int x;
    {
        try {
            System.out.print("Enter a number: ");
            x = Integer.parseInt(new Scanner(System.in).nextLine());
        } catch (NumberFormatException e) {
            x = 0;
        }
    }

    String y;
    {
        System.out.print("Enter a string: ");
        y = new Scanner(System.in).nextLine();
        for(int i = 0; i < 3; i++)
            y += y;
    }

    public InstanceInitializer() {
        System.out.println("The value of x is "+x);
        System.out.println("The value of y is "+y);
    }

    public static class ChildInstanceInitializer extends InstanceInitializer {
        {
            y = "a new value set by the child AFTER construction";
        }
    }

    public static void main(String[] args){
        new InstanceInitializer();
        new InstanceInitializer();
        System.out.println();
        System.out.println(new ChildInstanceInitializer().y);
        // This is essentially the same as:
        System.out.println(new InstanceInitializer(){
            {y = "a new value set by the child AFTER construction";}
        }.y);
    }
}

请注意,“新值”字符串是在父类的构造函数被调用之后才设置的。

如果java没有这个功能,那也没什么大不了的。@unreputable,我很少需要它们,但是匿名类不能有构造函数,但它们可以有实例初始化器,所以我们在语言中需要它。@Kaj字段初始化器可以完成这个任务job@ir声誉良好,仅当您需要为字段赋值时,而不需要调用方法时。此外,即使使用单个构造函数,一起声明和初始化也比在此处声明和在那边初始化更具可读性,风险略低。不过,在这种特殊情况下,我建议使用
java.util.Arrays.asList(T…ts)
相反:-)对于使用实例初始值设定项进行集合填充的用户,请将作为更优雅、更强大的替代方案。该类提供了很好的映射实用程序,并且它们的
不可变*
类对于此处描述的用例特别好,请参见。@Bohemian我很困惑您是如何调用put()的?如果前面没有变量名?@golden如果它是一个匿名类,它是一个(动态的)子类*,那么
put()
是一个实例方法,在
this
上隐式调用它-就像你可以从任何实例方法调用
toString()
而不必编写
this.toString()
。在这个例子中,我添加了一个重载版本的
put()
,它只接受一个参数,并且只在类定义中可见。看起来像是一个只有程序员自己才能阅读的经典代码示例(在他写了一年之后,直到他忘记他到底在想什么……)@事实上,我发现这些更容易阅读,因为它们将对象及其数据绑定到单个构造中。分离的静态初始值设定项块与正在初始化的对象没有明显的关联,实际上可能导致代码在运行时编译但爆炸。不管怎样,这就像大多数编程习惯用法一样;一旦你习惯了它们,它们就会成为你的第二天性。
private static final Map<String, String> CODES = new HashMap<String, String>() {
    {
        put("Alpha");
        put("Bravo");
    }

    void put(String code) {
        put(code.substring(0, 1), code);
    }
};
import java.util.Scanner;

public  class InstanceInitializer {
    int x;
    {
        try {
            System.out.print("Enter a number: ");
            x = Integer.parseInt(new Scanner(System.in).nextLine());
        } catch (NumberFormatException e) {
            x = 0;
        }
    }

    String y;
    {
        System.out.print("Enter a string: ");
        y = new Scanner(System.in).nextLine();
        for(int i = 0; i < 3; i++)
            y += y;
    }

    public InstanceInitializer() {
        System.out.println("The value of x is "+x);
        System.out.println("The value of y is "+y);
    }

    public static class ChildInstanceInitializer extends InstanceInitializer {
        {
            y = "a new value set by the child AFTER construction";
        }
    }

    public static void main(String[] args){
        new InstanceInitializer();
        new InstanceInitializer();
        System.out.println();
        System.out.println(new ChildInstanceInitializer().y);
        // This is essentially the same as:
        System.out.println(new InstanceInitializer(){
            {y = "a new value set by the child AFTER construction";}
        }.y);
    }
}
Enter a number: 1
Enter a string: a
The value of x is 1
The value of y is aaaaaaaa
Enter a number: q
Enter a string: r
The value of x is 0
The value of y is rrrrrrrr

Enter a number: 3
Enter a string: b
The value of x is 3
The value of y is bbbbbbbb
a new value set by the child AFTER construction
Enter a number: s
Enter a string: Hello
The value of x is 0
The value of y is HelloHelloHelloHelloHelloHelloHelloHello 
a new value set by the child AFTER construction