public class Test {
int value = 100;
public Test() {
}
}
和
public class Test {
int value;
public Test() {
value = 100;
}
}
相同,对吗?有什么理由我更愿意做一个而不是另一个?显然,如果构造函数接受稍后赋予字段的参数是一个原因:
public class Test {
int value;
public Test(int value) {
this.value = value;
}
}
或许我需要做一些特别的计算。
但如果我不这样做,还有另一个好理由吗?
答案 0 :(得分:4)
这一切都取决于你打算如何使用它。我将假设你不打算让value
静态,但它只是出于内部目的。
首先让我们看看字节码。
D:\eclipse\workspace\asdf\bin>javap -c A.class
Compiled from "A.java"
public class A {
int value;
public A();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100
7: putfield #12 // Field value:I
10: return
}
D:\eclipse\workspace\asdf\bin>javap -c B.class
Compiled from "B.java"
public class B {
int value;
public B();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100
7: putfield #12 // Field value:I
10: return
}
D:\eclipse\workspace\asdf\bin>
猜猜是什么?完全相同的!为什么?因为在使用new
关键字创建对象之前,您无法使用值。
oracle docs表示:
如您所见,您通常可以为字段提供初始值 在其声明中:
public class BedAndBreakfast {
// initialize to 10
public static int capacity = 10;
// initialize to false
private boolean full = false;
}
当初始化值可用并且可以启用初始化时,这很有效 一条线。但是,这种初始化形式有局限性,因为 简单。如果初始化需要一些逻辑(例如, 错误处理或for循环来填充复杂的数组),简单 任务不充分。实例变量可以初始化 构造函数,可以使用错误处理或其他逻辑。至 为类变量提供相同的功能,即Java编程 语言包括静态初始化块。
所以现在您已经确认在构造函数中执行此操作的重点是,如果您正在执行复杂的操作,例如初始化数组,则在声明字段时可以随意执行此操作。
如果您使用static
,那么您显然会做两件事。这几乎就像检查某人是否曾创建过这个对象的实例。您的变量将为0
,直到某人创建了一个对象,之后它将为100
。
答案 1 :(得分:1)
字段初始化代码被复制到每个构造函数中......如果你有多个构造函数并希望在每个构建函数中初始化相同的值(或者甚至只是大多数),那么最好在声明和覆盖时进行初始化构造函数中的值。
答案 2 :(得分:1)
嗯,这取决于。
对于第二种情况,value
将填充0
with its default value,仅在100
实例化时重新分配。在第一种情况下,value
立即被赋予100
的价值。
从语义上讲,这将有助于程序员 - 他们会看到这个特定值意味着某些东西不仅仅是它是任意的(尽管 应该是某个地方的常量值)。
以编程方式,如果将基元设置为某个初始值,则没有痛苦。这意味着你可以使用某些东西,如果你的程序依赖于非负或假值,那么George将会工作。
在处理对象引用时,事情变得更加明确。举个例子,这两个类:
public class Foo {
List<String> elements;
public Foo() {
}
public Foo(String... items) {
elements = new ArrayList<>();
for(String item : items) {
elements.add(item);
}
}
}
public class Bar {
List<String> elements = new ArrayList<>();
public Bar() {
}
public Bar(String... items) {
for(String item : items) {
elements.add(item);
}
}
}
如果我试图使用Foo
,那么有意识地没有arg构造函数来表达这一点 - 对于elements
,如果我不使用elements
,那我会遇到一些麻烦适当的构造函数 - null
是if(elements == null) {
elements = new ArrayList<>();
}
!*我可以然后只需要它就可以实例化它,但我非常希望避免破坏可能新建和填充的列表。
这意味着很多代码看起来像这样:
Bar
...然后我不得不担心它是线程安全的。 Sheesh,谈论一个麻烦。
使用elements
,我保证在实例化时,null
中有一个列表实例,所以我没有担心它是null
。**
这称为渴望实例化。你真的不想没有那个对象,所以为什么要等到你认为你需要它(或 lazily instantiate )?
*:default value for all reference types为{{1}}。
**:你做不得不担心被覆盖,但这个问题超出了这个问题的范围。
答案 3 :(得分:1)
public class Test {
int value = 100;
public Test() {
}
}
当初始化值可用时,这很有效,您可以在一行上声明并初始化字段。然而,这种形式的初始化由于其简单性而具有局限性。如果初始化需要一些逻辑(例如,错误处理或验证或条件),则简单分配是不合适的。使用构造函数初始化时,可能会执行错误处理或其他逻辑。为了为类变量提供相同的功能,Java编程语言包括静态初始化块。还有另外两种方法来初始化实例变量:
初始化程序块
{ //初始化 }
最终方法
class Foo{
int age=initAge();
protected int initAge(){
//initialization code
}
}
答案 4 :(得分:0)
如果您没有进行任何计算或不采用任何参数,那么无论是初始化还是不在构造函数中初始化这些变量,上述任何一个都没有区别。
如果您将它们声明为第一个声明为:
public class Test {
int value = 100;
public Test() {
}
}
它更像是一种可读格式,因为你直接为它们赋值,不需要从构造函数中查看。
如果你有多个构造函数,那么你也不必重复初始化(你不能忘记它们)。
答案 5 :(得分:0)
每当创建一个类时,首先初始化构造函数。因此,当您在构造函数中声明或定义变量时,首先将内存分配给该变量,然后继续该过程。
答案 6 :(得分:0)
目前在您的示例中,只有一个字段,您决定哪种初始化方式比另一种更好。
但是,如果你通过初始化很多字段(例如30或40)来增加复杂性,那么它确实会产生很大的不同。
在这种情况下,请考虑Joshua Bloch has to say on initializing through constructors。
以下是摘要
答案 7 :(得分:0)
我不是在谈论字节码,但它们可以在语义上有所不同(如果你有多个构造函数),
如果您将字段定义如下,则该字段将始终初始化为100,无论调用哪个构造函数:
int field = 100;
但是否则你应该在每个构造函数中初始化字段。
你的类可能只有一个构造函数,但是想想,在你的类的未来版本中是否还有其他构造函数?