??xml version="1.0" encoding="utf-8" standalone="yes"?>
老规矩,代码伺?^+^
你可以先想一下,q个g是多?看看与实际结果是否一致?/font>
输出l果Q?/font>
现在对ClassTest.javaq行一点修改,如下Q?br />
两次的输出结果会一样么Q?br />我既然这么问了,你肯定会说不一P那你知道原因么?你知道这ơ的输出l果么?
我们可以看见Q对于三个staitc final 变量 XQYQZQ他们的初始化表辑ּ是@环定义的。在~译期间不能定它们的|所以它们是q行期间帔R( runtime constants )Q编译器不会q行帔R替换?br />而且每个表达式的计算会依赖于类装蝲的顺序?br />
?当中Q也是同一道理
只是单的改变的输出顺序,l果却截然不同。哪一个才是你惌得结果呢Q?/font>
我的例子看v来有ҎI心思钻牛角,但是在大型项目当中,也许׃出现与例子当中相同的循环定义Q如果真的存在的话,那么在纷J的代码当中惌发现循环定义可不是gҎ的事情?br />
我只是讲了一下@环定义会引发的问题,我暂时也想不Z么好的解军_法,只能在编E的q程当中量注意啦?br />
马嘉?/font>
2006-10-19
在上一文章?
inconstant constants ( 变化无常的常?)
》的基础?我们再来研究一?font color="#000000">?Java 中用@环定义会出现哪些问题?/font>
public
class
ClassX{
public static final intX = 2*
ClassY.Y;
}
public class
ClassY{
public static final intY =ClassZ.Z+ 1
;
}
public
class
ClassZ
extends
ClassX{
public static final int Z= X + 3
;
}
public
class
ClassTest{
public static void
main(String[] args){
System.out.println(ClassX.X
+
ClassY.Y
+
ClassZ.Z);
}
}
代码中的static final变量XQYQZQ@环定义?/font>
15
public class
ClassTest{
public staticvoid
main(String[] args){
System.out.println(ClassZ.Z
+
ClassY.Y
+
ClassX.X);
}
}
可以先思考一下?br />
输出l果Q?/font>
8
让我来告诉你q是怎么回事?/p>
例如Qؓ了计出?中的 ClassTest l果Q我们可以预见,ClassX 是第一个被装蝲的类Q但是第一个完成初始化的类却是 ClassZ.
让我们一步一步看看都发生了什么?br />
1. X = 2 * ClassY.Y; 计算XQ需要知道ClassY.Y的|下一步计Y?br />2. Y = ClassZ.Z + 1; 计算YQ需要知道ClassZ.Z的|下一步计Z?br />3. Z = X + 3; 计算ZQ需要知道X的|而此时X的D没有被计出?又{回来了,居然是个圈,呵呵^+^)Q所以我们用X的默认??br />
因此Q?br /> Z = 3;
Y = 4;
X = 8;
所?ClassX.X + ClassY.Y + ClassZ.Z = 15
不同的是QClassZ是第一个被装蝲的类QClassX是第一个完成初始化的类
1. Z = X + 3;
2. X = 2 * ClassY.Y;
3. Y = ClassZ.Z + 1;(此时使用Z的默认?)
因此Q?/font>
Y = 1;
X = 2;
Z = 5;
所?ClassZ.Z + ClassY.Y + ClassX.X = 8
如果独立看每一个定义的话,g都可以进行常量替换,看不ZQ何问题。但是这L代码在不久的来׃引发问题Q而且不易被我们所察觉?br />
在你的应用程序当中不l意的代码改变(例如CZ代码中我们只是改变了输出序Q却产生了截然不同的l果Q,׃D不同的类装蝲序和计顺序,或者在q发的线E调度中Q可能也会导致致不同的类装蝲序和计顺序。不q的是,大多数编译器不认U代码是错误Q也不会对编Eh员给ZQ何警告?/font>
注:有错误请告诉我,非常感谢Q?/font>
]]>
马嘉?/font>
2006-10-18
看到q个题目也许你会感到奇怪,会想我在胡说八道什么,一定又是v个怪异的名字,骗取点击率。还请你耐心看完Q如果你有所收获Q那么我很高_如果你还是觉得上当了Q那我l努力写出点有用的东西,呵呵?br />
其实我想了很久,也还是不知道起一个什么题目好Q就套用了?The Java Language Specification 》中的一个名词?inconstant constants”,我把他翻译成?font color="#0000ff">变化无常的常?/font>?
注:部分内容在? 使用Java中的final变量需要注意的地方 》有提到Q不q我转蝲的原文不够详l深入,q才重新写一下?br />
我们q是来先看一D代码,׃码引出问题:
输出l果Q?br />
2
l果是显而易见的Q这里需要说明的是:
ҎJava语言规范Q对于java中的static final变量Q如果用一个在~译期间(complie time)可以计算出结果的表达式进行初始化Q则用到此变量的地方会被该表辑ּ的结果所替代。本例中Q在~译期间QClassTest.main() 函数?ClassX.X 被2所替代?br />
此时Q在cClassTest main()中不再有指向ClassX的动态链接,告诉ClassTest在运行的时候从ClassX获得X的|你可以通过使用javap反编译器帮助你理解?br />
1. 先编译ClassTest.java文g
javac ClassTest.java
2. 使用javap
javap -c ClassTest
屏幕输出Q?
可以看出Q在调用System.out.println()之前Q整?已经被放在JVM的堆栈中Q不再有指向ClassX.X的链接。如果此Ӟ改变ClassX.X的gؓ1,q且重新~译ClassX.X文gQ但是ƈ不重新编译ClassTest.java文gQ运行ClassTestQ输出结果仍然是2.
q么?帔R替换)的一个原因是Z在编译期间检查switch case语句。switch语句中的每一个case都需要一个常量|而且每两个之间都不能相同Q编译器在编译期间将会做查?
如果用来lstatic final变量q行初始化的表达式,只能在运行时L可以计算出|那么帔R替换׃会发?例如Q?
ClassX 改变了,我们再来看一下Main.main():
此时我们可以看见有个引用指向了Field X?br />( 如果把类ClassXҎInterfaceQ仍然会出现上面的结?)
当然有方法可以你避免出?font color="#0000ff">"inconstant constants"问题?br />
W一U方法:
当你要声明一个编译期间常量的时候,一定要保证此变量不会或者不太可能改变,或者尽量少使用声明为static final的变量。当然这只能L不能LQ所以我推荐使用W二U方法?br />
W二U方法:
变量声明ؓprivateQ同时声明一个方法来获得此变量的?
此时再改变ClassX.X的gؓ1Q重新编译ClassX.javaQ而不~译ClassTestQ结果就会显C?Q而非2。这避免了"inconstant constants"的问题?
下一准备讲一?/font>
《在java中用@环定义会出现哪些问题Q?/font>