初始化字段变量

发布于 2024-10-12 05:40:56 字数 207 浏览 12 评论 0 原文

public class Foo {
    private int var;

    public Foo() {
        var = 10;
    }
}

在这段代码中,var是先被赋予默认值,然后重新被赋予10,还是直接被赋予10而不被赋予默认值?

虽然是一个微不足道的问题,但我很好奇。

public class Foo {
    private int var;

    public Foo() {
        var = 10;
    }
}

In this code snippet, would var first be assigned a default value and then reassigned to 10, or would it be assigned to 10 directly without being assigned a default value?

Somewhat a trivial question, but I'm curious.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

感性不性感 2024-10-19 05:40:56

如果您查看 Foo.class 的反编译字节码,您会注意到以下内容:

  • 类的构造函数本身仅分配值 10(bipush 和 putfield)。该类的构造函数不会先分配 0,然后分配 10。
  • 无论何时访问该字段,VM 都会为该字段设置默认值 0 - 无论来自哪个代码。因此,这个默认值不会出现在任何地方 - 至少不会出现在类或其他通过反射访问该字段的类的字节码中。原始默认值被烘焙到虚拟机中。
  • 显式设置默认值将产生不同的字节码,请参阅第二个示例。

public class Foo {

  private int var;

  public Foo();
     0  aload_0 [this]
     1  invokespecial java.lang.Object() [10]
     4  aload_0 [this]
     5  bipush 10
     7  putfield Foo.var : int [12]
    10  return

如果您编写以下内容:

public class Foo {
    private int var = 0;

    public Foo() {
        var = 20;
    }
}

字节码将是:

 0  aload_0 [this]
 1  invokespecial java.lang.Object() [10]
 4  aload_0 [this]
 5  iconst_0
 6  putfield Foo.var : int [12]
 9  aload_0 [this]
10  bipush 20
12  putfield Foo.var : int [12]
15  return

下一个示例显示访问变量仍然不会导致任何值的赋值

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var);
        var=10;
    }
}

此代码将打印 0 因为操作码 8 处的 getField Foo.var 会将“0”压入操作数堆栈:

public Foo();
   0  aload_0 [this]
   1  invokespecial java.lang.Object() [10]
   4  getstatic java.lang.System.out : java.io.PrintStream [12]
   7  aload_0 [this]
   8  getfield Foo.var : int [18]
  11  invokevirtual java.io.PrintStream.println(int) : void [20]
  14  aload_0 [this]
  15  bipush 10
  17  putfield Foo.var : int [18]
  20  return

If you look at the decompiled byte code of Foo.class, you will notice the following:

  • The class' constructor itself only assigns the value 10 (bipush and putfield). The class's constructor does not first assign 0 and then 10.
  • The VM will have a default value of 0 for the field whenever it is accessed - no matter from which code. So this default value will not show up anywhere - at least not in the bytecode of the class or other classes which access the field for example by reflection. Primitive default values are baked into the VM.
  • Explicitly setting the default will produce different bytecode, see second example.

.

public class Foo {

  private int var;

  public Foo();
     0  aload_0 [this]
     1  invokespecial java.lang.Object() [10]
     4  aload_0 [this]
     5  bipush 10
     7  putfield Foo.var : int [12]
    10  return

If you write the following:

public class Foo {
    private int var = 0;

    public Foo() {
        var = 20;
    }
}

the bytecode will be:

 0  aload_0 [this]
 1  invokespecial java.lang.Object() [10]
 4  aload_0 [this]
 5  iconst_0
 6  putfield Foo.var : int [12]
 9  aload_0 [this]
10  bipush 20
12  putfield Foo.var : int [12]
15  return

The next example shows that accessing the variable will still not lead to an assignment of any value:

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var);
        var=10;
    }
}

This code will print 0 because getField Foo.var at opcode 8 will push "0" onto the operand stack:

public Foo();
   0  aload_0 [this]
   1  invokespecial java.lang.Object() [10]
   4  getstatic java.lang.System.out : java.io.PrintStream [12]
   7  aload_0 [this]
   8  getfield Foo.var : int [18]
  11  invokevirtual java.io.PrintStream.println(int) : void [20]
  14  aload_0 [this]
  15  bipush 10
  17  putfield Foo.var : int [18]
  20  return
