0 0

有关static编译时问题10

从csdn中看到的一个问题:
public class Test {
	static {
		s = "9";//no  error
		System.out.println(s); //非法向前引用error
	}
	
	private static String s;
}




以下纯属个人理解:

假设编译通过,从java生命周期中的装载——连接——初始化中的连接阶段来说,分为3个部分:验证、准备、解析,装载的时候在堆中生成java.lang.Class的实例,验证阶段做些字节码、类型等验证工作,准备阶段就是为类变量分配内存,并设为默认值,也就是说,在这个时候已经为String ss分配了内容,并且设成null值,而此时static块是没有执行的,static块在编译的时候被编译器弄成到<clinit>方法的方法体中,这是类和接口的初始化方法,java语言本身不能调用,这个<clinit>方法在初始化阶段才被java虚拟机调用,也就是说执行了static方法里的内容。

从我个人理解,这个类在运行时不会有任何问题。

但是为什么会在编译的时候报这样的错,我表示很费解,大牛们解释下?



执行javac -verbose Test.java命令出来的结果:
[解析开始时间 Test.java]
[解析已完成时间 31ms]
[源文件的搜索路径: D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mqjms.jar,D:\programs\IBM\WebSphere MQ\Java\lib\jms.ja
r,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi.local.
jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi.remote.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jm
qi.system.jar,D:\programs\IBM\WebSphere MQ\Java\lib\jta.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.ese.jar,D:\
programs\IBM\WebSphere MQ\Java\lib\dhbcore.jar,D:\programs\IBM\WebSphere MQ\Java\lib\rmm.jar,D:\programs\IBM\WebSphere M
Q\Java\lib\jndi.jar,D:\programs\IBM\WebSphere MQ\Java\lib\ldap.jar,D:\programs\IBM\WebSphere MQ\Java\lib\fscontext.jar,D
:\programs\IBM\WebSphere MQ\Java\lib\providerutil.jar,D:\programs\IBM\WebSphere MQ\Java\lib\CL3Export.java,D:\programs\I
BM\WebSphere MQ\Java\lib\CL3Nonexport.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jar,D:\programs\IBM\WebSphere
 MQ\Java\lib\com.ibm.mq.headers.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.pcf.jar,D:\programs\IBM\WebSphere M
Q\Java\lib\connector.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.commonservices.jar,.,D:\programs\IBM\SQLLIB\ja
va\db2java.zip,D:\programs\IBM\SQLLIB\java\db2jcc.jar,D:\programs\IBM\SQLLIB\java\sqlj.zip,D:\programs\IBM\SQLLIB\java\d
b2jcc_license_cu.jar,D:\programs\IBM\SQLLIB\bin,D:\programs\IBM\SQLLIB\java\common.jar]
[类文件的搜索路径: D:\programs\java\jdk1.6.0_18\jre\lib\resources.jar,D:\programs\java\jdk1.6.0_18\jre\lib\rt.jar,D:\pr
ograms\java\jdk1.6.0_18\jre\lib\sunrsasign.jar,D:\programs\java\jdk1.6.0_18\jre\lib\jsse.jar,D:\programs\java\jdk1.6.0_1
8\jre\lib\jce.jar,D:\programs\java\jdk1.6.0_18\jre\lib\charsets.jar,D:\programs\java\jdk1.6.0_18\jre\classes,D:\programs
\java\jdk1.6.0_18\jre\lib\ext\dnsns.jar,D:\programs\java\jdk1.6.0_18\jre\lib\ext\localedata.jar,D:\programs\java\jdk1.6.
0_18\jre\lib\ext\sunjce_provider.jar,D:\programs\java\jdk1.6.0_18\jre\lib\ext\sunmscapi.jar,D:\programs\java\jdk1.6.0_18
\jre\lib\ext\sunpkcs11.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mqjms.jar,D:\programs\IBM\WebSphere MQ\Java\lib
\jms.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi
.local.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jmqi.remote.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ib
m.mq.jmqi.system.jar,D:\programs\IBM\WebSphere MQ\Java\lib\jta.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.ese.
jar,D:\programs\IBM\WebSphere MQ\Java\lib\dhbcore.jar,D:\programs\IBM\WebSphere MQ\Java\lib\rmm.jar,D:\programs\IBM\WebS
phere MQ\Java\lib\jndi.jar,D:\programs\IBM\WebSphere MQ\Java\lib\ldap.jar,D:\programs\IBM\WebSphere MQ\Java\lib\fscontex
t.jar,D:\programs\IBM\WebSphere MQ\Java\lib\providerutil.jar,D:\programs\IBM\WebSphere MQ\Java\lib\CL3Export.java,D:\pro
grams\IBM\WebSphere MQ\Java\lib\CL3Nonexport.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.jar,D:\programs\IBM\We
bSphere MQ\Java\lib\com.ibm.mq.headers.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.pcf.jar,D:\programs\IBM\WebS
phere MQ\Java\lib\connector.jar,D:\programs\IBM\WebSphere MQ\Java\lib\com.ibm.mq.commonservices.jar,.,D:\programs\IBM\SQ
LLIB\java\db2java.zip,D:\programs\IBM\SQLLIB\java\db2jcc.jar,D:\programs\IBM\SQLLIB\java\sqlj.zip,D:\programs\IBM\SQLLIB
\java\db2jcc_license_cu.jar,D:\programs\IBM\SQLLIB\bin,D:\programs\IBM\SQLLIB\java\common.jar]
[正在装入 java\lang\Object.class(java\lang:Object.class)]
[正在装入 java\lang\String.class(java\lang:String.class)]
[正在检查 Test]
Test.java:5: 非法向前引用
                System.out.println(s); //非法向前引用error
                                   ^
