全世界所有程序员都会犯的错误 - 蔡学镛
虽然挺老的文章,虽然挺小的错误
当年,国际巨星成龙的「龙种」曝光,众人指责他对不起娇妻林凤娇,逼得他出面召开记者会,向世人自白他犯了「全世界所有男人都会犯的错误」。从来没犯过这种错误的我,也因此常常认为自己不是个男人。
虽然没犯过「全世界所有男人都会犯的错误」,但是我倒是曾经犯了「全世界所有程序员都会犯的错误」。不管使用何种语言,全世界所有程序员都一定犯过这种错误,那就是:太依赖编译器,却不知道编译器做了哪些事。
一般来说,越高阶的程序语言,会提供越多语法上的便利,以方便程序撰写,这就俗称为syntactic sugar,我称其为「语法上的甜头」。虽说是甜头,但是如果你未能了解该语法的实质内涵,很可能会未尝甜头,却吃尽苦头。
不久前,我收到一个电子邮件,读者列出下面的Java程序,向我求救。看过这个程序之后,我确定这又是一个「全世界所有程序员都会犯的错误」。
// 程序1
class Singleton {
private static Singleton obj = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return obj;
}
}
// 程序2
public class MyMain {
public static void main(String[] args) {
Singleton obj = Singleton.getInstance();
System.out.println("obj.counter1=="+obj.counter1);
System.out.println("obj.counter2=="+obj.counter2);
}
}
执行结果是:
obj.counter1==1
obj.counter2==0
你有没有被此结果吓一跳?乍看程序代码,你很可能会认为counter1和counter2的值一定会相等,但执行结果显然不是如此。其实,程序1被编译后的程序应该等同于下面的程序3:
// 程序3
class Singleton {
private static Singleton obj;
public static int counter1;
public static int counter2;
static { // 这就是class constructor
// 在进入此class constructor之前,class已经被JVM
// 配置好内存,所有的static field都会被先设定为0,
// 所以此时counter1和counter2都已经是0,且singleton为null
obj = new Singleton(); // 问题皆由此行程序产生
// counter1不会在此被设定为0
counter2 = 0; // counter2再被设定一次0(其实是多此一举)
}
private Singleton() { // 这是instance constructor
counter1++;
counter2++;
}
public static Singleton getInstance() {
return obj;
}
}
这
是因为:当class具有static field,且直接在宣告处透过「=...」的方式设定其值时,编译器会自动将这些叙述依序搬到
class constructor内。同样地,当class具有instance field,且直接在宣告处透过「=...」的
方式设定其值时,编译器会自动将这些叙述依序搬到instance constructor内。
此程序在
class constructor内,还未将static field初始化时(这时候,counter1和counter2都是
0),就呼叫instance constructor,而instance constructor竟然还会去更动
static field的值,使得counter1和counter2都变成1。然后instance constructor执
行完,回到class constructor,再把counter2的值设为0(但是
counter1维持不变)。最后的结果:counter1等于1,counter2等于0。
欲改正程序1,方法有三:
-方法一:将singleton field的宣告调到counter1与counter2 field之后。
这是最好的作法。
-方法二:将counter2=0的宣告中,「=0」的部分删除。这种作法只有在希望
-方法三:将初始化的动作搬到class constructors内,自行撰写,而不依赖
编译器产生。这是最保险的作法。
如何避免犯下「全世界所有程序员都会犯的错误」,我给各位Java程序员
的建议是:
-熟读Java Language Specification
-在有疑问时,使用J2SDK所提供的javap来反组译Java Bytecode,直接观察
编译后的结果。
下面是我用javap来反组译程序1的示范:
C:\>javap -c -classpath . Singleton
Compiled from MyMain.java
class Singleton extends java.lang.Object {
public static int counter1;
public static int counter2;
public static Singleton getInstance();
static {};
}
Method Singleton()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 getstatic #2 <Field int counter1>
7 iconst_1
8 iadd
9 putstatic #2 <Field int counter1>
12 getstatic #3 <Field int counter2>
15 iconst_1
16 iadd
17 putstatic #3 <Field int counter2>
20 return
Method Singleton getInstance()
0 getstatic #4 <Field Singleton obj>
3 areturn
Method static {}
0 new #5 <Class Singleton>
3 dup
4 invokespecial #6 <Method Singleton()>
7 putstatic #4 <Field Singleton obj>
10 iconst_0
11 putstatic #3 <Field int counter2>
14 return
其实Java的syntactic sugar并不算多,C#的syntactic sugar才真的是无所不在,
也因此C#的初学者更容易犯了「全世界所有程序员都会犯的错误」。许多C#的书都会一边介绍C#语法,一边介绍编译之后MSIL(.NET的中间语言,类似Java的Bytecode)的结果,然而Java的书却鲜少这么做。
虽说是「全世界所有程序员都会犯的错误」,但是这不代表你犯了此错误之后,仍可以同爱借钱的曹启泰一般地「抬头挺胸、理直气壮」。只要有心,其实这一类的错误仍是可以避免的。
本文作者:蔡学镛
文章出处:Sleepless 2.0
发表日期:03/10/2003
Comments
Many thanks aledesign.it for appreciating our get the job done!
Posted by: Bart Rumpel | August 10, 2012 10:08 PM
How to remove your Twitter Direct Messages...
Posted by: GST Audit Melbourne | August 20, 2012 5:54 PM
You might have shared the best collection ever. "Sleek Minimal PSD Website" is truly an incredible PSD.
Posted by: marketing plan | August 30, 2012 1:46 PM
What i do not realize is actually how you're not actually much more well-liked than you might be right now. You are so intelligent. You realize therefore significantly relating to this subject, produced me personally consider it from numerous varied angles. Its like women and men aren't fascinated unless it is one thing to do with Lady gaga! Your own stuffs outstanding. Always maintain it up!
Posted by: how do i get rid of mice | August 31, 2012 6:55 PM
It is helpful. Many thanks for sharing.
Posted by: Lydia Chauca | September 4, 2012 12:25 PM