最近,我遇到了一个我从未见过的Java构造,我想知道是否应该使用它。它似乎被称为初始化块。
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}代码块将被复制到每个构造函数中,也就是说,如果您有多个构造函数,则不必重写代码。
但是,我看到了使用这种语法的三个主要缺点:
如果我的上述陈述是正确的,为什么(以及何时)引入这个语言结构?是否有任何合法的用例?
发布于 2014-05-12 15:27:40
有两种情况下,我使用初始化块。
第一个是初始化最终成员。在Java中,您可以根据声明初始化最终成员,也可以在构造函数中初始化它。在方法中,禁止向最终成员分配。
这是有效的:
final int val = 2;这也是有效的:
final int val;
MyClass() {
val = 2;
}这是无效的:
final int val;
MyClass() {
init();
}
void init() {
val = 2; // cannot assign to 'final' field in a method
}如果有多个构造函数,并且不能内联地初始化最终成员(因为初始化逻辑太复杂),或者如果构造函数无法调用自己,则可以复制/粘贴初始化代码,也可以使用初始化程序块。
final int val;
final int squareVal;
MyClass(int v, String s) {
this.val = v;
this.s = s;
}
MyClass(Point p, long id) {
this.val = p.x;
this.id = id;
}
{
squareVal = val * val;
}初始化程序块的另一个用例是构建小助手数据结构。我声明一个成员,并在其声明之后将值放在它自己的初始化器块中。
private Map<String, String> days = new HashMap<String, String>();
{
days.put("mon", "monday");
days.put("tue", "tuesday");
days.put("wed", "wednesday");
days.put("thu", "thursday");
days.put("fri", "friday");
days.put("sat", "saturday");
days.put("sun", "sunday");
}发布于 2016-06-24 19:15:56
通常,不要使用非静态初始化块(也可能避免静态初始化块)。
看看这个问题,有3个答案,但你用这个语法愚弄了4个人。我是他们中的一员,我写Java已经16年了!显然,语法可能容易出错!我会远离它的。
对于非常简单的内容,您可以使用“伸缩”构造器来避免这种混淆:
public class Test {
private String something;
// Default constructor does some things
public Test() { doStuff(); }
// Other constructors call the default constructor
public Test(String s) {
this(); // Call default constructor
something = s;
}
}如果您需要在每个构造函数或其他复杂的初始化结束时使用doStuff(),那么最好使用构建器模式。乔希·布洛赫列出了为什么构建器是一个好主意的几个原因。建设者花点时间写,但写得恰到好处,他们都是喜悦用的。
public class Test {
// Value can be final (immutable)
private final String something;
// Private constructor.
private Test(String s) { something = s; }
// Static method to get a builder
public static Builder builder() { return new Builder(); }
// builder class accumulates values until a valid Test object can be created.
private static class Builder {
private String tempSomething;
public Builder something(String s) {
tempSomething = s;
return this;
}
// This is our factory method for a Test class.
public Test build() {
Test t = new Test(tempSomething);
// Here we do your extra initialization after the
// Test class has been created.
doStuff();
// Return a valid, potentially immutable Test object.
return t;
}
}
}
// Now you can call:
Test t = Test.builder()
.setString("Utini!")
.build();我过去经常使用静态初始化器,但偶尔会遇到循环,其中两个类依赖于彼此调用的静态初始化块,然后才能完全加载该类。这会产生“未能加载类”或类似的模糊错误消息。为了找出问题所在,我不得不将文件与源代码管理中上一个已知的工作版本进行比较。一点都不好玩。
也许静态初始化器在工作时对性能有好处,而且不会太混乱。但是总的来说,现在我更喜欢延迟初始化而不是静态初始化器。很清楚它们是干什么的,我还没有遇到类加载错误,而且它们比初始化程序块在更多的初始化情况下工作。
与构建数据结构的静态初始化不同(与其他答案中的示例相比),我现在使用Paguro的不可变数据定义辅助函数:
private ImMap<String,String> days =
map(tup("mon", "monday"),
tup("tue", "tuesday"),
tup("wed", "wednesday"),
tup("thu", "thursday"),
tup("fri", "friday"),
tup("sat", "saturday"),
tup("sun", "sunday"));在Java开始时,初始化程序块是完成某些事情的唯一方法,但是现在它们很混乱,容易出错,并且在大多数情况下已经被更好的替代物所取代(上面详细介绍了)。了解初始化程序块是很有趣的,以防您在遗留代码中看到它们,或者它们出现在测试中,但是如果我正在进行代码评审,并且在新代码中看到了一个,我会要求您在对代码竖起大拇指之前,说明为什么上述任何选项都是合适的。
发布于 2014-05-12 15:44:27
除了声明为final的实例变量的初始化(请参阅barjak的回答)外,我还要提到static初始化块。
你可以用它们作为一种“静态结构”。
这样,您就可以在第一次引用类时对静态变量进行复杂的初始化。
下面是一个由barjak的例子启发而来的例子:
public class dayHelper(){
private static Map<String, String> days = new HashMap<String, String>();
static {
days.put("mon", "monday");
days.put("tue", "tuesday");
days.put("wed", "wednesday");
days.put("thu", "thursday");
days.put("fri", "friday");
days.put("sat", "saturday");
days.put("sun", "sunday");
}
public static String getLongName(String shortName){
return days.get(shortName);
}
}https://softwareengineering.stackexchange.com/questions/238820
复制相似问题