恰似旧人归 2024-10-19 05:40:56

在调用构造函数之前,未初始化的字段将始终被分配默认值,因为运行时将在调用构造函数之前将对象的内存分配归零。它必须这样做,因为它不知道构造函数会提前做什么,并且因为派生类可能存在于其他 jars/类路径中并获取值(如果它受保护)或调用使用该字段之前的方法。由构造函数初始化。

这是独立于编译器执行的,因此这不是编译器可以优化掉的东西,并且编译器甚至无法控制它。

Uninitialized fields will always be assigned the default value prior to calling the constructor, because the runtime will zero the memory allocation for the object prior to calling the constructor. It must do this because it does not know what the constructor might do ahead of time, and because derived classes may live in other jars/classpaths and fetch the value (if it's protected) or call into a method that uses the field before it is initialized by the constructor.

This is performed independently of the compiler, and therefore this is not something the compiler can optimize away, and the compiler doesn't even have control over this.

过去的过去 2024-10-19 05:40:56

根据规范:(第 4.2.15 节)

首先是 0,

然后是 10。

如果您首先在构造函数中读取它,您将得到 0。

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var); //0
        var = 10;
    }
}

According to the spec: (Section 4.2.15)

First 0.

Then 10.

If you would read it first in the constructor, you would get 0.

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var); //0
        var = 10;
    }
}
白昼 2024-10-19 05:40:56

它将首先被赋予默认值。特别是,如果 Foo 派生自 Bar,并且 Bar 的构造函数可以以某种方式获取 var 的值(例如,通过在 Bar 中声明并在 Foo 中重写的虚拟方法),则该默认值将是可见的,即使该变量是最终的。例如:

class Parent {

    public Parent() {
        showVariables();
    }

    public void showVariables() {
    }
}

class Child extends Parent {
    private final int x;

    public Child() {
        x = 10;
    }

    @Override
    public void showVariables() {
        System.out.println("x = " + x); // Prints x = 0
    }
}

public class Test {

    public static void main(String[] args) {
        new Child();
    }
}

请注意,即使在声明时对字段进行了初始化,仍然也会发生这种情况:

public class Foo {
    private int var = 10;

    public Foo() {
        // Implicit call to super constructor - this occurs *before*
        // var is assigned the value 10
    }
}

在这方面,Java 与 C# 不同。在 C# 中,var 在调用基本构造函数之前会被赋值为 10。

It would be given the default value first. In particular, if Foo was derived from Bar, and the constructor of Bar could get at the value of var somehow (e.g. through a virtual method declared in Bar and overridden in Foo), that default value would be visible, even if the variable is final. For example:

class Parent {

    public Parent() {
        showVariables();
    }

    public void showVariables() {
    }
}

class Child extends Parent {
    private final int x;

    public Child() {
        x = 10;
    }

    @Override
    public void showVariables() {
        System.out.println("x = " + x); // Prints x = 0
    }
}

public class Test {

    public static void main(String[] args) {
        new Child();
    }
}

Note that this still happens even when the field is initialized at the point of declaration:

public class Foo {
    private int var = 10;

    public Foo() {
        // Implicit call to super constructor - this occurs *before*
        // var is assigned the value 10
    }
}

In this respect Java differs from C#. In C#, var would be assigned the value 10 before the call to the base constructor.

往日 2024-10-19 05:40:56

分配默认值意味着在您分配默认值之前它已经有一个值。对象在创建时具有默认值,但并未分配该值。

如果查看字节码,唯一的代码就是分配新值。

Assigning the default value implies it had a value before you assigned the default value. An object has the default value when it is created, it is not assigned it.

If you look at the byte code, the only code is to assign the new value.

梦亿 2024-10-19 05:40:56

语言规范说:

否则,新对象中的所有实例变量(包括超类中声明的实例变量)都将初始化为其默认值(第 4.12.5 节)。

http://java.sun.com/文档/books/jls/third_edition/html/execution.html#44410

然后调用构造函数。

The language specification says:

Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#44410

Then the constructor is called.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文