java constants_inconstant constants(变化无常的常量)

inconstant constants ( 变化无常的常量 )

马嘉楠

2006-10-18

看到这个题目也许你会感到奇怪,会想我在胡说八道什么,一定又是起个怪异的名字,骗取点击率。还请你耐心看完,如果你有所收获,那么我很高兴;如果你还是觉得上当了,那我继续努力写出点有用的东西,呵呵。

其实我想了很久,也还是不知道起一个什么题目好,就套用了《 The Java Language Specification》中的一个名词“ inconstant constants”,我把他翻译成“变化无常的常量”

注:部分内容在《

使用Java中的final变量需要注意的地方

》有提到,不过我转载的原文不够详细深入,这才重新写一下。

我们还是来先看一段代码,由代码引出问题:

public

class

ClassX {

public

static

final

int

X

=

2

;

}

public

class

ClassTest {

public

static

void

main(String[] args){

System.out.println(ClassX.X);

}

}

输出结果:

2

结果是显而易见的,这里需要说明的是:

根据Java语言规范,对于java中的static final变量,如果用一个在编译期间(complie time)可以计算出结果的表达式进行初始化,则用到此变量的地方会被该表达式的结果所替代。本例中,在编译期间,ClassTest.main() 函数中 ClassX.X 将被2所替代。

此时,在类ClassTest main()中不再有指向ClassX的动态链接,告诉ClassTest在运行的时候从ClassX获得X的值,你可以通过使用javap反编译器帮助你理解。

1. 先编译ClassTest.java文件

javac ClassTest.java

2. 使用javap

javap -c ClassTest

屏幕输出:

Complied from "ClassTest.java"

public class ClassTest extends java.lang.Object{

public ClassTest();

Code:

0: aload_0

1: invokespecial     #1; //Method java/lang/Object."

":()V

4: return

public static void main(java.lang.String[]);

Code:

0: getstatic         #2; //Field java/lang/System.out:Ljava/io/PrintStream;

3: iconst_2

4: invokevirtua      #3; //Method java/io/PrintStream.println:(I)V

7: return

}

可以看出,在调用System.out.println()之前,整数2已经被放在JVM的堆栈中,不再有指向ClassX.X的链接。如果此时,改变ClassX.X的值为1,并且重新编译ClassX.X文件,但是并不重新编译ClassTest.java文件,运行ClassTest,输出结果仍然是2.

这么做(常量替换)的一个原因是为了在编译期间检查switch case语句。switch语句中的每一个case都需要一个常量值,而且每两个之间都不能相同,编译器在编译期间将会做检查。

如果用来给static final变量进行初始化的表达式,只能在运行时刻才可以计算出值,那么常量替换就不会发生.例如:

public

class

ClassX {

public

static final int X= new

java.util.Random().nextInt();

}

ClassX 改变了,我们再来看一下Main.main():

Complied from "ClassTest.java"

public class ClassTest extends java.lang.Object{

public ClassTest();

Code:

0: aload_0

1: invokespecial      #1; //Method java/lang/Object."

":()V

4: return

public static void main(java.lang.String[]);

Code:

0: getstatic          #2; //Field java/lang/System.out:Ljava/io/PrintStream;

3: getstatic          #3; //Field ClassX.X:I

6: invokevirtual      #4; //Method java/io/PrintStream.println:(I)V

9: return

}

此时我们可以看见有个引用指向了Field X。

( 如果把类ClassX改成Interface,仍然会出现上面的结果 )

当然有方法可以使你避免出现"inconstant constants"问题。

第一种方法:当你要声明一个编译期间常量的时候,一定要保证此变量不会或者不太可能改变,或者尽量少使用声明为static final的变量。当然这只能治标不能治本,所以我推荐使用第二种方法。

第二种方法:

将变量声明为private,同时声明一个方法来获得此变量的值

//

ClassX.java修改如下:

public

class

ClassX {

private static final int X= 2

;

public

static

int

getX(){

return

X;

}

}

//

ClassTest.java修改如下:

public class

ClassTest{

public static void

main(String[] args){

System.out.println(ClassX.getX());

}

}

此时再改变ClassX.X的值为1,重新编译ClassX.java,而不编译ClassTest,结果就会显示1,而非2。这就避免了"inconstant constants"的问题。

下一篇准备讲一下

《在java中使用循环定义会出现哪些问题?》

马嘉楠

jianan.ma@gmail.com

posted on 2006-10-18 16:27 马嘉楠 阅读(1493) 评论(5)  编辑  收藏 所属分类: Java

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值