- 浏览: 132062 次
- 性别:
- 来自: ...
文章分类
最新评论
近日无意中看到一道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 2124看到一道很有意思的编程题:大厅里有64盏灯,每盏灯都编 ... -
一道字符串截取的编程题
2010-03-11 10:52 2320最近接触到一道字符串截取的编程题:编写一个截取字符串的 ... -
一道多线程趣味热身题
2010-02-28 18:01 1960保持对知识点或者技术的熟悉度对于程序员至关重要,要学会 ... -
疑似Google多线程面试题的Java实现
2010-02-24 17:39 4964来到一个完全陌生的地方,即将一切从新开始,内心兴奋又忐 ... -
Mina的线程池实现分析(2)
2010-02-10 17:31 4589分析了I/O事件的存储,下面看看多个Worker同时工 ... -
Mina的线程池实现分析(1)
2010-02-10 17:28 11632线程池是并发应用中,为了减少每个任务调用的开销增强性能 ... -
多线程基础总结十一--ConcurrentLinkedQueue
2010-02-03 17:52 12952ConcurrentLinkedQueue充分使用了a ... -
LinkedBlockingQueue应用--生产消费模型简单实现
2010-01-29 20:45 8212之前介绍时LinkedBlockingQueue提到了 ... -
多线程基础总结十--LinkedBlockingQueue
2010-01-28 14:33 15442随着多线程基础总结的增多,却明显的感觉知道的越来越少, ... -
多线程基础总结九--Mina窥探(1)
2010-01-21 23:46 5454一直以来的多线程的基础总结都是脱离应用的,但是要说多线 ... -
多线程基础总结八--ReentrantReadWriteLock
2010-01-15 23:22 7557说到ReentrantReadWriteLock,首先 ... -
多线程基础总结七--ReentrantLock
2010-01-09 23:17 7730之前总结了部分无锁机制的多线程基础,理想的状态当然是利 ... -
关于atomic问题的一点理解
2009-12-30 16:42 2486之前看到一个帖子是关于atomic使用的,当时没有仔细 ... -
多线程基础总结六--synchronized(2)
2009-12-18 18:45 1911早在总结一时,我就尽量的把synchronized的重点 ... -
多线程基础总结五--atomic
2009-12-17 19:46 3594在简单介绍java.util.c ... -
多线程基础总结四--ThreadLocal
2009-12-16 19:48 2763说到ThreadLocal,首先 ... -
多线程基础总结三--volatile
2009-12-15 20:09 2583前面的两篇总结简 ... -
多线程基础总结二--Thread
2009-12-12 23:27 2711对于Thread来说 ... -
多线程基础总结一--synchronized(1)
2009-12-12 23:23 3117最近写关于并发的小应 ... -
由destory-method引发的IOC容器设计的思考
2009-12-07 16:51 1724第一次读Spring的源 ...
相关推荐
Java标准版(J2SE,Java 2 Platform Standard Edition)是Java开发平台的核心部分,用于构建和运行桌面应用。在面试中,对于J2SE的掌握程度常常是评估候选人技术能力的重要指标。以下是一些J2SE面试中常见的知识点:...
J2SE基础知识是每一个Java开发者必须掌握的核心技能。以下将详细阐述J2SE中的关键概念和技术。 1. **Java语言基础** - **语法**:包括变量声明、数据类型(基本类型和引用类型)、运算符、流程控制(如if语句、for...
Java Standard Edition (J2SE) 是 Java 语言的核心部分,主要用于桌面应用开发。面试中对 J2SE 的考察涵盖了...这些知识点构成了 J2SE 面试的基础,深入理解和掌握它们将有助于在面试中展现出扎实的 Java 编程功底。
标题中提到的"J2SE基础",意味着这份资源涵盖了Java语言的基础概念和语法,适合初学者入门。描述中提到的"myeclipse课本源码"表明,这些资料可能来自于一本使用MyEclipse集成开发环境的教科书,MyEclipse是流行的...
Java Standard Edition(J2SE)是Java平台的核心部分,它为开发和运行桌面应用程序、...通过分析和学习这些J2SE基础源代码,开发者不仅可以掌握Java编程的基础,还能深入理解其工作原理,提升编程技能和问题解决能力。
J2SE基础知识梳理总结,完整版;可以用来回顾温习!
【J2SE基础学习笔记】 Java 作为一门高级编程语言,源自Sun公司的Green项目,原本设计用于家用电子产品,但由于C++的复杂性和安全性问题,Sun公司基于C++开发出了Oak,即Java的前身。1995年Java正式发布,随后迅速...
本资源包是一套全面的J2SE(Java Standard Edition)基础教程,适合初学者深入学习Java编程。它包含了从基本语法到高级特性的多个主题,帮助初学者逐步掌握Java编程的核心知识。 1. **第二章 Java语法基础**:这...
推箱子游戏,作为一款经典的逻辑益智游戏,其源码程序是学习Java Standard Edition (J2SE)基础知识的绝佳实践案例。在这个程序中,开发者会用到Java的基础类库,包括图形用户界面(GUI)、事件处理、数据结构以及...
本教程“J2SE_核心技术基础”针对Java初学者,通过详实的例子代码帮助学习者快速掌握Java编程的基本概念和技巧。 一、Java语言基础 Java是一种面向对象的编程语言,其设计目标是简洁、安全、高效。学习Java首先要...
《J2SE基础全套资料》是一份全面涵盖Java标准版(J2SE)基础知识的资源集合,适合初学者和有经验的开发者进行深入学习。这份资料包括了集合、线程、网络、图形用户界面(GUI)、数组、字符串等多个关键主题,旨在...
为了在Java开发者岗位上取得成功,掌握扎实的J2SE(Java Standard Edition)基础知识至关重要。本资料集合了多个来源的Java面试题,涵盖了广泛的知识点,旨在帮助求职者充分准备面试。 一、Java基础 1. 类与对象:...
tsp2c J2se基础 迪锐软件 迪锐软件IT实训 迪锐软件J2SE基础讲解
Java 2 Platform, Standard Edition (J2SE) 是 Java 语言的核心部分,它提供了开发和运行桌面应用程序、服务器端应用程序以及嵌入式系统的...通过练习题和答案,可以检验和巩固对这些概念的理解,进一步提升编程技能。
Java 2 Platform, Standard Edition(J2SE)是Java开发平台的一个版本,主要面向桌面应用和服务器端开发。它提供了核心的Java编程环境,包括...在J2SE的学习过程中,深入理解这些基础概念将对提升编程能力大有裨益。
本课程旨在深入讲解J2SE的基础知识,帮助学习者掌握Java编程的基础技能,为后续的高级学习和软件开发打下坚实的基础。 1. **Java程序设计基础** - **基本语法和程序设计方法**:学习Java程序的基本结构,包括变量...
### J2SE基础复习知识点详解 #### 一、Java环境搭建与配置 - **环境配置**: Java环境配置是学习Java编程的基础步骤。主要包括JDK(Java Development Kit)的安装与配置。 - **JRE与JDK的区别**: JRE(Java Runtime ...