一、判斷題(正確的描述打鉤,錯(cuò)誤的打叉,每題 1 分,共 10 分)
1. 一個(gè) Java 源文件中只能定義一個(gè)公有類,并且類名必須與文件名一致。(對(duì))
2. 通過(guò)在程序中引入“ import javax.swing.*; ”語(yǔ)句,就可以使用 javax.swing 包中的所有類(包括其嵌套的子包中的類)。 (錯(cuò))
要使用嵌套子包中的類,必須再次添加 import 子句
3. J2SE 的功能是 J 2 ME 的一個(gè)子集。(錯(cuò))
4. Java 字節(jié)碼只有被裝入到內(nèi)存中之后,才能被執(zhí)行。(對(duì))
在當(dāng)前計(jì)算機(jī)體系結(jié)構(gòu)之下,CPU無(wú)法直接讀取保存于磁盤上的程序文件,這些文件所包容的指令必須被裝入到內(nèi)存之后,CPU才能讀取。
5. Swing是一套GUI組件,采用了新的思路設(shè)計(jì)Java應(yīng)用程序的界面,它完全地替換掉了原有的AWT。(錯(cuò))
Swing仍然使用AWT的事件模型,并非完全拋棄。
值得注意的NetBeans 6.9.1完成最新的更新組件后,創(chuàng)建Java桌面應(yīng)用程序時(shí),有了一個(gè)新的提示消息框:
看來(lái)Oracle也知道Java在桌面上沒(méi)戲,干脆放棄了對(duì)Swing框架的進(jìn)一步開發(fā)。已經(jīng)存在這么多年的Swing框架被“新主人”拋棄,真不知道該說(shuō)什么。
6. 在編程時(shí)編寫過(guò)多的注釋,會(huì)使編譯之后生成的程序文件變大。(錯(cuò))
放心, 編譯器會(huì)刪除所有的注釋。所以,哪怕你在程序中寫了一部長(zhǎng)篇小說(shuō),也不會(huì)拖慢程序的運(yùn)行速度。
7. Java Applet 可運(yùn)行于瀏覽器中,這意味著瀏覽器本身就可以直接執(zhí)行 Applet 程序。(錯(cuò))
瀏覽器本身只認(rèn)識(shí)HTML,它對(duì)其他類型信息的支持,比如Flash,Applet,Silverlight應(yīng)用程序等都是通過(guò)啟動(dòng)本地計(jì)算機(jī)安裝的附加插件來(lái)實(shí)現(xiàn)的。
8. JDK 5.0新增的枚舉類型( enum )是一個(gè)原始數(shù)據(jù)類型。(錯(cuò))
9. Java 中規(guī)定所有的方法都必須放到某個(gè)類中,不存在獨(dú)立于類的方法。(對(duì))
10. 面向?qū)ο筌浖到y(tǒng)設(shè)計(jì)師們經(jīng)常用到的 UML 是一種編程語(yǔ)言。(錯(cuò))
二、簡(jiǎn)答題(請(qǐng)將答案寫到答題紙上,注明題號(hào)。每題 4 分,共 40 分)
1. 現(xiàn)代編程語(yǔ)言有兩種主要的類型:一種是“編譯型 ”的,比如 C ,程序源碼必須經(jīng)過(guò)編譯才能運(yùn)行;另一種是“解釋型 ”的,這種類型的語(yǔ)言(比如早期的 Basic )通常都擁有一個(gè)交互環(huán)境,用戶輸入一句代碼計(jì)算機(jī)就執(zhí)行一句代碼。
Java 屬于上述哪種類型?如果你認(rèn)為 Java 不屬于上述任何一種類型,那么它是不是一種新的編程語(yǔ)言類型?
Java源程序需要編譯,但運(yùn)行時(shí)需要即時(shí)“解釋”為本地CPU能執(zhí)行的機(jī)器指令,所以它不屬于“純”的編譯型或解釋型,而是一種混合類型。
有的同學(xué)很“絕”,他說(shuō):“Java是一種面向?qū)ο箢愋偷木幊陶Z(yǔ)言”。這句話還真是對(duì)的,我也給了分。
2. Java 中有兩個(gè)關(guān)鍵字: void 和 null ,它們有什么區(qū)別?
這題100%的學(xué)生都能答對(duì),純粹是送分題。
3. 簡(jiǎn)述“類(Class)”、“類庫(kù)(Class Library)”、“包(Package)”、“ Jar 文件”這四個(gè)概念間的聯(lián)系。
類庫(kù)其實(shí)是一個(gè)“統(tǒng)稱”,它是類的集合。包則是類庫(kù)的組織形式,它類似于C++中的namespace,可以解決類的同名問(wèn)題。
Jar則是一個(gè)采用Zip格式壓縮的文件包 ,主要是為了方便Java組件的復(fù)用,簡(jiǎn)化Java應(yīng)用程序的部署和發(fā)布。Jar包中可以保存任意類型的多個(gè)文件和多級(jí)嵌套的
文件夾。比如JDK就包容了不少Jar壓縮包,你可以使用解壓縮工具(比如WinRAR)去查看它的內(nèi)容。可以把Jar文件看成是Java類庫(kù)的一個(gè)物理“載體”,之所以稱
其為“物理”的,是因?yàn)槲覀兛梢栽谟?jì)算機(jī)中直接“看見”并“操作”它。
在學(xué)習(xí)和探索知識(shí)的過(guò)程中,有的時(shí)候就必須注意清晰地分清各個(gè)似是而非的概念,這道題有好幾個(gè)同學(xué)就把這幾個(gè)概念間的關(guān)系弄亂了。
4. 面向?qū)ο蟮某绦蛟谶\(yùn)行時(shí)會(huì)創(chuàng)建多個(gè)對(duì)象,這些對(duì)象之間通常可以相互 “ 發(fā)送消息 ” ,談?wù)勀銓?duì) “ 對(duì)象之間發(fā)送消息 ” 這句話的理解,并編寫若干
句示例代碼展示對(duì)象之間發(fā)送消息的具體編程實(shí)現(xiàn)方法。
對(duì)象間的消息發(fā)送最基本的手段就是:一個(gè)對(duì)象執(zhí)有另一個(gè)對(duì)象的引用,然后,通這個(gè)對(duì)象引用調(diào)用對(duì)方的方法,存取對(duì)方的字段(或?qū)傩裕?
對(duì)象互發(fā)消息,其本質(zhì)目的就是為了交換信息。
除了上述這兩種直接方式之外,我們還可以通過(guò)一些第三方的媒價(jià)來(lái)實(shí)現(xiàn)對(duì)象間的信息交換:比如利用類的靜態(tài)字段,利用內(nèi)存映射文件,利用數(shù)據(jù)庫(kù),利用
外部共享的“物理”文件等。
我在《.NET 4.0面向?qū)ο缶幊搪?》的第14章《對(duì)象間的協(xié)作與信息交換》中介紹了七八種單個(gè)、多個(gè)對(duì)象間消息發(fā)送和信息交換的方式,雖然是用C#實(shí)現(xiàn)
的,但其思想完全可用于Java中。
掌握這些編程技巧,對(duì)開發(fā)非常關(guān)鍵。
5. 加法運(yùn)算符“+”可以施加于原始數(shù)值類型(比如 int )的變量,但我們發(fā)現(xiàn)一些對(duì)象類型(比如 Integer ) 的變量,也支持“+”運(yùn)算:
Integer v1 = 100;
Integer v2 = 200;
System.out.println(v1+v2 ); //輸出: 300
這看上去好象 Integer 類型重載了 “ + ” 法運(yùn)算符,一些編程語(yǔ)言比如 C ++可以為特定的類重載運(yùn)算符,但 Java 本身并不支持運(yùn)算符重載這一特性。依你的
理解或猜測(cè), Java 是采用什么方法讓兩個(gè) Integer 對(duì)象可以直接“相加”的?
上面代碼首先是調(diào)用Integer.valueOf方法將整數(shù)轉(zhuǎn)換為Integer對(duì)象,當(dāng)兩個(gè)Integer對(duì)象相加時(shí),其實(shí)是使用Integer.intValue方法取出其所“包裝”的整數(shù)值,相加之
后,再將結(jié)果輸出。
上述結(jié)論是通過(guò)分析javap反匯編示例代碼得到的。
諸如Java和C#之類的編程語(yǔ)言,許多現(xiàn)象已不僅僅是語(yǔ)法問(wèn)題,編譯器在其中起了很重要的作用。我在課堂上已經(jīng)介紹過(guò)如何使用javap去反匯編.class文件,通
過(guò)閱讀Java編譯器生成的字節(jié)碼指令去弄明白java編譯器在后臺(tái)玩的把戲。
這道題主要是引導(dǎo)學(xué)生學(xué)編程語(yǔ)言不要僅學(xué)語(yǔ)法,要學(xué)會(huì)利用工具透過(guò)表面現(xiàn)象看本質(zhì)。
有的學(xué)生猜測(cè)Java可能是設(shè)計(jì)了另外的對(duì)象負(fù)責(zé)完成“+”運(yùn)算,雖然與Java實(shí)際情況不符,但這個(gè)思路是可行的,我同樣給分。
6. 在一個(gè)很大的循環(huán)(比如循環(huán)10000000 次)中,如果需要進(jìn)行字串的連接操作,通常我們者使用StringBuffer 取代String ,解釋這么做的理由。
絕大部分同學(xué)都答對(duì)了。但有的同學(xué)說(shuō):在大循環(huán)中使用String,會(huì)“浪費(fèi)”大量的內(nèi)存,這句話是不準(zhǔn)確的,因?yàn)镴ava有一個(gè)垃圾收集機(jī)制,在合適的時(shí)機(jī)會(huì)
出來(lái)回收不用的String對(duì)象,因此,這些內(nèi)存不能說(shuō)是被“浪費(fèi)”的,我們唯一可以說(shuō)的是:
由于String對(duì)象是只讀的,使用它大量字串連接操作,將導(dǎo)致頻繁的內(nèi)存分配操作 ,這會(huì)嚴(yán)重地影響程序性能。
7. 現(xiàn)有兩個(gè)長(zhǎng)度不同的數(shù)組:
int[] arr1 = new int[10];
int[] arr2 = new int[100];
那么以下語(yǔ)句能通過(guò)編譯嗎?
arr1 = arr2;
如果你認(rèn)為可以通過(guò)編譯,得到這個(gè)結(jié)論的依據(jù)是什么?如果不能通過(guò)編譯,你認(rèn)為其原因是什么?
可以通過(guò)編譯。此題考核學(xué)生知不知道 Java 中的數(shù)組屬于對(duì)象類型,知不知道對(duì)象變量相互賦值的含義。
8. 請(qǐng)看以下代碼 :
double d1=100.1;
double d2=1001/10;
System.out.println(d1==d2); // 輸出 : false
明明 d1 與 d2 是相等的 , 為什么程序運(yùn)行時(shí)會(huì)輸出一個(gè)讓人意外的結(jié)果 : false ?
很多同學(xué)都指出1001/10 其實(shí)是“整除”,得到100 ,再轉(zhuǎn)為double ,所以d2=100.0 ,自然d1==d2 返回false 了。
由于我在課堂上曾展示過(guò)計(jì)算機(jī)難于精確表示雙精度數(shù),比較兩個(gè)double 變量是否相等應(yīng)該檢測(cè)其差的絕對(duì)值是否在某個(gè)可以容忍的誤差范圍內(nèi),所以幾個(gè)同
學(xué)就往這個(gè)思路上去想了。
對(duì)這種情況,我沒(méi)扣分。
9. 當(dāng)使用多個(gè) catch 語(yǔ)句捕獲多個(gè)異常時(shí), Java 規(guī)定捕獲 Exception 的 catch 語(yǔ)句必須排在最后,如下所示:
try { …… }
catch(ClassCastException ex){…… }
catch(NumberFormatException ex){…… }
catch(Exception ex){ …… } // 此句必須放在最后!
為什么會(huì)有這個(gè)限制?談?wù)勀闶窃趺蠢斫獾摹?
學(xué)生們幾乎 100% 答對(duì)此題,由于 Exception 是頂層類,放到前面會(huì)導(dǎo)致后面的 catch 語(yǔ)句不可能有機(jī)會(huì)運(yùn)行。
10. 請(qǐng)看以下示例代碼:
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // 輸出: true
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s3 == s4); // 輸出: false
請(qǐng)解釋一下為什么上述代碼中 “ System.out …” 兩句代碼輸出完全不同的結(jié)果 ?
這里面的關(guān)鍵在于 Java 常量池。 JVM 在裝載一個(gè) .class 文件時(shí),會(huì)在內(nèi)存中創(chuàng)建一個(gè)常量池。
“ Hello ”是字符串常量, Java 編譯器會(huì)將“ Hello ”放到常量池中,并給它一個(gè)索引:
const #28 = Asciz Hello;
以下兩句代碼:
String s1 = "Hello";
String s2 = "Hello";
將生成以下的字節(jié)碼指令:
0: ldc #2; //String Hello
2: astore_1
3: ldc #2; //String Hello
5: astore_2
上面的 #2 代表常量池中的第 2 項(xiàng):
const #2 = String #28; // Hello
注意它引用常量池的第 28 項(xiàng),前面已經(jīng)看到,第 28 項(xiàng)就是一個(gè)“ Hello ”字符串對(duì)象。
astore 指令將常量池中同一個(gè)“ Hello ”字符串對(duì)象的地址賦給了 s1 和 s2 ,所以, s1==s2 肯定返回 true 。
下面來(lái)看一下:
String s3 = new String("Hello");
所生成的字節(jié)碼指令:
// 創(chuàng)建 String 對(duì)象,對(duì)象引用被 Push 到操作數(shù)堆棧( operand stack )
// 常量池的第 5 項(xiàng)指向一個(gè) CONSTANT_Class_info 類型的表項(xiàng),這個(gè)表項(xiàng)將引用 String 類型
// 在程序運(yùn)行時(shí),會(huì)導(dǎo)致 JVM 裝載并連接 String 類型信息
22: new #5; //class java/lang/String
25: dup // 復(fù)制對(duì)象引用,再次壓入操作數(shù)堆棧,此復(fù)制的對(duì)象引用將用于調(diào)用構(gòu)造函數(shù)
26: ldc #2; //String Hello 裝載常量“ Hello ”
28: invokespecial #6;
//Methodjava/lang/String."<init>":(Ljava/lang/String;)V
// 調(diào)用 String 類的構(gòu)造函數(shù),它從操作數(shù)堆棧提取信息初始化 String 對(duì)象
31: astore_3 // 將創(chuàng)建好的對(duì)象引用保存到 s3 中
從上述分析可以清晰地看到, s3 與 s4 都引用不同的對(duì)象。所以, s3==s4 返回 fasle 。
現(xiàn)在,同學(xué)們對(duì) JVM 的認(rèn)識(shí)是不是更深入了一步?
現(xiàn)在 Java 程序員“遍地”都是,但其中高水平的不多,為什么?
一個(gè)很重要的原因就是淺嘗輒止,僅記住了一些語(yǔ)法結(jié)論,卻從不主動(dòng)地去探索一下代碼背后的奧秘,怎會(huì)有提高?
我很早就在課堂上展示了 javap 的使用方法,并向同學(xué)們推薦了那本經(jīng)典的《 Inside the Java Virtual Machine 》,但我估計(jì)由于課業(yè)負(fù)擔(dān)重,沒(méi)幾個(gè)同學(xué)真的
去看了這本書,對(duì)此,我只能一聲嘆息。
多問(wèn)幾個(gè)為什么,對(duì)事物刨根問(wèn)底,這其實(shí)不是在學(xué)習(xí),而是在探索了,這個(gè)過(guò)程中,你的能力會(huì)得到不斷地鍛煉和提高。
另外我要指出,同樣的學(xué)習(xí)方法可以直接用 .NET 領(lǐng)域,比如 .NET 有一個(gè) ildasm 可以直接查看 .NET 程序集( .exe 和 .dll ,等價(jià)于 Java 中的 .class )中
的 IL 指令(對(duì)應(yīng)于 Java 字節(jié)碼指令),有一個(gè) Reflector 工具可以很方便地反匯編查看任意一個(gè)程序集的源碼,對(duì)應(yīng)地, .NET 虛擬機(jī)—— CLR ,也與 JVM
有許多相似之處。
我在寫作《 .NET 4.0 面向?qū)ο缶幊搪劇窌r(shí),就通過(guò)直接查看程序集的 IL 指令代碼,弄清楚了不少東西,也加深了對(duì)語(yǔ)言編譯器和虛擬機(jī)的認(rèn)識(shí)。
象 Java 和 .NET 這種非常相近的平臺(tái),下功夫弄清楚一個(gè),你會(huì)發(fā)現(xiàn)學(xué)習(xí)與掌握另一個(gè)并不困難。計(jì)算機(jī)有很多東西是相通的。關(guān)鍵不在于學(xué)什么東西,
而是是否掌握了學(xué)習(xí)的科學(xué)方法,并且有一種不斷學(xué)習(xí)、勇于探索未知的精神。具備這種基本素質(zhì)的學(xué)生,我相信他日后的發(fā)展應(yīng)該不會(huì)差的。
posted on 2011-02-19 16:39
鵬凌 閱讀(652)
評(píng)論(0) 編輯 收藏 所屬分類:
java