为什么使用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