[正在装入 java\lang\System.class(java\lang:System.class)]
[正在装入 java\io\PrintStream.class(java\io:PrintStream.class)]
[正在装入 java\io\FilterOutputStream.class(java\io:FilterOutputStream.class)]
[正在装入 java\io\OutputStream.class(java\io:OutputStream.class)]
[总时间 297ms]
1 错误



问题补充
xx521 写道
这样编写是有问题的,因为在同一级别的时候编译是有先后顺序的
可以看下这个贴,以前遇到的错误,后来整理的
http://www.ctaoyu.com/index.php/archives/241
http://www.ctaoyu.com/index.php/archives/245




理论上来说,类的字段放在任何位置都应该没有问题

对于放在static或者非static块后面声明的字段如果在块中被使用,就会报这种非法向前引用的错误

而放在构造方法中则没有问题,其中缘由仍不得解

如下
public class Test {
	{
		System.out.println(s);
	}
	private String s;
}


这个代码还是有向前非法引用的错误,而下面这个则没有错误:
public class Test {
	public Test(){
		System.out.println(s);
	}
	private String s;
}


问题补充:<div class="quote_title">xfei6868 写道</div><div class="quote_div">建议你看一下这个小书:http://wenku.baidu.com/view/1807cf84b9d528ea81c77949.html <br /> <br />关于类初始化,可以这么认为 <br /> <br />static变量&nbsp; 和 static块 是最先初始化的(他们不分顺序) <br /> <br />类变量和非静态块 是第二顺序,应该也不分顺序。 <br /> <br />然后才是类的构造函数。</div> <br /> <br /> <br />这个问题与类的初始化已经没有关系了,且类的装载连接初始化问题我已经按照jvm规范中的理解写在了问题中 <br /> <br />现在是编译通不过

问题补充:首先,你回复的这个里面没有静态变量和静态块 <br /> <br />其次,即使是静态变量和静态块,在静态块中可以对静态变量可以赋值,但不能使用,不解在于此 <br /> <br /> <br /> <br /> <br /><div class="quote_title">xfei6868 写道</div><div class="quote_div">怎么 就理解不了呢? <br /> <br />这个就是因为顺序引起的,静态变量在后面的原因无法报错。 <br /> <br /><div class="quote_title">引用</div><div class="quote_div"> <br />public class Test { <br /> { <br /> System.out.println(s); <br /> } <br /> private String s; <br />} <br /></div> <br />而 <br /> <br /><div class="quote_title">引用</div><div class="quote_div"> <br />public class Test { <br /> public Test(){ <br /> System.out.println(s); <br /> } <br /> private String s; <br />} <br /></div> <br /> <br />能成功的原因是&nbsp; 构造函数是在静态段和静态变量后完成的。 <br />自然没有问题。</div> <br />

