【问题标题】:Why does javac allow fields to be defined after methods that use them in a class?为什么 javac 允许在类中使用它们的方法之后定义字段?
【发布时间】:2021-07-03 08:10:32
【问题描述】:

我注意到当你执行像sum=a+b; 这样的语句时 我们需要在此操作中使用它们之前初始化三个字段 即

int a=1 
int b=1;
int sum=0;
sum=a+b;

。 我的理解是,这是因为 javac 像解释器一样工作并且逐行读取。 但是当涉及到我们调用类中的字段并将值传递给方法的方法时,我们可以在方法声明之后定义字段。

public class Dog extends Animal{
public Dog(String name, int weight, int teeth, String coat) {
    super(name, true, true, weight);
    this.tail=true;
    this.eyes=2;
    this.teeth=teeth;
    this.coat=coat;

}

private int eyes;
private boolean tail;
private int teeth;
private String coat;}

参数eyes、tail、tooth 和coat 在用于将值传递给构造函数后已定义。 javac怎么理解方法后面有变量。 这是否允许,因为我们只能在类定义中定义字段而不能对它们进行任何操作?

【问题讨论】:

  • javac 不像解释器那样工作。

标签: java jvm javac


【解决方案1】:

Javac 是 Java 编译器将你的 Java 代码编译成字节码

JVM 是 Java 虚拟机运行/解释/将字节码翻译成本机机器码

这个错误只发生在局部变量上,因为编译器没有为局部变量设置默认值。对于实例变量,编译器设置默认值,例如整数为0,字符串为null等。

在上述情况下,如果你没有在构造函数中设置值,你的实例变量仍然会被默认值初始化。

private int eyes = 0;
private boolean tail = false;
private int teeth = 0;
private String coat = null;

但对于局部变量 a、b 或 sum,情况并非如此。 sum 可以未初始化,因为您在使用之前将值分配给 sum。那就是 sum = a+b 的声明将赋值,在你使用它之前在其他地方可能会像 sum2 = sum,它保证 sum 有一个值。

【讨论】:

    【解决方案2】:

    局部变量声明(以及最终的初始化)与它所在的块一起执行,并从中访问;
    实例字段声明(和初始化)在创建实例时执行,在方法执行之前1

    另一方面,这是不允许的:

    public class Dog extends Animal {
        private int total = eyes + teeth;  // ERROR!
        private int eyes = 2;
        private int teeth = 24;
    }
    

    以上代码无法编译 - 这是故意的!

    虽然这是有效的:

    public class Dog extends Animal {
        private int eyes = 2;
        private int teeth = 24;
        private int total = eyes + teeth;  // OK
    }
    

    Why ...

    在...类或接口 C(第 8.2 节,第 9.2 节)中声明的成员 m 的声明范围是 C 的整个主体,...

    由局部变量声明语句(第 14.4.2 节)在块中声明的局部变量的范围是块的其余部分,...

    这样做的一个原因是要避免 JLS 中提到的循环引用,请参阅下面链接中的示例 1。也在this answer 中由Hoopje 解释。


    1 - 初始化器中的字段引用例外,请参阅Restrictions on Field References in Initializers

    【讨论】:

      【解决方案3】:

      这与解释器和编译器之间的区别无关。它只是添加到语言中的一条规则,以防止两个字段在其初始化代码中相互引用。例如,以下类中字段的初始值是多少?

      class A {
          int a = b * b;
          int b = a + 1;
      }
      

      当然,Java 设计者本可以决定允许前向引用,并且仅在检测到此类循环时才生成错误。但他们做出了不同的决定,我认为这是正确的决定。

      没有理由禁止在方法中对字段的前向引用,因为当方法执行时,类已经被初始化。甚至构造函数在字段的初始值设定项被评估之后运行。

      【讨论】:

        猜你喜欢
        • 2015-03-02
        • 1970-01-01
        • 2015-02-20
        • 2021-11-25
        • 1970-01-01
        • 2023-04-09
        • 2014-09-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多