非法向前引用

BadPattern 2012-03-15 11:20:16
来自java 语言规范8.3.2.3:
http://docs.oracle.com/javase/specs/jls/se7/html/
class Test1 {
int i = j; // compile-time error:
// incorrect forward reference
int j = 1;
}


class Z {
static int i = j + 2;
static int j = 4;
}

都会报"非法向前引用",我的理解是在变量声明之前使用了它,c语言中这样做是正常的,使用方法和变量之前都要先定义或者声明,习惯了java中方法可以不按顺序随便写(当然变量最好不要乱放位置)就感觉有点奇怪.
java语言规范中有一句不太明白的解释:
The restrictions above are designed to catch, at compile time, circular or otherwise malformed initializations

但是方法却不以这种形式来检查,所以以下代码不会报错,而且执行的结果为0:
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}

想请教一下,为什么要这样规定,是为了规避什么问题??
...全文
586 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
jigenghua 2013-03-16
  • 打赏
  • 举报
回复
[quote=引用 9 楼 dr8737010 的回复:] 类加载分装载->链接->初始化三个阶段,在链接阶段的准备阶段中所有类变量和成员变量都已经分配了所需的空间了,最后初始化阶段引用的变量当然已经定义了,所以跟你这里举例的不一样,方法里边的变量都是存在栈上的. 肯定出于某种目的才做了这种编译期间报错的防范. 正解!这才是问题的关键!
BadPattern 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 jiakai0419 的回复:]

引用 9 楼 dr8737010 的回复:

在链接阶段的准备阶段中所有类变量和成员变量都已经分配了所需的空间了



不可能。

不new对象。不会分配成员变量的空间。

成员变量是属于对象的。

当你new对象的时候,就会发生的说的那种情况。
[/Quote]
嗯,你说的对,准备阶段只会为类变量分配所需空间,成员变量不包含于此
Acesidonu 2012-03-15
  • 打赏
  • 举报
回复
不能用一个未初始化的值来初始化另一个值吧
jiakai0419 2012-03-15
  • 打赏
  • 举报
回复 1
我印象中,只有方法和类。可以消除向前引用。

为什么方法和类,可以消除向前引用。确实跟如何编译应该是有关的。

但是,为什么楼主说的那种情况是不行的呢?

其实你可以这么理解。


public class Test {

public static void main(String[] args) throws Exception{
int i = j; //(1)
int j = 10; //(2)
}

}



程序是动态执行的。

运行到(1)的时候,栈里面还没有j

所以int i = j;是错误的。

编译器编译的时候检查出了这种错误。所以不行。
creso 2012-03-15
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 dr8737010 的回复:]

引用 1 楼 creso 的回复:

int i = j;
这时候j还没被声明,所以编译异常这个很好理解。


那对于方法为什么没有这样的说法,前边的函数完全可以调用后边的函数,那后边的函数也没声明啊
[/Quote]
这个就是编译顺序问题了
调用方法的时候j一定是先被初始化了,但是没有赋值
五哥 2012-03-15
  • 打赏
  • 举报
回复
先声明 后使用
BadPattern 2012-03-15
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 creso 的回复:]

int i = j;
这时候j还没被声明,所以编译异常这个很好理解。
[/Quote]

那对于方法为什么没有这样的说法,前边的函数完全可以调用后边的函数,那后边的函数也没声明啊
creso 2012-03-15
  • 打赏
  • 举报
回复
int i = j;
这时候j还没被声明,所以编译异常这个很好理解。
jiakai0419 2012-03-15
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 dr8737010 的回复:]

在链接阶段的准备阶段中所有类变量和成员变量都已经分配了所需的空间了

[/Quote]

不可能。

不new对象。不会分配成员变量的空间。

成员变量是属于对象的。

当你new对象的时候,就会发生的说的那种情况。



BadPattern 2012-03-15
  • 打赏
  • 举报
回复
大概明白了一些,查看java解惑一书第49条,这种初始化属于一种类初始化循环,它们可能会导致在静态字段被初始化之前就调用构造器.静态字段,甚至是final类型的静态字段,可能会在它们被初始化之前,被读走其默认值.

因此"非法向前引用"这种编译报错的机制在一定程度上避免了这种被读走默认值的情况发生,因为由这种类初始化循环所引发的问题是难以诊断的.

结贴了
BadPattern 2012-03-15
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 acesidonu 的回复:]

不能用一个未初始化的值来初始化另一个值吧
[/Quote]
事实上可以用一个未初始化的值来初始化:
public class T{
private static int j;
private static int i = (j+2);
static{
j = 1;
}
public static void main(String[] args){
System.out.println(i);
}
}

打印i的值为2而不是3
BadPattern 2012-03-15
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 jiakai0419 的回复:]

我印象中,只有方法和类。可以消除向前引用。

为什么方法和类,可以消除向前引用。确实跟如何编译应该是有关的。

但是,为什么楼主说的那种情况是不行的呢?

其实你可以这么理解。

Java code

public class Test {

public static void main(String[] args) throws Exception{
……
[/Quote]

类加载分装载->链接->初始化三个阶段,在链接阶段的准备阶段中所有类变量和成员变量都已经分配了所需的空间了,最后初始化阶段引用的变量当然已经定义了,所以跟你这里举例的不一样,方法里边的变量都是存在栈上的.

肯定出于某种目的才做了这种编译期间报错的防范.
菖蒲老先生 2012-03-15
  • 打赏
  • 举报
回复
java中变量,方法,类之间还是有很多区别的。
方法可以,变量不一定可以
结贴是美德 2012-03-15
  • 打赏
  • 举报
回复
这是个难题 我看我可以mark一下。。

62,635

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