在编程过程中,我们可能会遇到一些怪异的情况,这些怪异来源你对JAVA的熟悉程度。
请看下面的代码,千万不要马上运行,仔细想想后再运行。
在执行java程序的顺序是这样的:
1.如果类中有static变量,按照代码的前后顺序执行static成员变量。
特别注意的是staitc的初始化只能执行一次,只能在第一次类装载时,才会执行。之后,就不在执行。
2.再执行非static变量
3.再执行构造方法
来看看有意思的东西。你肯定对static的变量更加清楚:
//程序1
class Singleton {
private static Singleton obj = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton() {
System.out.println("counter1:"+counter1);
System.out.println("counter2:"+counter2);
counter1++;
counter2++;
System.out.println("counter1:"+counter1);
System.out.println("counter2:"+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);
}
}
请问执行结果是什么?
你有没有被此结果吓一跳吧?好好分析一下。
其实,程序1被编译后的程序应该等同于下面的程序3:
class Singleton {
private static Singleton obj;
public static int counter1;
public static int counter2;
// 这就是class constructor
static {
// 在进入此class constructor之前,class已经被JVM配置好内存,
// 所有的static field都会被先设定为0,所以有的reference都为null
// 所以此时counter1和counter2都已经是0,且singleton为null
// 问题皆由此行程序产生,在此调用了构造方法
obj = new Singleton();
// counter1不会在此被设定为0
counter2 = 0; // counter2再被设定一次0(其实是多此一举)
}
private Singleton() { // 这是instance constructor
System.out.println("counter1:"+counter1);
System.out.println("counter2:"+counter2);
counter1++;
counter2++;
System.out.println("counter1:"+counter1);
System.out.println("counter2:"+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并不算多,
Tk2v%)gVIgT3
教SdM]D?hvV
U!5}xs软ZC=v网1iz78C
C#的syntactic sugar才真的是无所不在,
也因此C#的初学者更容易犯了「全世界所有程序员都会犯的错误」。许多C#的书都会一边介绍C#语法,一边介绍编译之后MSIL(.NET的中间语言,类似Java的Bytecode)的结果,然而Java的书却鲜少这么做。
虽说是「全世界所有程序员都会犯的错误」,但是这不代表你犯了此错误之后,仍可以同爱借钱的曹启泰一般地「抬头挺胸、理直气壮」。只要有心,其实这一类的错误仍是可以避免的。
分享到:
相关推荐
这个资源是java 开发者容易犯的10个错误,是每个java初学者的必看内容
作为初学者常犯的一些错误和总结,在开发时候可以作为文档进行查询,能帮助我们快速找到答案
一些面向对象的设计法则 java环境变量设置 Java初学者需掌握的30个基本概念 编写Java程序最容易犯的21种错误
java学习第一天,讲解java是什么 本章学习要点 了解 Java 语言的版本及特点 了解学习 Java 的方法 掌握 JDK 的安装 掌握环境变量的配置 熟悉 Java 程序的编写、...14.Java初学者容易犯的错误 15.Java程序员学习路线
具体内容可参阅主页文章:【2023,学点儿新Java-09】Java初学者常会犯的错误总结与解决方案 | Java中的注释类型 | 详细教学:通过命令行 执行 Java中特有的文档注释
java编程规则,适合新手做参考,内容包括面向对象的特殊性和初学者编程容易犯下的错误
1.6.3 初学者容易犯的错误 18 1.7 垃圾回收机制 20 1.8 何时开始使用IDE工具 21 学生提问:老师,我想学习Java编程,到底是学习Eclipse好呢,还是学习JBuilder好呢? 21 1.9 本章小结 22 本章练习 22 第2章 ...
JCppEdit是一个免费的“最佳初学者IDE”,并且是您一站式IDE,可满足您的所有编码需求。 无论您是需要完成Java项目还是提交第一个HTML网页,还是在Java程序中将Java程序执行到Java IDE中时是否需要用C语言编写代码,...
同时,老师还列举了大量初学者容易犯的错误。避免学员在以后实际开发过程中“踩坑”。 Java Swing技术虽然是一门比较老的技术,但本课程中老师讲解了很多软件开发通用的原理和思想,因此,通过学习本课程,可以让...
初学者对Python语言不是特别了解的话,又正好有c++,java的语言背景,很容易把++i和i+=1弄混 先来看一个小例子: i=0 mylist=[1,2,3,4,5,6] while i <len(mylist): print(mylist[i]) ++i 这段代码会想当然的...
成为容易犯错误的地方。 TS 负责编译(转译)代码,并为程序员提供更好的代码编写环境。 写作环境? 是的,当您想在浏览器上运行 TS 代码时,它最终会被转换为 JS。 看下面的例子: 左边是TS代码,右边是转译后的JS...
•初学者使用for循环时也容易犯一个错误,他们以为只要在for后的括号内控制了循环循环迭代语句就万无一失, 但实际情况则不是这样的。 for循环的分号 •for 循环圆括号中只有两个分号是必须的,初始化...
顾客角色角色1:初学者,没有智能手机工作方式的经验。 角色2:基本了解智能手机,但不知道如何使用互联网的中级用户。 女神异闻录3:具有中等经验的中级用户,难以安装和卸载应用程序。 女神异闻录4:拥有扎实基础...