在构造函数内部或外部设置字段是否有区别?

时间:2014-01-07 04:19:49

标签: java

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;
    }
}

或许我需要做一些特别的计算。

但如果我这样做,还有另一个好理由吗?

8 个答案:

答案 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,那我会遇到一些麻烦适当的构造函数 - nullif(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编程语言包括静态初始化块。还有另外两种方法来初始化实例变量:

  1. 初始化程序块

    {       //初始化   }

  2. 最终方法

    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

以下是摘要

  1. telescoping constructor pattern有效,但很难写 当有很多参数时,客户端代码仍然难以读取 它。
  2. 解决方案是Builder模式的一种形式,而不是制作 直接所需的对象,客户端调用构造函数(或 静态工厂)具有所有必需参数并得到一个 建造者对象。

答案 7 :(得分:0)

我不是在谈论字节码,但它们可以在语义上有所不同(如果你有多个构造函数),

如果您将字段定义如下,则该字段将始终初始化为100,无论调用哪个构造函数:

int field = 100;

但是否则你应该在每个构造函数中初始化字段。

你的类可能只有一个构造函数,但是想想,在你的类的未来版本中是否还有其他构造函数?