- 浏览: 130489 次
- 性别:
- 来自: ...
文章分类
最新评论
近日无意中看到一道Java基础题,号称在接受测试的1000名人中,仅有1.5%的程序员或者项目经理完全正确。原题是3个小程序并要求按顺序答题,我修改了一下弄成一个例子。个人觉得虽然是三个稍微不同的例子,但是考察的本质都是一样,只要了解了原理就是100个变种也还是表达一个意思。先来看看例子:
这个看起来很孔乙己的例子的输出是什么呢?
下面说说我个人理解的过程:
(1).Child的main(...)方法内的new语句会触发JVM对类的加载。因为Child是继承自Father的,所以会先加载Father.class.加载过程会执行两步:
(a).所有的static数据域初始化为默认值(0,false,null),所以Father内的static的n首先默认值为0.
(b).执行所有的static域的初始化语句或者初始化块(按出现的顺序),此时Father内static块的打印语句执行:super-->static,并且static的n赋值语句n = 10执行。
父类的方法表建立后算是父类加载完毕,接着会加载子类Child,执行的过程与父类类似。因为子类没有static的数据域,所以仅仅static的初始化块内的打印语句执行:child-->static。
这也解释了为什么static的初始化只有一次,因为它仅仅在类加载的时候执行而与实例化没有关系。
(2).需要的类加载完毕之后new Child()开始使用类构造器创建实例。我理解的构造器的执行有以下几步:
(a)所有数据域初始化为默认值(0,false,null)。
(b)如果有父类则先执行父类的构造器。
(c)按声明出现的顺序执行所有数据域的初始化语句或者初始化块
(d)如果构造器主体的第一句调用了另一个构造器,则执行第二个构造器的主体(非super)
(e)执行构造器的主体部分
现在对号入座看看此例的情况,首先Child的数据域初始化为默认值:n=0, x=0.接着由于继承Father如果没有在构造器主体显示使用super(...)调用Father有参或者无参的构造器,则隐式的去调用无参的构造器。此时开始了Father构造器的执行过程:
首先Father的所有数据域初始化为默认值:x=0;接着是Father的父类Object的构造器执行;完毕后Fahter开始数据域的初始化语句的执行:x=100;Father构造器主体没有调用自身的其他的构造器,第四步跳过;最后一步就是Father构造器的主体:先执行打印super's x=100,接着调用age().这里Father和Child的方法表内都有age()方法,但是因为要创建的是Child实例,所以JVM会动态的绑定到Child的age()方法,所以执行了System.out.println("age=" + n);而此时n仅仅是默认值0,所以打印age=0.
父类的构造器调用完毕之后,继续Child构造器执行的第三步:数据域的初始化语句和初始化块,此时按照声明的顺序n=20,接着在初始化块中n=30,然后就是x=200;Child的第四步满足条件,所以会执行Child(String s)有参构造器的主体,打印The other constructor;最后一步执行本构造器的主体,打印child constructor body: 30。
至此实例化完毕,main函数内调用了实例的printX()方法执行System.out.println("x=" + x)语句。这里虽然Father和Child都有public的x。但是由于语句内没有显示使用super.x,则会隐式的使用this.x。这样打印x=200.完整的执行结果如下:
super-->static
child-->static
super's x=100
age=0
The other constructor
child constructor body: 30
x=200
这个小题目虽然是基础题,但是涉及到整个实例化的过程,相对还是有些迷惑的。原题拆分成三个所以很容易得到正确的答案,很遗憾的找不到原题了,所以设计了这个看起来很混乱的小例子来分析。很大的体会是之前觉得知道JVM原理也没什么用处,现在发现要想深入的掌握一门语言,基础的原理是需要知道的。比如volatile为什么禁用了寄存器的写入直接写回内存就能保证了可见性?因为JVM中寄存器和栈都是线程独享的,而堆和方法区是线程共享的。这些了解的深刻,将有助于提高代码的正确率。
/** * * @author: yanxuxin * @date: 2010-1-18 */ public class Child extends Father{ static { System.out.println("child-->static"); } private int n = 20; { n = 30; } public int x = 200; public Child() { this("The other constructor"); System.out.println("child constructor body: " + n); } public Child(String s) { System.out.println(s); } public void age() { System.out.println("age=" + n); } public void printX() { System.out.println("x=" + x); } public static void main(String[] args) { new Child().printX(); } } class Father { static { System.out.println("super-->static"); } public static int n = 10; public int x = 100; public Father() { System.out.println("super's x=" + x); age(); } public void age(){ System.out.println("nothing"); } }
这个看起来很孔乙己的例子的输出是什么呢?
下面说说我个人理解的过程:
(1).Child的main(...)方法内的new语句会触发JVM对类的加载。因为Child是继承自Father的,所以会先加载Father.class.加载过程会执行两步:
(a).所有的static数据域初始化为默认值(0,false,null),所以Father内的static的n首先默认值为0.
(b).执行所有的static域的初始化语句或者初始化块(按出现的顺序),此时Father内static块的打印语句执行:super-->static,并且static的n赋值语句n = 10执行。
父类的方法表建立后算是父类加载完毕,接着会加载子类Child,执行的过程与父类类似。因为子类没有static的数据域,所以仅仅static的初始化块内的打印语句执行:child-->static。
这也解释了为什么static的初始化只有一次,因为它仅仅在类加载的时候执行而与实例化没有关系。
(2).需要的类加载完毕之后new Child()开始使用类构造器创建实例。我理解的构造器的执行有以下几步:
(a)所有数据域初始化为默认值(0,false,null)。
(b)如果有父类则先执行父类的构造器。
(c)按声明出现的顺序执行所有数据域的初始化语句或者初始化块
(d)如果构造器主体的第一句调用了另一个构造器,则执行第二个构造器的主体(非super)
(e)执行构造器的主体部分
现在对号入座看看此例的情况,首先Child的数据域初始化为默认值:n=0, x=0.接着由于继承Father如果没有在构造器主体显示使用super(...)调用Father有参或者无参的构造器,则隐式的去调用无参的构造器。此时开始了Father构造器的执行过程:
首先Father的所有数据域初始化为默认值:x=0;接着是Father的父类Object的构造器执行;完毕后Fahter开始数据域的初始化语句的执行:x=100;Father构造器主体没有调用自身的其他的构造器,第四步跳过;最后一步就是Father构造器的主体:先执行打印super's x=100,接着调用age().这里Father和Child的方法表内都有age()方法,但是因为要创建的是Child实例,所以JVM会动态的绑定到Child的age()方法,所以执行了System.out.println("age=" + n);而此时n仅仅是默认值0,所以打印age=0.
父类的构造器调用完毕之后,继续Child构造器执行的第三步:数据域的初始化语句和初始化块,此时按照声明的顺序n=20,接着在初始化块中n=30,然后就是x=200;Child的第四步满足条件,所以会执行Child(String s)有参构造器的主体,打印The other constructor;最后一步执行本构造器的主体,打印child constructor body: 30。
至此实例化完毕,main函数内调用了实例的printX()方法执行System.out.println("x=" + x)语句。这里虽然Father和Child都有public的x。但是由于语句内没有显示使用super.x,则会隐式的使用this.x。这样打印x=200.完整的执行结果如下:
super-->static
child-->static
super's x=100
age=0
The other constructor
child constructor body: 30
x=200
这个小题目虽然是基础题,但是涉及到整个实例化的过程,相对还是有些迷惑的。原题拆分成三个所以很容易得到正确的答案,很遗憾的找不到原题了,所以设计了这个看起来很混乱的小例子来分析。很大的体会是之前觉得知道JVM原理也没什么用处,现在发现要想深入的掌握一门语言,基础的原理是需要知道的。比如volatile为什么禁用了寄存器的写入直接写回内存就能保证了可见性?因为JVM中寄存器和栈都是线程独享的,而堆和方法区是线程共享的。这些了解的深刻,将有助于提高代码的正确率。
发表评论
文章已被作者锁定,不允许评论。
-
一道位操作的趣味编程题
2010-03-14 10:50 2081看到一道很有意思的编程题:大厅里有64盏灯,每盏灯都编 ... -
一道字符串截取的编程题
2010-03-11 10:52 2273最近接触到一道字符串截取的编程题:编写一个截取字符串的 ... -
一道多线程趣味热身题
2010-02-28 18:01 1913保持对知识点或者技术的熟悉度对于程序员至关重要,要学会 ... -
疑似Google多线程面试题的Java实现
2010-02-24 17:39 4904来到一个完全陌生的地方,即将一切从新开始,内心兴奋又忐 ... -
Mina的线程池实现分析(2)
2010-02-10 17:31 4518分析了I/O事件的存储,下面看看多个Worker同时工 ... -
Mina的线程池实现分析(1)
2010-02-10 17:28 11573线程池是并发应用中,为了减少每个任务调用的开销增强性能 ... -
多线程基础总结十一--ConcurrentLinkedQueue
2010-02-03 17:52 12836ConcurrentLinkedQueue充分使用了a ... -
LinkedBlockingQueue应用--生产消费模型简单实现
2010-01-29 20:45 8134之前介绍时LinkedBlockingQueue提到了 ... -
多线程基础总结十--LinkedBlockingQueue
2010-01-28 14:33 15369随着多线程基础总结的增多,却明显的感觉知道的越来越少, ... -
多线程基础总结九--Mina窥探(1)
2010-01-21 23:46 5394一直以来的多线程的基础总结都是脱离应用的,但是要说多线 ... -
多线程基础总结八--ReentrantReadWriteLock
2010-01-15 23:22 7508说到ReentrantReadWriteLock,首先 ... -
多线程基础总结七--ReentrantLock
2010-01-09 23:17 7674之前总结了部分无锁机制的多线程基础,理想的状态当然是利 ... -
关于atomic问题的一点理解
2009-12-30 16:42 2435之前看到一个帖子是关于atomic使用的,当时没有仔细 ... -
多线程基础总结六--synchronized(2)
2009-12-18 18:45 1866早在总结一时,我就尽量的把synchronized的重点 ... -
多线程基础总结五--atomic
2009-12-17 19:46 3546在简单介绍java.util.c ... -
多线程基础总结四--ThreadLocal
2009-12-16 19:48 2716说到ThreadLocal,首先 ... -
多线程基础总结三--volatile
2009-12-15 20:09 2522前面的两篇总结简 ... -
多线程基础总结二--Thread
2009-12-12 23:27 2664对于Thread来说 ... -
多线程基础总结一--synchronized(1)
2009-12-12 23:23 3067最近写关于并发的小应 ... -
由destory-method引发的IOC容器设计的思考
2009-12-07 16:51 1680第一次读Spring的源 ...
相关推荐
J2SE基础知识大全 J2SE基础知识大全 J2SE基础知识大全
J2SE面试题总汇,多年面试经验,J2SE系列面试题汇总,包你拿到OFFER
Java J2SE基础篇各章习题汇总
J2SE常见面试题总结,以及详细答案,好不容易整理出来的希望有用
J2SE面试题 经典 背过之后 各种笔试轻松过
J2SE基础源代码
关于j2se基础文档和源代码 初学者 很好的资源
J2SE基础知识梳理总结,完整版;可以用来回顾温习!
J2SE基础相关内容J2SE基础相关内容
是myeclipse课本源码,里面有详细的注解。希望能给大家带来帮助
J2SE阶段测试题+答案 J2SE阶段测试题+答案 J2SE阶段测试题+答案
推箱子游戏是j2se基础类知识的充分综合与应用,通过这个程序源码的学习,能更好的帮助我们掌握j2se基础的相关概念和各种类的使用
J2SE基础复习提纲!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
非常基础的学习笔记,希望对你学习基础有用!
tsp2c J2se基础 迪锐软件 迪锐软件IT实训 迪锐软件J2SE基础讲解
一份网上找的j2se基本教程 初学者挺有用的
j2se中部分概念的重要区别 接口,抽象类等概念,能更好的建立编程基础
包含J2SE的所有基础知道,如集合,线程,网络,GUI,数组,字符串等。
实例按照实例顺序存放,即:实例1、实例2……。读者能很方便的查找到所需的实例。 各实例目录下会有一个关于实例运行的简单说明文件:readme.txt。...无论有无说明文件,请读者仍然以书本上介绍的运行方法为基础。
java基础(j2se代码)java基础(j2se代码)java基础(j2se代码)java基础(j2se代码)java基础(j2se代码)java基础(j2se代码)java基础(j2se代码)