有没有任何一种方法更有优势?
示例1:
class A {
B b = new B();
}
例子2:
class A {
B b;
A() {
b = new B();
}
}
有没有任何一种方法更有优势?
示例1:
class A {
B b = new B();
}
例子2:
class A {
B b;
A() {
b = new B();
}
}
此外还有初始化块,也是由编译器放在构造函数中的:
{
a = new A();
}
请查看Sun的解释和建议
根据这个教程:
然而,字段声明不是任何方法的一部分,因此它们不能像语句那样被执行。相反,Java编译器会自动生成实例字段初始化代码并将其放在类的构造函数中。初始化代码按照出现在源代码中的顺序插入到构造函数中,这意味着一个字段初始化器可以使用在它之前声明的字段的初始值。
此外,您可能希望对字段进行惰性初始化。在初始化字段是一项昂贵操作的情况下,您可以在需要时立即进行初始化:
ExpensiveObject o;
public ExpensiveObject getExpensiveObject() {
if (o == null) {
o = new ExpensiveObject();
}
return o;
}
正如 Bill 指出的那样,为了依赖管理的考虑,在你的类中最好避免使用 new
运算符。而是使用依赖注入 - 即让其他类/框架来实例化和注入你类中的依赖。
另一个选择是使用依赖注入。
class A{
B b;
A(B b) {
this.b = b;
}
}
这样做可以将创建 B
对象的责任从 A
的构造函数中移除。这会使您的代码在长期维护和测试时更易于管理。其思想是降低类 A
和 B
之间的耦合度。其中一个好处是,现在您可以将任何扩展 B
的对象(或者是实现 B
接口的对象)传递给 A
的构造函数,它都可以正常工作。缺点是放弃了对 B
对象的封装性,因此它暴露给了调用 A
构造函数的用户。您必须考虑这种权衡是否值得,但在许多情况下确实是有益的。
A
和B
之间的联系更加明显。以前,使用B
是A
内部问题,如果事实证明不使用B
更好,则更难更改您的建议。 - JaakkoKA
现在需要 B
,我的观点是关于如果这种情况发生变化的。 - JaakkoKList<Integer> intList = new ArrayList<>();
这样简单的东西吗?这完全可以是一个内部实现细节。将ArrayList传递到构造函数中似乎与良好封装的理念背道而驰。 - sprinter今天我以一个有趣的方式被烧伤了:
class MyClass extends FooClass {
String a = null;
public MyClass() {
super(); // Superclass calls init();
}
@Override
protected void init() {
super.init();
if (something)
a = getStringYadaYada();
}
}
注意到错误了吗?事实证明,在调用超类构造函数之后才会调用a = null
初始化程序。由于超类构造函数调用init(),因此a
的初始化在a = null
初始化程序之后发生。
我的个人“规则”(几乎从不违反)是:
因此,我的代码看起来像:
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;
}
}
}
通过这种方式,我始终可以百分之百确定在哪里查找变量声明(在块的开头),以及它们的赋值(在声明后尽快进行,只要有意义)。这样做可能更有效率,因为你不会使用不需要的值来初始化变量(例如声明和初始化变量,然后在需要一半变量具有值之前抛出异常)。同时也不会进行无意义的初始化(比如 int i = 0; 然后稍后,在使用 "i" 之前,将其设置为 i = 5;。
我非常重视一致性,因此我始终遵循这个“规则”,这使与代码的工作变得更加容易,因为你不必四处搜索。
你的情况可能有所不同。
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.
}
}
// ...
}
在其他答案中已经详细解释了,通常优先使用依赖注入或延迟初始化。
当您不想或不能使用这些模式,并且对于原始数据类型,我可以想到三个强有力的理由,说明在构造函数外部初始化类属性的优先级更高:
我认为这几乎只是一个品味问题,只要初始化简单且不需要任何逻辑。
如果您不使用初始化块,则构造函数方法会稍微脆弱一些,因为如果您后来添加了第二个构造函数并忘记在那里初始化b,那么只有在使用最后一个构造函数时才会得到空b。
有关Java中初始化的更多详细信息(以及有关初始化器块和其他不太知名的初始化功能的解释),请参见http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html。
Ctrl-<hover_over_the_variable>-<left_mouse_click>
),无论你在代码的哪个位置。这样,您就可以立即看到该变量的值。否则,您需要“搜索”初始化的位置(通常为构造函数)。这两种方法都是可行的。请注意,在后一种情况下,如果存在另一个构造函数,则b=new B()
可能不会被初始化。将构造函数外的初始化器代码视为公共构造函数,并执行该代码。
我认为示例2更好。最佳实践是在构造函数外声明,在构造函数中初始化。