Don*_*onX 217 java constructor instance-variables
两种方法都有任何优势吗?
例1:
class A {
B b = new B();
}
Run Code Online (Sandbox Code Playgroud)
例2:
class A {
B b;
A() {
b = new B();
}
}
Run Code Online (Sandbox Code Playgroud)
Boz*_*zho 261
还有初始化块,它也由编译器放在构造函数中:
{
a = new A();
}
Run Code Online (Sandbox Code Playgroud)从本教程:
但是,字段声明不是任何方法的一部分,因此它们不能像语句那样执行.相反,Java编译器会自动生成实例字段初始化代码,并将其放在类的构造函数或构造函数中.初始化代码按照它在源代码中出现的顺序插入到构造函数中,这意味着字段初始值设定项可以使用在它之前声明的字段的初始值.
此外,您可能希望懒洋洋地初始化您的字段.如果初始化字段是一项昂贵的操作,您可以在需要时立即对其进行初始化:
ExpensiveObject o;
public ExpensiveObject getExpensiveObject() {
if (o == null) {
o = new ExpensiveObject();
}
return o;
}
Run Code Online (Sandbox Code Playgroud)
最终(正如Bill所指出的),为了依赖管理,最好避免new
在你班级的任何地方使用操作员.相反,使用依赖注入是更可取的 - 即让其他人(另一个类/框架)实例化并在您的类中注入依赖项.
Bil*_*ard 36
另一种选择是使用依赖注入.
class A{
B b;
A(B b) {
this.b = b;
}
}
Run Code Online (Sandbox Code Playgroud)
这消除了B
从构造函数创建对象的责任A
.从长远来看,这将使您的代码更易于测试并且更易于维护.这样做是为了减少两个类之间的耦合A
和B
.这给您带来的好处是,您现在可以将任何扩展B
(或实现,B
如果它是一个接口)的对象传递给A
构造函数,它将起作用.一个缺点是你放弃了B
对象的封装,所以它暴露给A
构造函数的调用者.你必须考虑这些利益是否值得这种权衡,但在很多情况下它们都是.
Edw*_*alk 20
今天我以一种有趣的方式被烧了:
class MyClass extends FooClass {
String a = null;
public MyClass() {
super(); // Superclass calls init();
}
@Override
protected void init() {
super.init();
if (something)
a = getStringYadaYada();
}
}
Run Code Online (Sandbox Code Playgroud)
看到错误?事实证明,在a = null
调用超类构造函数之后调用初始化程序.由于超类构造函数调用init()的初始化a
是遵循由a = null
初始化.
Tof*_*eer 14
我个人的"规则"(几乎没有破坏)是:
所以我会有如下代码:
public class X
{
public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
private static final int A;
private final int b;
private int c;
static
{
A = 42;
}
{
b = 7;
}
public X(final int val)
{
c = val;
}
public void foo(final boolean f)
{
final int d;
final int e;
d = 7;
// I will eat my own eyes before using ?: - personal taste.
if(f)
{
e = 1;
}
else
{
e = 2;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这样我总是100%确定在哪里寻找变量声明(在块的开头),以及它们的赋值(在声明之后一旦有意义).由于您永远不会使用未使用的值初始化变量(例如声明和init变量,然后在需要具有值的那些变量的一半之前抛出异常),因此这可能会更有效.你也不会做无意义的初始化(比如int i = 0;然后在之后,在使用"i"之前,做i = 5;.
我非常重视一致性,所以遵循这个"规则"是我一直在做的事情,这使得使用代码变得更加容易,因为你不必去找东西.
你的旅费可能会改变.
例2的灵活性较差.如果添加另一个构造函数,则需要记住在该构造函数中实例化该字段.只是直接实例化该字段,或在getter中的某处引入延迟加载.
如果实例化需要的不仅仅是简单new
,请使用初始化块.无论使用何种构造函数,都将运行此命令.例如
public class A {
private Properties properties;
{
try {
properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
} catch (IOException e) {
throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
}
}
// ...
}
Run Code Online (Sandbox Code Playgroud)