问题补充:<div class="quote_title">cantellow 写道</div><div class="quote_div">请参照JVM规范2.16.4 <br /><a href="http://java.sun.com/docs/books/jvms/first_edition/html/Concepts.doc.html#19075" target="_blank">http://java.sun.com/docs/books/jvms/first_edition/html/Concepts.doc.html#19075</a> <br />The intent here is that a type has a set of initializers that put it in a consistent state, and that this state is the first state that is observed by other classes. The static initializers and class variable initializers are executed in textual order and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope. This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations. <br /> <br />大概意思就是,类变量初始化顺序按照文本顺序执行,并且不可以在类变量声明之前引用(注意是引用)它,即使这些变量是可见的。设计这个限制是为了在编译时期检测绝大多数循环或者损坏的初始化。 <br /> <br />我的理解是,引用类变量会导致类主动初始化,而这个时候正在执行&lt;cinit&gt;方法,所以为了防止此类循环和损坏的初始化发生,java编译器设置了这一条规定。<img src="/images/smiles/icon_wink.gif"/>&nbsp;<img src="/images/smiles/icon_wink.gif"/> </div> <br /> <br /> <br />正如前面几位所说: <br />public class Test {&nbsp; <br />&nbsp;&nbsp;&nbsp; static {&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = "9";//no&nbsp; error&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(s); //非法向前引用error&nbsp; <br /> &nbsp;&nbsp; print(); <br />&nbsp;&nbsp;&nbsp; }&nbsp; <br />&nbsp;&nbsp;&nbsp; static void print() { <br /> System.out.println(s); <br /> } <br />&nbsp;&nbsp;&nbsp; private static String s; <br /> <br />} <br /> <br />这算不算声明之前就“引用”呢

问题补充:<div class="quote_title">cantellow 写道</div><div class="quote_div">当然算声明之前就应用了,我想你应该知道,如果把private static String s;放在最前面就没有问题。所以,对s的赋值是可以打乱顺序的,但是只要访问它,就会出现这种问题。下面的代码也会出现向前引用的问题: <br /><pre name="code" class="java">
public class Test { 
    static { 
        s = "9";//no  error 
        String ss = s; //非法向前引用error 
    } 
     
