`
yangwn
  • 浏览: 75550 次
  • 性别: Icon_minigender_2
  • 来自: 大连
社区版块
存档分类
最新评论
  • icewubin: kimmking 写道icewubin 写道Dollyn 写道 ...
    Java
  • kimmking: icewubin 写道Dollyn 写道我十分怀疑最后一条,很 ...
    Java
  • beneo: 完全忽略了一個好的JVM的優化能力 難道java是C編譯器么 ...
    Java
  • icewubin: Dollyn 写道我十分怀疑最后一条,很多编译器都会自动做类似 ...
    Java
  • Dollyn: 我十分怀疑最后一条,很多编译器都会自动做类似优化吧(不知道JD ...
    Java

java 初学者可能犯的错误

    博客分类:
  • JAVA
阅读更多

在编程过程中,我们可能会遇到一些怪异的情况,这些怪异来源你对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 开发者容易犯的10个错误,是每个java初学者的必看内容

    Java常见异常和错误总结

    作为初学者常犯的一些错误和总结,在开发时候可以作为文档进行查询,能帮助我们快速找到答案

    java初学者福音

    一些面向对象的设计法则 java环境变量设置 Java初学者需掌握的30个基本概念 编写Java程序最容易犯的21种错误

    java是什么.avi

    java学习第一天,讲解java是什么 本章学习要点 了解 Java 语言的版本及特点 了解学习 Java 的方法 掌握 JDK 的安装 掌握环境变量的配置 熟悉 Java 程序的编写、...14.Java初学者容易犯的错误 15.Java程序员学习路线

    xiexu-doc-20230619-生成Java文档注释文件

    具体内容可参阅主页文章:【2023,学点儿新Java-09】Java初学者常会犯的错误总结与解决方案 | Java中的注释类型 | 详细教学:通过命令行 执行 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 v4.0:最佳初学者IDE-开源

    JCppEdit是一个免费的“最佳初学者IDE”,并且是您一站式IDE,可满足您的所有编码需求。 无论您是需要完成Java项目还是提交第一个HTML网页,还是在Java程序中将Java程序执行到Java IDE中时是否需要用C语言编写代码,...

    Java工程师必学系列课程之4--《Java Swing》视频课程

    同时,老师还列举了大量初学者容易犯的错误。避免学员在以后实际开发过程中“踩坑”。  Java Swing技术虽然是一门比较老的技术,但本课程中老师讲解了很多软件开发通用的原理和思想,因此,通过学习本课程,可以让...

    Python新手们容易犯的几个错误总结

    初学者对Python语言不是特别了解的话,又正好有c++,java的语言背景,很容易把++i和i+=1弄混 先来看一个小例子: i=0 mylist=[1,2,3,4,5,6] while i &lt;len(mylist): print(mylist[i]) ++i 这段代码会想当然的...

    程序员为什么还要刷题-typescript-summary:供初学者了解TS的简短摘要!

    成为容易犯错误的地方。 TS 负责编译(转译)代码,并为程序员提供更好的代码编写环境。 写作环境? 是的,当您想在浏览器上运行 TS 代码时,它最终会被转换为 JS。 看下面的例子: 左边是TS代码,右边是转译后的JS...

    【03-流程控制与数组】

    •初学者使用for循环时也容易犯一个错误,他们以为只要在for后的括号内控制了循环循环迭代语句就万无一失,  但实际情况则不是这样的。 for循环的分号 •for 循环圆括号中只有两个分号是必须的,初始化...

    TechEase-Tech-Literacy-App:TechEase-老年人的技术素养应用程序

    顾客角色角色1:初学者,没有智能手机工作方式的经验。 角色2:基本了解智能手机,但不知道如何使用互联网的中级用户。 女神异闻录3:具有中等经验的中级用户,难以安装和卸载应用程序。 女神异闻录4:拥有扎实基础...

Global site tag (gtag.js) - Google Analytics