实例初始值设定项和* this *关键字

时间:2015-06-19 10:45:52

标签: java initialization

尝试编译这段代码

public class Main {

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

    { System.out.println(x); } //Error here

    int x=1;
}

产生cannot reference a field before it is defined错误。但是如果我将初始化器行更改为

    { System.out.println(this.x); }

它就像一个魅力,打印默认的int值 0

这对我来说有点困惑,为什么this会有所不同?在这种情况下,它不应该是多余的吗?任何人都可以向我解释幕后发生的事情,以明确它是如何运作的吗?

PS:我知道通过在初始化程序之前声明x也可以使其正常工作。

4 个答案:

答案 0 :(得分:11)

我将尝试在编译器层上进行解释。

假设你有一个方法:

int x;
x = 1;
System.out.println(x);

编译将成功并执行。 如果您将方法更改为:

System.out.println(x);
int x;
x = 1;

它甚至不会与你给出的例子编译相同。

编译器将{ }初始化程序的代码复制到 ctor以及x=1初始化。

正如您所说,如果您在x=1初个化程序之前设置{ },它就会有效。

public class MainC {

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

    int x=1;
    {
      System.out.println(x);
    }
}

请参阅以下Java字节码:

  public MainC();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field x:I
         9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        12: aload_0
        13: getfield      #2                  // Field x:I
        16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        19: return
      LineNumberTable:
        line 1: 0
        line 7: 4
        line 9: 9
        line 10: 19

声明字段x并在使用之前获取值1 System.out.println致电。

那么,如果你在{ }之后根据同样的原因设置它,它为什么不起作用 你不能使用我的第二个例子的代码。在使用之后声明该字段是没有意义的。

那么为什么它适用于this关键字?!

让我们看一些代码:

public class Main {

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

    { System.out.println(this.x); } //Error here

    int x=1;
}

ctor的相应Java字节码:

  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: aload_0
         8: getfield      #3                  // Field x:I
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        14: aload_0
        15: iconst_1
        16: putfield      #3                  // Field x:I
        19: return
      LineNumberTable:
        line 1: 0
        line 7: 4
        line 9: 14

所以这里发生了什么?很容易说this关键字加载主对象 堆栈上的引用。之后,可以访问字段x,以便System.out.println调用可以成功执行。

答案 1 :(得分:5)

JSL 8.6应解释您的编译时错误:

  

允许实例初始值设定项通过关键字this(§15.8.3)引用当前对象...

     

使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此。有关控制实例变量的前向引用的精确规则,请参见§8.3.3。

§8.3.3中,它说:

  

使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此。具体来说,如果以下全部为真,则为编译时错误:

     
      
  • 在使用实例变量后,类或接口C中的实例变量声明以文本形式出现;

  •   
  • 在C的实例变量初始值设定项或C的实例初始值设定项中使用简单名称;

  •   
  • 使用不在作业的左侧;

  •   
  • C是封闭使用的最里面的类或接口。

  •   

这就是为什么写简单名x会给你错误。

答案 2 :(得分:1)

与JSL(§15.8.3

一样
  

当用作主表达式时,关键字this表示一个值,是对调用实例方法的对象的引用§15.12),或者到正在建造的物体。

调用

This关键字,然后在后台创建类的实例。当您使用{ System.out.println(this.x); }类创建i变量Main时。

答案 3 :(得分:0)

  

允许实例初始值设定项通过引用当前对象   关键字this(§15.8.3),使用关键字super(§15.11.2,   §15.12),并在范围内使用任何类型变量。

     

初始化包括执行任何类变量   初始化程序和类Test的静态初始化程序,以文本形式表示   顺序。

jvm在堆栈帧中为x分配内存会发生什么。当您使用this.x时,返回0,因为已经分配了变量。 如果变量在初始化时是静态的,你将得到1。