    private static String s; 

</pre></div> <br />但那个代码是可以通过编译的: <br />public class Test {&nbsp; <br />&nbsp;&nbsp;&nbsp; static {&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = "9";//no&nbsp; error&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(s); //非法向前引用error&nbsp; <br />&nbsp;&nbsp; print(); <br />&nbsp;&nbsp;&nbsp; }&nbsp; <br />&nbsp;&nbsp;&nbsp; static void print() { <br />System.out.println(s); <br />} <br />&nbsp;&nbsp;&nbsp; private static String s; <br /> <br />}
2010年12月13日 11:05

8个答案 按时间排序 按投票排序

0 0

引用
这个<clinit>方法在初始化阶段才被java虚拟机调用,也就是说执行了static方法里的内容。


并不是执行!!试想一下,这是在编译时期,怎么会是执行呢?
要理解清楚,首先不要用运行的眼光去看待它!

2010年12月29日 20:11
0 0

public class Test {  
    static {  
        s = "9";//no  error  
       // System.out.println(s); //非法向前引用error  
   print(); 
    }  
    static void print() { 
System.out.println(s); 
} 
    private static String s; 

}


java编译器在编译时,会把类变量初始化语句和静态初始化语句的代码都放在class文件中的<cinit>方法,但是当访问s类变量时,有直接引用和间接引用之分, String ss = s;它在编译时期(不是运行时期!!)就去访问s,而print(); 方法并没有在编译时期直接访问s,而是在运行时期访问s,这有本质的区别!!因为在编译时期访问s的话,会导致s的初始化,这就会发生像前面说的循环初始化和损坏初始化。
下面这段代码也会出现同样的编译错误:
static {
	s = "9";// no error
	//System.out.println(s); // 非法向前引用error
	//String ss = s;
	print(s); 
    }
    static void print(String s) { 
	System.out.println(s); 
	} 
    public static String s = "";

2010年12月29日 20:03
0 0

当然算声明之前就应用了,我想你应该知道,如果把private static String s;放在最前面就没有问题。所以,对s的赋值是可以打乱顺序的,但是只要访问它,就会出现这种问题。下面的代码也会出现向前引用的问题:

public class Test {  
    static {  
        s = "9";//no  error  
        String ss = s; //非法向前引用error  
    }  
      
    private static String s;  
}  

2010年12月29日 18:40
0 0

请参照JVM规范2.16.4
http://java.sun.com/docs/books/jvms/first_edition/html/Concepts.doc.html#19075
The intent here is that a type has a set of initializers that put it in a consistent state, and that this state is the first state that is observed by other classes. The static initializers and class variable initializers are executed in textual order and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope. This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations.

大概意思就是,类变量初始化顺序按照文本顺序执行,并且不可以在类变量声明之前引用(注意是引用)它,即使这些变量是可见的。设计这个限制是为了在编译时期检测绝大多数循环或者损坏的初始化。

我的理解是,引用类变量会导致类主动初始化,而这个时候正在执行<cinit>方法,所以为了防止此类循环和损坏的初始化发生,java编译器设置了这一条规定。 

2010年12月29日 10:14
0 0

简单的可以看出 java的块中的引用跟定义的引用使用了同样的规则,

就是 在其前面的不能被引用 ,就类似于:

int i = j;  //向前引用错误。
int j = 5;


然而同样之所以在方法中可以同样是由于我上面说的一种装载顺序的原因:

static 变量或static块 -->  成员变量或成员块 --> 构造函数

例如这样就能成功:
public class Test {
	{
		System.out.println(s);
	}
	private static String s;
}


同样值得研究的是方法普通方法是永远可以调用对应变量的(无论在其前或者后),而块同样可以调用方法,如果写一个这样的方法就可以实现了楼主的想法:
public class Test {
	{
		s = "9";
                printOut();
	}
        void printOut() {
                System.out.println(s);
        }
	private String s;
}


然而赋值的位置不同造成输出结果也就不同,例如改为:
public class Test {
	{
                printOut();
	}
        void printOut() {
                System.out.println(s);
        }

        {
                s = "9";
        }
	private String s;
}


输出结果就不是9。

同理:static的块,变量,方法,赋值也一样遵循这样的原则。

可以看出来,这样的原则:
1. java 块,变量在引用问题上是同一个层次的,就是不能在定义之前引用。
2. java的方法就可以打破这样的约束,但是并不是所有的变量赋值,方法引用完成后才执行此方法,要看方法“嵌入”的位置。
3. 变量的赋值是可以放在任何地方的,同时定义和赋值并不是一起的,例如:
public class Test {
	{
                printOut();
	}
        void printOut() {
                System.out.println(s);
        }

	private String s="9";
}

这个结果同样会令人不解。

综合以上:

可以看出楼主说的 赋值后输出本身应该没什么问题了,但是由于java对于必须在定义后引用的检查造成这样的做是不能编译通过的。





2010年12月14日 09:30
0 0

怎么 就理解不了呢?

这个就是因为顺序引起的,静态变量在后面的原因无法报错。

引用

public class Test {
{
System.out.println(s);
}
private String s;
}



引用

public class Test {
public Test(){
System.out.println(s);
}
private String s;
}


能成功的原因是  构造函数是在静态段和静态变量后完成的。
自然没有问题。

2010年12月13日 20:20
0 0

建议你看一下这个小书:http://wenku.baidu.com/view/1807cf84b9d528ea81c77949.html

关于类初始化,可以这么认为

static变量  和 static块 是最先初始化的(他们不分顺序)

类变量和非静态块 是第二顺序,应该也不分顺序。

然后才是类的构造函数。

2010年12月13日 11:05
0 0

这样编写是有问题的,因为在同一级别的时候编译是有先后顺序的
可以看下这个贴,以前遇到的错误,后来整理的
http://www.ctaoyu.com/index.php/archives/241
http://www.ctaoyu.com/index.php/archives/245

2010年12月13日 11:05

相关推荐

Global site tag (gtag.js) - Google Analytics