Java 默认构造函数与内联字段初始化
默认构造函数和直接初始化对象字段有什么区别 有什么理由选择下面的一个例子而不是另一个 例1 例2Java 默认构造函数与内联字段初始化,java,oop,constructor,Java,Oop,Constructor,默认构造函数和直接初始化对象字段有什么区别 有什么理由选择下面的一个例子而不是另一个 例1 例2 选择示例一的原因是,它的功能相同,代码更少(这总是好的) 除此之外,没有区别 但是,如果您确实有显式构造函数,我更愿意将所有初始化代码放入其中(并链接它们),而不是在构造函数和字段初始值设定项之间进行拆分。初始值设定项在构造函数体之前执行。(这意味着,如果同时具有初始化器和构造函数,则构造函数代码将执行第二个并重写初始化值) 当您总是需要相同的初始值(如在您的示例中,给定大小的数组或特定值的整数)时
选择示例一的原因是,它的功能相同,代码更少(这总是好的) 除此之外,没有区别
但是,如果您确实有显式构造函数,我更愿意将所有初始化代码放入其中(并链接它们),而不是在构造函数和字段初始值设定项之间进行拆分。初始值设定项在构造函数体之前执行。(这意味着,如果同时具有初始化器和构造函数,则构造函数代码将执行第二个并重写初始化值) 当您总是需要相同的初始值(如在您的示例中,给定大小的数组或特定值的整数)时,初始化器很好,但它可以对您有利或不利: 如果有许多构造函数以不同的方式初始化变量(即使用不同的值),那么初始化器是无用的,因为更改将被覆盖,而且是浪费 另一方面,如果有许多构造函数使用相同的值初始化,则可以通过将初始化保留在一个位置来保存代码行(并使代码更易于维护)
正如Michael所说,这也涉及到品味问题——您可能希望将代码保存在一个地方。尽管如果你有很多构造函数,你的代码在任何情况下都不在一个地方,所以我倾向于使用初始化器。我能想到的唯一区别是如果你要添加另一个构造函数
public Foo(int inX){
x = inX;
}
在第一个示例中,您将不再拥有默认构造函数,而在第二个示例中,您仍然拥有默认构造函数(如果需要,甚至可以从新构造函数内部调用它)当有复杂的初始化逻辑要执行时,我更喜欢字段初始值设定项,并求助于默认构造函数(例如,填充一个映射,一个ivar通过一系列启发式步骤依赖另一个ivar来执行,等等) @迈克尔B说: 。。。我更愿意将所有初始化代码放入这些代码中(并链接它们),而不是在构造函数和字段初始值设定项之间进行拆分
MichaelB(我向71+K代表鞠躬)说得很有道理,但我的倾向是将简单的初始化保留在内联最终初始化器中,并在构造函数中完成复杂的初始化部分。我们应该支持字段初始化器还是构造函数为字段提供默认值?
我不会考虑在现场实例化和字段懒惰/急于实例化中出现的异常,这些问题涉及到其他的关注点,而不是可读性和可维护性问题。 对于执行相同逻辑并产生相同结果的两个代码,应采用可读性和可维护性最好的方式
TL;DR- 选择第一个或第二个选项首先是代码组织、可读性和可维护性的问题
- 在选择方式上保持一致性(它使整个应用程序代码更清晰)
- 请毫不犹豫地使用字段初始值设定项来实例化
字段,以防止集合
NullPointerException
- 对于可能被构造函数覆盖的字段,不要使用字段初始值设定项
- 在具有单个构造函数的类中,字段初始值设定项方式通常更具可读性,也不太冗长
- 在具有多个构造函数的类中,如果构造函数之间没有耦合或耦合很少,则字段初始值设定项方法通常更具可读性且不太冗长
- 在具有多个构造函数的类中,构造函数之间存在耦合,这两种方法都不是更好的方法,但无论选择何种方法,将其与链接构造函数相结合都是最好的方法(请参见用例1)
操作问题 通过一个非常简单的代码,在字段声明期间的赋值似乎更好,事实也确实如此。
这不太冗长,更直截了当:
public class Foo {
private int x = 5;
private String[] y = new String[10];
}
而不是构造函数的方式:
public class Foo{
private int x;
private String[] y;
public Foo(){
x = 5;
y = new String[10];
}
}
在具有如此真实的特殊性的真实课堂上,情况就不同了事实上,根据遇到的具体情况,一种方式,另一方或其中任何一方都应该受到青睐
更详细的示例来说明
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat = 5;
private Color color = Color.black;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat) {
this.nbSeat = nbSeat;
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
this.name = "Super car";
this.origin = "Mars";
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
// assignment at a single place
this.nbSeat = nbSeat;
this.color = color;
// validation rules at a single place
...
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
initDefaultValues();
}
public Car(int nbSeat, Color color) {
initDefaultValues();
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
initDefaultValues();
this.replacingCar = replacingCar;
// specific validation rules
}
private void initDefaultValues() {
name = "Super car";
origin = "Mars";
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
this.replacingCar = replacingCar;
// specific validation rules
}
}
研究案例1
我将从一个简单的Car
类开始,我将更新该类来说明这些要点。Car
声明4个字段以及它们之间存在关系的一些构造函数
1.在字段初始化器中为所有字段指定默认值是不可取的
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat = 5;
private Color color = Color.black;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat) {
this.nbSeat = nbSeat;
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
this.name = "Super car";
this.origin = "Mars";
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
// assignment at a single place
this.nbSeat = nbSeat;
this.color = color;
// validation rules at a single place
...
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
initDefaultValues();
}
public Car(int nbSeat, Color color) {
initDefaultValues();
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
initDefaultValues();
this.replacingCar = replacingCar;
// specific validation rules
}
private void initDefaultValues() {
name = "Super car";
origin = "Mars";
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
this.replacingCar = replacingCar;
// specific validation rules
}
}
字段声明中指定的默认值并不都可靠。
只有name
和origin
字段具有真正的默认值。nbSeat
和color
字段首先在其声明中赋值,然后这些字段可能会在带有参数的构造函数中被覆盖。它容易出错,此外,使用这种字段赋值方法,该类会降低其可靠性级别。怎么可能 依赖于在字段声明期间指定的任何默认值,而事实证明它对于两个字段都不可靠?
2.使用构造函数对所有字段进行赋值并依赖构造函数链接是很好的
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat = 5;
private Color color = Color.black;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat) {
this.nbSeat = nbSeat;
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
this.name = "Super car";
this.origin = "Mars";
this.nbSeat = nbSeat;
this.color = color;
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
...
...
// Other fields
...
public Car() {
this(5, Color.black);
}
public Car(int nbSeat) {
this(nbSeat, Color.black);
}
public Car(int nbSeat, Color color) {
// assignment at a single place
this.nbSeat = nbSeat;
this.color = color;
// validation rules at a single place
...
}
}
public class Car {
private String name;
private String origin;
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
initDefaultValues();
}
public Car(int nbSeat, Color color) {
initDefaultValues();
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
initDefaultValues();
this.replacingCar = replacingCar;
// specific validation rules
}
private void initDefaultValues() {
name = "Super car";
origin = "Mars";
}
}
public class Car {
private String name = "Super car";
private String origin = "Mars";
private int nbSeat;
private Color color;
private Car replacingCar;
...
...
// Other fields
...
public Car() {
}
public Car(int nbSeat, Color color) {
this.nbSeat = nbSeat;
this.color = color;
}
public Car(Car replacingCar) {
this.replacingCar = replacingCar;
// specific validation rules
}
}
这个解决方案非常好,因为它不创建重复,它在一个位置收集所有逻辑:具有最大数量参数的构造函数。它有一个缺点:需要将调用链接到另一个构造函数
但这是一个缺点吗 3.在字段初始化器中为符合以下条件的字段指定默认值