久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

SCJP 认证考试指南

發(fā)布時(shí)間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SCJP 认证考试指南 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
SCJP 認(rèn)證考試指南 SCJP – Sun Certified Java Programmer (Sun Java 程序員認(rèn)證) 第1 章 聲明和訪問(wèn)控制 目標(biāo)一 創(chuàng)建數(shù)組 數(shù)組 Java 中的數(shù)組跟C/C++這些語(yǔ)言中的數(shù)組的語(yǔ)法結(jié)構(gòu)很相似。但是,Java 去掉了C/C++中的可以通過(guò)[]或者使用指針來(lái)訪問(wèn)元素的功能。這種在C/C++中被普遍接受的功能雖然強(qiáng)大,但是也讓Bug 橫行的軟件更容易出現(xiàn)。因?yàn)镴ava 不支持這種直接通過(guò)指針來(lái)操縱數(shù)據(jù),這類(lèi)的Bug 也被消除了。 數(shù)組是一類(lèi)包含被稱為元素的值的對(duì)象。這就為你在程序中移動(dòng)或保存一組數(shù)據(jù)以很方便的支持,并且允許你根據(jù)需要訪問(wèn)和改變這些值。用一個(gè)小例子來(lái)說(shuō):你可以創(chuàng)建一個(gè)String 類(lèi)型的數(shù)組,每一個(gè)都包含一個(gè)運(yùn)動(dòng)隊(duì)隊(duì)員名字。數(shù)組可以傳送給一個(gè)需要訪問(wèn)每個(gè)隊(duì)員名字的方法。如果一個(gè)新隊(duì)員加入,其中一個(gè)老隊(duì)員的名字可以被修改成新隊(duì)員的名字。這就顯得比player1、player2、player3 等等很隨意的不相關(guān)的變量方便很多。跟變量通過(guò)變量名來(lái)訪問(wèn)不同的是,元素通過(guò)從0 開(kāi)始的數(shù)字來(lái)訪問(wèn)。因此,你可以一個(gè)個(gè)的訪問(wèn)數(shù)組的每個(gè)元素。 數(shù)組跟對(duì)象很相似,它們都是用new 關(guān)鍵字來(lái)創(chuàng)建,并且有屬于主要父對(duì)象類(lèi)的方法。數(shù)組可能存儲(chǔ)簡(jiǎn)單類(lèi)型或者對(duì)象的引用。 數(shù)組的每個(gè)元素必須是同一類(lèi)型的。元素的類(lèi)型在數(shù)組被聲明時(shí)確定。如果你需要存儲(chǔ)不同類(lèi)型元素的方式,你可以選擇collection 類(lèi),collection 類(lèi)是Java2 考試中的新增的考點(diǎn),我們將會(huì)在第十部分討論它。你可以用數(shù)組來(lái)存儲(chǔ)對(duì)象的句柄,你能像使用其它任意對(duì)象引用一樣訪問(wèn),摘錄或者使用它。 聲明但不分配空間 聲明一個(gè)數(shù)組不需分配任何存儲(chǔ)空間,它僅僅是代表你試圖創(chuàng)建一個(gè)數(shù)組。跟C/C++聲明一個(gè)數(shù)組的明顯區(qū)別就是空間的大小沒(méi)有被特別標(biāo)識(shí)。因此,下面的聲明將會(huì)引起一個(gè)編譯期錯(cuò)誤。 int num[5]; 一個(gè)數(shù)組的大小將在數(shù)組使用new 關(guān)鍵字真正創(chuàng)建時(shí)被給定,例如: int num[]; num = new int[5]; 你可以認(rèn)為命令new 的使用跟初始化一個(gè)類(lèi)的實(shí)例的使用是類(lèi)似的。例子中數(shù)組名num說(shuō)明數(shù)組大小可以是任意大小的整形數(shù)據(jù)。 同時(shí)聲明和創(chuàng)建數(shù)組 這個(gè)例子也可以使用一行語(yǔ)句完成: int num[] = new int[5]; 方括號(hào)也可以放在數(shù)據(jù)類(lèi)型后面或者數(shù)組名后面。下面的兩種都是合法的: int[] num; int num[]; 你可以讀作: 一個(gè)名字為num 的整型數(shù)組 一個(gè)數(shù)據(jù)類(lèi)型為整型名字為num 的數(shù)組 Java 和C/C++數(shù)組的比較 Java 數(shù)組知道它的大小,并且Java 語(yǔ)言支持對(duì)意外的移動(dòng)到數(shù)組末端的保護(hù)。 如果你從Visual Basic 背景下轉(zhuǎn)到Java 開(kāi)發(fā),并且還不習(xí)慣于一直從0 開(kāi)始計(jì)數(shù),這點(diǎn)是很方便的。這也可以幫你避免一些在C/C++程序中很難發(fā)現(xiàn)的錯(cuò)誤,例如移動(dòng)到了數(shù)組末端并且指向了任意內(nèi)存地址。 例如,下面的程序會(huì)引起一個(gè)ArrayIndexOutOfBoundsException 異常。 int[] num= new int[5]; for(int i =0; i<6; i++){ num[i]=i*2; } 訪問(wèn)一個(gè)Java 數(shù)組的標(biāo)準(zhǔn)習(xí)慣用法是使用數(shù)組的length 成員 例如: int[] num= new int[5]; for(int i =0; i<num.length; i++){ num[i]=i*2; } 數(shù)組知道它的大小 假如你跳過(guò)了C/C++的對(duì)照,Java 中的數(shù)組總是知道它們的大小,這表現(xiàn)在length 字段。 因此,你可以通過(guò)下面的語(yǔ)句動(dòng)態(tài)移動(dòng)數(shù)組: int myarray[]=new int[10]; for(int j=0; j<myarray.length;j++){ myarray[j]=j; } 注意,數(shù)組有l(wèi)ength 字段,而不是length()方法。當(dāng)你開(kāi)始用一組字符串的時(shí)候,你會(huì)像s.length()這樣使用字符串的length 方法。 數(shù)組中的length 是域(或者說(shuō)特性)而不是方法。 Java 數(shù)組和Visual Basic 數(shù)組的對(duì)照 Java 中的數(shù)組總是從0 開(kāi)始。如果使用了Option base 聲明,Visual Basic 可能從1 開(kāi)始。Java 中沒(méi)有跟Visual Basic 中可以使你不刪除內(nèi)容就改變數(shù)組大小的redim preserve 命令等價(jià)的語(yǔ)句。但你可以建立一個(gè)同樣大小的新數(shù)組,并且復(fù)制現(xiàn)有元素到里面。 一個(gè)數(shù)組聲明可以有多個(gè)方括號(hào)。Java 形式上不支持多維數(shù)組,但是它可以支持?jǐn)?shù)組的數(shù)組,就是我們常說(shuō)的嵌套數(shù)組。 C/C++中那樣的多維數(shù)組和嵌套數(shù)組的最主要區(qū)別就是,每個(gè)數(shù)組不需要有同樣的長(zhǎng)度。如果你將一個(gè)數(shù)字當(dāng)作一個(gè)矩陣,矩陣不一定是矩形。按照J(rèn)ava 語(yǔ)言規(guī)范:(http://java.sun.com/docs/books/jls/html/10.doc.html#27805)“括號(hào)里的數(shù)指明了數(shù)組嵌套的深度” 在其他語(yǔ)言中,就要跟數(shù)組的維度相符。因此,你可以建立一個(gè)類(lèi)似于下面的形式的二維數(shù)組: int i[][]; 第一個(gè)維度可以匹配X,第二個(gè)維度可以匹配Y。 聲明和初始化相結(jié)合 一個(gè)數(shù)組可以通過(guò)一個(gè)語(yǔ)句來(lái)創(chuàng)建并初始化,這就代替了通過(guò)數(shù)組循環(huán)來(lái)初始化的方式。這種方法很適合小數(shù)組。下面的語(yǔ)句創(chuàng)建了一個(gè)整型數(shù)組并且賦值為0 到4: int k[]=new int[] {0,1,2,3,4}; 注意,你沒(méi)有必要確定數(shù)組元素的數(shù)量。你可能在測(cè)驗(yàn)中被問(wèn)到下面的語(yǔ)句是不是正確的問(wèn)題: int k=new int[5] {0,1,2,3,4} //Wrong, will not compile! 你可以創(chuàng)建數(shù)組的同時(shí)確定任何數(shù)據(jù)類(lèi)型,因此,你可以創(chuàng)建一個(gè)類(lèi)似于下面形式的字符串 數(shù)組: String s[]=new String[] {"Zero","One","Two","Three","Four"}; System.out.println(s[0]); 這句將會(huì)輸出String[0]。 數(shù)組的默認(rèn)值 不同于其他語(yǔ)言中的變量在類(lèi)級(jí)別創(chuàng)建和本地方法級(jí)別創(chuàng)建有不同的動(dòng)作,Java 數(shù)組總是被設(shè)定為默認(rèn)值。 無(wú)論數(shù)組是否被創(chuàng)建了,數(shù)組中的元素總是設(shè)為默認(rèn)值。因此,整型的數(shù)組總是被置0,布爾值總是被置false。下面的代碼編譯時(shí)不會(huì)出錯(cuò),并且輸出0。 public class ArrayInit{ public static void main(String argv[]){ int[] ai = new int[10]; System.out.println(ai[0]); } } 問(wèn)題 問(wèn)題1)怎樣通過(guò)一個(gè)語(yǔ)句改變數(shù)組大小同時(shí)保持原值不變? 1) Use the setSize method of the Array class 2) Use Util.setSize(int iNewSize) 3) use the size() operator 4) None of the above 問(wèn)題2) 你想用下面的代碼查找數(shù)組最后一個(gè)元素的值,當(dāng)你編譯并運(yùn)行它的時(shí)候,會(huì)發(fā)生什么? public class MyAr{ public static void main(String argv[]){ int[] i = new int[5]; System.out.println(i[5]); } } 1) Compilation and output of 0 2) Compilation and output of null 3) Compilation and runtime Exception 4) Compile time error 問(wèn)題3)作為一個(gè)好的Java 程序員,你已忘記了曾經(jīng)在C/C++中知道的關(guān)于數(shù)組大小信息的知識(shí)。如果你想遍歷一個(gè)數(shù)組并停止在最后一個(gè)元素處。你會(huì)使用下面的哪一個(gè)? 1)myarray.length(); 2)myarray.length; 3)myarray.size 4)myarray.size(); 問(wèn)題4)你的老板為了你寫(xiě)出了HelloWorld 而很高興地為你升職了,現(xiàn)在她給你分配了一個(gè)新任務(wù),去做一個(gè)踢踏舞游戲(或者我小時(shí)候玩的曲棍球游戲)。你認(rèn)為你需要一個(gè)多維數(shù)組,下面哪一個(gè)能做這個(gè)工作? 1) int i =new int[3][3]; 2) int[] i =new int[3][3]; 3) int[][] i =new int[3][3]; 4) int i[3][3]=new int[][]; 問(wèn)題5) 你希望找到一個(gè)更優(yōu)雅的方式給你的數(shù)組賦值而不使用for 循環(huán)語(yǔ)句,下面的哪一個(gè)能做到? 1)myArray{ [1]="One"; [2]="Two"; [3]="Three"; } 2)String s[5]=new String[] {"Zero","One","Two","Three","Four"}; 3)String s[]=new String[] {"Zero","One","Two","Three","Four"}; 4)String s[]=new String[]={"Zero","One","Two","Three","Four"}; 問(wèn)題6)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? public class Ardec{ public static void main(String argv[]){ Ardec ad = new Ardec(); ad.amethod(); } public void amethod(){ int ia1[]= {1,2,3}; int[] ia2 = {1,2,3}; int ia3[] = new int[] {1,2,3}; System.out.print(ia3.length); } } 1) Compile time error, ia3 is not created correctly 2) Compile time error, arrays do not have a length field 3) Compilation but no output 4) Compilation and output of 3 答案 答案1) 4) None of the above 你不能改變一個(gè)數(shù)組的大小。你需要?jiǎng)?chuàng)建一個(gè)不同大小的臨時(shí)數(shù)組,然后將原數(shù)組中的內(nèi)容放進(jìn)去。Java 支持能夠改變大小的類(lèi)的容器,例如Vector 或者collection 類(lèi)的一個(gè)成員。 答案2) 3) Compilation and runtime Exception 當(dāng)你試著移動(dòng)到數(shù)組的末端的時(shí)候,你會(huì)得到一個(gè)運(yùn)行時(shí)錯(cuò)誤。因?yàn)閿?shù)組從0 開(kāi)始索引,并且最后一個(gè)元素是i[4]而不是i[5]。 答案3) 2) myarray.length; 答案4) 3) int[][] i=new int[3][3]; 答案5) 3)String s[]=new String[] {"Zero","One","Two","Three","Four"}; 答案6) 4) Compilation and output of 3 所有的數(shù)組的聲明都是正確的。如果你覺(jué)得不太可能,可以自己編譯這段代碼。 目標(biāo)二 定義類(lèi)和變量 定義類(lèi),內(nèi)部類(lèi),方法,實(shí)例變量,靜態(tài)變量和自動(dòng)(本地方法)變量,需要合適的選用允許的修飾詞。(例如public,final,static,abstract 諸如此類(lèi))。這些修飾詞或者單獨(dú)使用或者聯(lián)合使用,定義了包的關(guān)系。 本目標(biāo)需要注意的 我發(fā)現(xiàn)目標(biāo)中用了“諸如此類(lèi)”,這讓我有些煩惱,我想你需要弄明白下面詞的意思: native transient synchronized volatile 什么是類(lèi)? 一個(gè)類(lèi)的的定義把它很生硬描述為“方法和數(shù)據(jù)的集合”。它把面向?qū)ο缶幊坛鰜?lái)之前的編程思想結(jié)合起來(lái),這對(duì)理解該概念很有幫助。在類(lèi)和面向?qū)ο蟪绦蛟O(shè)計(jì)前的主要概念是結(jié)構(gòu)化程序設(shè)計(jì)。結(jié)構(gòu)化程序設(shè)計(jì)的理念是程序員將復(fù)雜問(wèn)題劃分為小塊的代碼,一般稱為函數(shù)或子程序。這符合“做一件很大很復(fù)雜的事情的好辦法是把它分成一系列比較小但更容 易管理的問(wèn)題”的理念。 盡管結(jié)構(gòu)化程序設(shè)計(jì)在管理復(fù)雜性方面很有用,但它不能容易的解決代碼重用問(wèn)題。程序員發(fā)現(xiàn)他們總是“重復(fù)發(fā)明”輪子。在試著對(duì)現(xiàn)實(shí)物理對(duì)象的思考中,程序設(shè)計(jì)方面的思想家找到了面向?qū)ο蟮睦砟?#xff08;有時(shí)被稱為OO)。舉例來(lái)說(shuō),一個(gè)計(jì)算機(jī)廠商準(zhǔn)備生產(chǎn)一種新型個(gè)人電腦,如果計(jì)算機(jī)廠商使用類(lèi)似于程序設(shè)計(jì)的方式的話,就要求他建立新團(tuán)隊(duì)來(lái)設(shè)計(jì)新CPU 芯片,新聲卡,沒(méi)準(zhǔn)還需要另一個(gè)團(tuán)隊(duì)設(shè)計(jì)規(guī)劃制造新的主板。事實(shí)上,這根本不可能出現(xiàn)。由于電腦組件接口的標(biāo)準(zhǔn)化,計(jì)算機(jī)廠商只需要聯(lián)系配件供應(yīng)商,并商議好他們要生產(chǎn)的新型號(hào)的說(shuō)明書(shū)就行了。注意組件接口標(biāo)準(zhǔn)化的重要性。 比較C++/VB 和Java 的類(lèi) 因?yàn)镴ava 被設(shè)計(jì)成容易讓C++程序員學(xué)習(xí)的語(yǔ)言,因此兩種語(yǔ)言在處理類(lèi)上有很多相似的地方。C++和Java 都有繼承,多態(tài)和數(shù)據(jù)隱藏特性,并使用顯式的修飾詞。有一些不同也是因?yàn)槭笿ava 更容易學(xué)習(xí)和使用。C++語(yǔ)言實(shí)現(xiàn)了多態(tài)繼承,這樣,一個(gè)類(lèi)就可以比一個(gè)的父類(lèi)(或基類(lèi))更強(qiáng)大。Java只允許單繼承,這樣就只有一個(gè)父類(lèi)。為了克服這個(gè)限制,Java 有一個(gè)被稱作接口的特性。Java 語(yǔ)言的設(shè)計(jì)者確定接口能夠提供多態(tài)繼承的好處而沒(méi)有壞處。所有Java 類(lèi)都是Object 類(lèi)的后代。 對(duì)象在Visual Basic 中是語(yǔ)言設(shè)計(jì)之后才加入的想法。Visual Basic 有時(shí)被稱作基于對(duì)象的語(yǔ)言而不是面向?qū)ο蟮恼Z(yǔ)言。這就好像是語(yǔ)言的設(shè)計(jì)者認(rèn)為類(lèi)很酷,然后隨著VB4 的發(fā)布,他們決定加入一個(gè)新類(lèi)型的模塊,稱它為類(lèi)并且加上冒號(hào),讓它看起來(lái)更像C++。VB 的類(lèi)概念中失去了至關(guān)重要的元素:繼承。微軟在VB5 中加入了跟Java 的接口很相似的接口的概念。VB 類(lèi)和Java 類(lèi)的最主要相似之處是引用的使用和new 關(guān)鍵字。 Java 中類(lèi)的角色 類(lèi)是Java 的心臟,所有的Java 代碼都在一個(gè)類(lèi)里。Java 里沒(méi)有自由獨(dú)立代碼的概念,甚至最簡(jiǎn)單的HelloWorld 應(yīng)用都是包含在類(lèi)里被創(chuàng)建的。為了指出一個(gè)類(lèi)是另一個(gè)類(lèi)的派生類(lèi),我們使用extend 關(guān)鍵字。如果extend 關(guān)鍵字沒(méi)有被使用,這個(gè)類(lèi)將是基類(lèi)Object 派生的。這可以使它有一些基本的功能,比如打印自己的名字和其他一些在線程中可能需要用到的功能。 類(lèi)的最簡(jiǎn)單特性 定義一個(gè)類(lèi)至少需要class 關(guān)鍵字,類(lèi)名和一對(duì)花括號(hào)。如: class classname {} 如果不是有特別作用的類(lèi),它在語(yǔ)法上是正確的(我很驚訝的發(fā)現(xiàn),當(dāng)我舉例說(shuō)明繼承時(shí),我定義了一個(gè)跟著類(lèi)似的類(lèi))。通常,一個(gè)類(lèi)還會(huì)包括一個(gè)訪問(wèn)修飾符,放在關(guān)鍵字class 前面,還會(huì)有程序體放在花括號(hào)之間。下面的是一個(gè)更好的類(lèi)模版: public class classname{ //Class body goes here } 創(chuàng)建一個(gè)簡(jiǎn)單的HelloWorld 類(lèi) 這里有一個(gè)簡(jiǎn)單的HelloWorld 程序,它將會(huì)向控制臺(tái)輸出“hello world”。 public class HelloWorld{ public static void main(String argv[]){ System.out.println("Hello world"); } }//End class definition 關(guān)鍵字public 是一個(gè)可見(jiàn)的修飾符,指明了這個(gè)類(lèi)對(duì)于其他類(lèi)來(lái)說(shuō)都是可見(jiàn)的。一個(gè)文件只有一個(gè)外部類(lèi)可以聲明為public。內(nèi)部類(lèi)將會(huì)隱藏在任意位置。如果你在一個(gè)文件中定義了多于一個(gè)的public 類(lèi),將會(huì)發(fā)生一個(gè)編譯期錯(cuò)誤。注意,Java 對(duì)每一部分都是很敏感的,包含這個(gè)類(lèi)的文件名字必須是HelloWorld.java。當(dāng)然,這跟微軟平臺(tái)雖然保護(hù)但是卻忽略文 件的大小寫(xiě)有些差別。 關(guān)鍵字class 指明了一個(gè)將被定義的類(lèi),并且類(lèi)名是HelloWorld。左花括號(hào)表明類(lèi)的開(kāi)始。注意,類(lèi)結(jié)束的右花括號(hào)后面沒(méi)有分號(hào)。注釋語(yǔ)句 //End class definition 使用了C/C++中同樣允許的單行類(lèi)型。Java 也能夠識(shí)別/**/的注釋模式。 創(chuàng)建一個(gè)類(lèi)的實(shí)例 上面描述的HelloWorld 應(yīng)用例子很淺顯的告訴了你所能創(chuàng)建的最簡(jiǎn)單的應(yīng)用,但是它漏掉了使用類(lèi)時(shí)至關(guān)重要的元素,那就是關(guān)鍵字new 的使用,new 指出了一個(gè)類(lèi)的新實(shí)例的創(chuàng)建。在HelloWorld 應(yīng)用中,因?yàn)橹挥蠸ystem.out.println 這個(gè)唯一的static 方法,并且不需要類(lèi)使用new 關(guān)鍵字創(chuàng)建,因此創(chuàng)建新實(shí)例不是必要的。static 方法只能訪問(wèn)static 變量。可以稍微改進(jìn)一下HelloWorld 應(yīng)用,下面舉例說(shuō)明一個(gè)類(lèi)的新實(shí)例的創(chuàng)建。 public class HelloWorld2{ public static void main(String argv[]){ HelloWorld2 hw = new HelloWorld2(); hw.amethod(); } public void amethod(){ System.out.println("Hello world"); } } 上面的代碼通過(guò)這行代碼創(chuàng)建了自己的一個(gè)新實(shí)例。 HelloWorld2 hw = new HelloWorld2(); 這是使用類(lèi)創(chuàng)建新實(shí)例的一個(gè)基本語(yǔ)法。注意類(lèi)的名字怎樣出現(xiàn)了兩次。第一個(gè)指明了類(lèi)的引用的數(shù)據(jù)類(lèi)型。這需要它不能和new 關(guān)鍵字所修飾真正的類(lèi)的名字相同。這個(gè)類(lèi)實(shí)例的名字是hw。這僅僅是給變量選擇的名字。這里有一個(gè)命名習(xí)慣,一個(gè)類(lèi)的實(shí)例名以小寫(xiě)字母開(kāi)頭,而類(lèi)的名字以大寫(xiě)字母開(kāi)頭。 創(chuàng)建方法 在上一個(gè)例子HelloWorld2 中,一個(gè)Java 中的方法跟C/C++中的函數(shù)和Visual Basic 中的子程序很相似。上例中名字為amethod 的方法和本例中的amethod 方法被聲明為public,這說(shuō)明它可以在任何地方被訪問(wèn)。它有一個(gè)返回值void,表明沒(méi)有值返回。并且括號(hào)中也是空的,表明它沒(méi)有參數(shù)。 同樣的方法可以從下面幾種方式之中選擇: private void amethod(String s) private void amethod(int i, String s) protected void amethod(int i) 這些例子說(shuō)明了一些典型的方法簽名。使用關(guān)鍵字private 和protected 說(shuō)明它們將會(huì)在別處隱藏。 Java 方法和其他像C 這樣的非面向?qū)ο笳Z(yǔ)言的方法的區(qū)別是Java 方法屬于類(lèi)。這表明它們通過(guò)點(diǎn)號(hào)指明代碼屬于哪個(gè)類(lèi)的實(shí)例來(lái)調(diào)用。(static 方法是一個(gè)例外,但我們現(xiàn)在無(wú)需擔(dān)心) 因此在HelloWorld 中amethod 通過(guò)下面的語(yǔ)句調(diào)用 HelloWorld hw = new HelloWorld(); hw.amethod(); 在HelloWorld 類(lèi)中創(chuàng)建的其他實(shí)例中,方法被類(lèi)的每個(gè)實(shí)例所調(diào)用。每個(gè)類(lèi)的實(shí)例將能夠訪問(wèn)它自己的變量。因此下面的代碼將調(diào)用不同實(shí)例的amethod 方法 HelloWorld hw = new HelloWorld(); HelloWorld hw2 = new HelloWorld(); hw.amethod(); hw2.amethod(); 類(lèi)的兩個(gè)實(shí)例hw 和hw2 可能訪問(wèn)不同的變量。 自動(dòng)局部變量 自動(dòng)變量是方法變量。它們?cè)诜椒ùa開(kāi)始運(yùn)行時(shí)生效,并在方法結(jié)束時(shí)失效。因?yàn)樗鼈冎荒茉诜椒▋?nèi)可見(jiàn),因此臨時(shí)操作數(shù)據(jù)時(shí)比較有用。如果你希望一個(gè)值在方法被調(diào)用時(shí)保持,你需要將變量創(chuàng)建在類(lèi)級(jí)別。一個(gè)自動(dòng)變量將“屏蔽”類(lèi)級(jí)別的變量。因此,下面的代碼將打印99 而不是10 public class Shad{ public int iShad=10; public static void main(String argv[]){ Shad s = new Shad(); s.amethod(); }//End of main public void amethod(){ int iShad=99; System.out.println(iShad); }//End of amethod } 修飾語(yǔ)和封裝 修飾符的可見(jiàn)性是Java 封裝機(jī)制的一部分。封裝允許分離方法執(zhí)行的接口。修飾符的可見(jiàn)性是Java 封裝機(jī)制至關(guān)重要的部分。封裝允許分離方法執(zhí)行的接口。帶來(lái)的好處就是類(lèi)內(nèi)部的代碼的細(xì)節(jié)可以被改變,同時(shí)不影響其他對(duì)象的使用。這是面向?qū)ο笤O(shè)計(jì)(最后不得不在某處使用這個(gè)詞)的一個(gè)關(guān)鍵概念。 封裝一般用找回或更新private 類(lèi)的變量值的方法的形式。這些方法一般是accessor 或mutator 方法。訪問(wèn)方法找回值而設(shè)置方法改變值。命名慣例是這些方法名類(lèi)似于setFOO 改變值,getFOO 得到值。注意,使用set 和get 來(lái)命名的方法比僅僅使程序員感到方便更重要,并且是Javabean 系統(tǒng)的重要組成部分。不過(guò)我們的測(cè)試還沒(méi)有涉及到Javabean 的內(nèi)容。舉一個(gè)例子,你有一個(gè)變量用來(lái)存儲(chǔ)學(xué)生的年齡。你可能簡(jiǎn)單的用一個(gè)public 的整型變量來(lái)存儲(chǔ)。 int iAge; 接下來(lái),當(dāng)你的應(yīng)用程序交付使用后,你可能會(huì)發(fā)現(xiàn)你的某些學(xué)生可能有超過(guò)200 歲的記錄,還有小于0 歲的記錄。你需要一段代碼來(lái)檢查錯(cuò)誤條件。所以當(dāng)你的程序改變年齡的值的時(shí)候,你用if 語(yǔ)句來(lái)檢查范圍。 if(iAge > 70){ //do something } if (iAge <3){ //do something } 當(dāng)你正在做這些的時(shí)候,你漏掉了一些使用過(guò)iAge 變量的代碼,所以你被召回了,因?yàn)槟憧赡苡幸粋€(gè)19 歲的學(xué)生,但是你的記錄里卻是190 歲。 面向?qū)ο笫褂梅庋b處理了這樣的問(wèn)題,就是創(chuàng)建一個(gè)訪問(wèn)包含年齡值的private 域的方法,名字類(lèi)似于setAge 和getAge。setAge 方法可能有一個(gè)整型的參數(shù)并且更新年齡的private值,getAge 方法沒(méi)有參數(shù)但從private 的年齡域返回值。 public void setAge(int iStudentAge){ iAge = iStudentAge; } public int getAge(){ return iAge; } 開(kāi)始,我們也許認(rèn)為這么長(zhǎng)的代碼來(lái)做一小段代碼就能完成的工作沒(méi)有意義,但是,當(dāng)這些方法能夠滿足你的需求時(shí),可以幫你做更多的iAge 域的確認(rèn)工作,同時(shí)不會(huì)影響已經(jīng)在使用這些信息的代碼。通過(guò)這樣的代碼執(zhí)行處理方式,實(shí)際的程序代碼行可以改變,而外面的部分(接口)保持不變。 Private(私有) 私有變量?jī)H僅在創(chuàng)建它的類(lèi)內(nèi)部可見(jiàn)。這意味著它們?cè)谧宇?lèi)里不可見(jiàn)。這使變量除了當(dāng)前類(lèi)之外,絕緣于其他方法的修改。像是修飾語(yǔ)和封裝里描述的,這對(duì)于將接口與接口實(shí)現(xiàn)分離開(kāi)很有幫助。 class Base{ private int iEnc=10; public void setEnc(int iEncVal){ if(iEncVal < 1000){ iEnc=iEncVal; }else System.out.println("Enc value must be less than 1000"); //Or Perhaps thow an exception }//End if } public class Enc{ public static void main(String argv[]){ Base b = new Base(); b.setEnc(1001); }//End of main } public(共有) public 修飾符可以應(yīng)用于變量(域)或者類(lèi)。它可能是你學(xué)習(xí)Java 過(guò)程中最先接觸的修飾符。想想HelloWorld.Java 程序中被這樣聲明的類(lèi)的代碼 public class HelloWorld 這是因?yàn)镴ava 虛擬機(jī)僅僅在一個(gè)聲明為public 的類(lèi)中查找神奇的main 啟動(dòng)方法。public static void main(String argv[]) 一個(gè)public 類(lèi)有全局的作用范圍,一個(gè)實(shí)例可以在程序內(nèi)部或外部的任意位置創(chuàng)建。任何文件中只能有一個(gè)非內(nèi)部類(lèi)可以用public 關(guān)鍵字定義。如果你用public 關(guān)鍵字在一個(gè)文件中定義了超過(guò)一個(gè)非內(nèi)部類(lèi),編譯器將會(huì)報(bào)錯(cuò)。 使用public 修飾符定義一個(gè)變量可以使它在任何位置適用。使用方法如下: public int myint =10; 如果你希望創(chuàng)建一個(gè)可以在任何地方修改的變量,你可以將它聲明為public。你可以使 用類(lèi)似于調(diào)用方法那樣的點(diǎn)號(hào)來(lái)訪問(wèn)它。 class Base { public int iNoEnc=77; } public class NoEnc{ public static void main(String argv[]){ Base b = new Base(); b.iNoEnc=2; System.out.println(b.iNoEnc); }//End of main } 注意,并不建議你對(duì)代碼的接口和執(zhí)行不加分隔的使用。如果你想改變iNoEnc 的數(shù)據(jù)類(lèi)型,你必須修改執(zhí)行改變代碼的每一部分。 protected(保護(hù)) protected 有一點(diǎn)古怪。一個(gè)protected 變量在類(lèi),子類(lèi)和同一個(gè)包內(nèi)部可見(jiàn),但不是全部可見(jiàn)。限制就是它在包內(nèi)部的可見(jiàn)性可能超過(guò)你的預(yù)期。在同一路徑下的類(lèi)都是被默認(rèn)為 在一個(gè)包內(nèi),因此,protected 類(lèi)將會(huì)可見(jiàn)。這就意味著一個(gè)protected 變量會(huì)比一個(gè)沒(méi)有任何訪問(wèn)修飾符的變量更有可見(jiàn)性。 一個(gè)沒(méi)有訪問(wèn)修飾符定義的變量稱為它有默認(rèn)的可見(jiàn)性。默認(rèn)可見(jiàn)性是說(shuō)一個(gè)變量可以在類(lèi)內(nèi)部可見(jiàn),而包內(nèi)的其他類(lèi)中均不可見(jiàn),不在同一個(gè)包的子類(lèi)內(nèi)也不可見(jiàn)。 靜態(tài)的(static) 雖然static 可以起到可見(jiàn)性修飾符的作用,但它不是直接的可見(jiàn)性修飾符。static 修飾符可以應(yīng)用于內(nèi)部類(lèi),方法和變量。功能代碼經(jīng)常放在static 方法中,例如Math 類(lèi)有完整的功能方法,如:random,sin 和round。基本數(shù)據(jù)類(lèi)型的包裝類(lèi)Integer,Double 等等也有static方法處理包裝過(guò)的基本數(shù)據(jù)類(lèi)型,如返回符合字符串“2”的int 值。 標(biāo)記一個(gè)變量為static 表明每個(gè)類(lèi)只能有一個(gè)副本存在。這是與普通的情況相區(qū)別。一般情況下,一個(gè)類(lèi)的每個(gè)實(shí)例都有一個(gè)整型變量的副本。在下面的非static int 例子中,三個(gè)實(shí)例中的int iMyVal 都有對(duì)應(yīng)各自實(shí)例的不同值。 class MyClass{ public int iMyVal=0; } public class NonStat{ public static void main(String argv[]){ MyClass m1 = new MyClass(); m1.iMyVal=1; MyClass m2 = new MyClass(); m2.iMyVal=2; MyClass m3 = new MyClass(); m3.iMyVal=99; //This will output 1 as each instance of the class //has its own copy of the value iMyVal System.out.println(m1.iMyVal); }//End of main } 下面的例子說(shuō)明了當(dāng)你有包含static 整型數(shù)的類(lèi)的多個(gè)實(shí)例時(shí)會(huì)發(fā)生什么 class MyClass{ public static int iMyVal=0; } public class Stat{ public static void main(String argv[]){ MyClass m1 = new MyClass(); m1.iMyVal=0; MyClass m2 = new MyClass(); m2.iMyVal=1; MyClass m3 = new MyClass(); m2.iMyVal=99; //Because iMyVal is static, there is only one //copy of it no matter how many instances //of the class are created /This code will //output a value of 99 System.out.println(m1.iMyVal); }//End of main } 你必須要忍受這樣的事實(shí),你不能在一個(gè)static 方法內(nèi)部訪問(wèn)一個(gè)非static 變量。因此,下面的代碼會(huì)引起一個(gè)編譯時(shí)錯(cuò)誤 public class St{ int i; public static void main(String argv[]){ i = i + 2;//Will cause compile time error} 一個(gè)static 方法不能在一個(gè)子類(lèi)中重寫(xiě)為非static 方法。同樣,一個(gè)非static(普通的)方法也不能在子類(lèi)中重寫(xiě)為static 方法。但是同樣的規(guī)則對(duì)方法重載沒(méi)有作用。下面的代碼在它嘗試重寫(xiě)類(lèi)的方法為非static 方法amethod 時(shí)將會(huì)引起一個(gè)錯(cuò)誤。 class Base{ public static void amethod(){} public class Grimley extends Base{ public void amethod(){}//Causes a compile time error } IBM Jikes 編譯器會(huì)產(chǎn)生下面的錯(cuò)誤 Found 1 semantic error compiling "Grimley.java": 6. public void amethod(){} <-------> *** Error: The instance method "void amethod();" cannot override the static method "void amethod();" declared in type "Base" static 方法不能在子類(lèi)中重寫(xiě),但是可以被隱藏 在我的模擬測(cè)驗(yàn)中,我有一個(gè)問(wèn)題問(wèn)到static 方法是否可以被重寫(xiě),答案是不能,但是引來(lái)了大量的email,很多人舉例說(shuō)明static 方法被重寫(xiě)了。在子類(lèi)中,重寫(xiě)過(guò)程包括的不僅僅是簡(jiǎn)單的替代一個(gè)方法。它還包括運(yùn)行時(shí)決定哪個(gè)方法被調(diào)用取決于它的引用類(lèi)型。 這里有一個(gè)例子的代碼,看起來(lái)顯示了一個(gè)static 方法被重寫(xiě)了 class Base{ public static void stamethod(){ System.out.println("Base");} public class ItsOver extends Base{ public static void main(String argv[]){ ItsOver so = new ItsOver(); so.stamethod();public static void stamethod(){ System.out.println("amethod in StaOver");} 這段代碼會(huì)被編譯并且輸出"amethod in StaOver" 本地的(native) native 修飾符僅僅用來(lái)修飾方法,指明代碼體不是用Java 而是用C 或C++所寫(xiě)。native方法經(jīng)常為平臺(tái)的特殊目的所寫(xiě),例如訪問(wèn)某些Java 虛擬機(jī)不支持的硬件。另一個(gè)原因是為了需要獲得更好的性能。 一個(gè)native 方法以一個(gè)分號(hào)結(jié)尾,而不是代碼塊。例如下面的代碼將會(huì)調(diào)用一個(gè)可能用C++所寫(xiě)的外部程序: public native void fastcalc(); 抽象(abstract) 粗略的看一下abstract 修飾符顯得很容易,但是也會(huì)漏掉它的一些隱含內(nèi)容。屬于主考者很喜歡問(wèn)的那種狡猾的,關(guān)于那類(lèi)修飾符的問(wèn)題。 abstract 修飾符可以被用在類(lèi)和方法上。當(dāng)用在方法上時(shí),表明方法會(huì)沒(méi)有方法體(也就是沒(méi)有花括號(hào)的部分),并且代碼只能在子類(lèi)執(zhí)行時(shí)運(yùn)行。但是,還有一些關(guān)于何時(shí)何處你能擁有abstract 方法的限制和包含這類(lèi)方法的類(lèi)的規(guī)則。如果一個(gè)類(lèi)有一個(gè)或多個(gè)abstract方法,或者繼承了不準(zhǔn)備運(yùn)行的abstract 方法,則它必須聲明為abstract。另外一個(gè)情況是,如果一個(gè)類(lèi)實(shí)現(xiàn)了接口但是不準(zhǔn)備運(yùn)行接口的每個(gè)方法。但這種情況很少見(jiàn)。如果一個(gè)類(lèi)有abstract 方法,則它需要聲明為abstract 類(lèi)不要認(rèn)為一個(gè)abstract 類(lèi)不能有非abstract 方法而感到心煩意亂。任何從abstract 類(lèi)繼承而來(lái)的類(lèi)都要實(shí)現(xiàn)基類(lèi)的abstract 方法,或者聲明自身為abstract 類(lèi)。這些規(guī)則傾向于問(wèn)你為什么想要?jiǎng)?chuàng)建abstract 方法? abstract 類(lèi)對(duì)于類(lèi)的設(shè)計(jì)者很有用。它使類(lèi)的設(shè)計(jì)者能夠創(chuàng)建應(yīng)當(dāng)被實(shí)現(xiàn)的方法的原型,但是真正的實(shí)現(xiàn)留給以后使用這個(gè)類(lèi)的人。下面的例子是一個(gè)包含abstract 方法的abstract類(lèi)。再次注意,類(lèi)必須被聲明為abstract,否則會(huì)出現(xiàn)編譯時(shí)錯(cuò)誤。下面的類(lèi)是abstract 類(lèi),它會(huì)被正確編譯并打印輸出字符串 public abstract class abstr{ public static void main(String argv[]){ System.out.println("hello in the abstract"); } public abstract int amethod(); } 常量(final) final 修飾符可以用在類(lèi),方法和變量上。它跟遺傳關(guān)系的意思很相近,因此很容易記憶。一個(gè)final 類(lèi)可能從不被繼承。另外一種想法是,一個(gè)final 類(lèi)不能作為父類(lèi)。任何final類(lèi)中的方法自動(dòng)成為final 方法。如果你不希望別的程序員“弄亂你的代碼”,這是一個(gè)有效的方法。另一個(gè)好處就是效率,編譯器對(duì)于一個(gè)final 方法的工作很少。這些內(nèi)容在Core Java的第一卷中有提及。 final 修飾符表明方法不能被重寫(xiě)。因此,如果你在子類(lèi)中有一個(gè)同樣簽名的方法的話,你會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤。下面的例子說(shuō)明對(duì)一個(gè)類(lèi)使用final 修飾符。這段代碼將會(huì)打印字符串"amethod" final class Base{ public void amethod(){ System.out.println("amethod");} } public class Fin{ public static void main(String argv[]){ Base b = new Base(); b.amethod();} } 一個(gè)final 變量的值不能被改變,并且必須在一定的時(shí)刻賦值。這跟其他語(yǔ)言中的constant的思想比較相似。 同步的(Synchronized) synchronized 關(guān)鍵字被用來(lái)保證不只有一個(gè)的線程在同一時(shí)刻訪問(wèn)同一個(gè)代碼塊。參看第七部分關(guān)于線程的內(nèi)容來(lái)了解更多的關(guān)于它的運(yùn)行的知識(shí)。 瞬時(shí)(Transient) transient 修飾符是不常用的修飾符之一。它表明一個(gè)變量在序列化過(guò)程中不能被寫(xiě)出。 不穩(wěn)定的(Volatile) 你可能對(duì)volatile 關(guān)鍵字有疑問(wèn)。最壞的情況就是你確認(rèn)它真的是一個(gè)Java 關(guān)鍵字。根據(jù)Barry Boone 所說(shuō)“它告訴編譯器一個(gè)變量可能在線程異步時(shí)被改變”接受它是Java 語(yǔ)言的一部分,然后去擔(dān)心別的吧。 聯(lián)合使用修飾符 可見(jiàn)性修飾符不能被聯(lián)合使用,一個(gè)變量不可能同時(shí)是private 和public,public 和protected,protected 和private。你當(dāng)然可以聯(lián)合使用可見(jiàn)性修飾符和我在下面列表中提及的修飾符。 native transient synchronized volatile 這樣你就可以有一個(gè)public static native 方法了。 修飾符可以用在哪里? 問(wèn)題 問(wèn)題1)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? abstract class Base{ abstract public void myfunc(); public void another(){ System.out.println("Another method");} } public class Abs extends Base{ public static void main(String argv[]){ Abs a = new Abs(); a.amethod();} public void myfunc(){ System.out.println("My func");} public void amethod(){ myfunc();} } 1) The code will compile and run, printing out the words "My Func" 2) The compiler will complain that the Base class has non abstract methods 3) The code will compile but complain at run time that the Base class has non abstract methods 4) The compiler will complain that the method myfunc in the base class has no body, nobody at allto looove it 問(wèn)題2)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? public class MyMain{ public static void main(String argv){System.out.println("Hello cruel world");} } 1) The compiler will complain that main is a reserved word and cannot be used for a class 2) The code will compile and when run will print out "Hello cruel world" 3) The code will compile but will complain at run time that no constructor is defined 4) The code will compile but will complain at run time that main is not correctly defined 問(wèn)題3)下面的哪個(gè)是Java 修飾符? 1) public 2) private 3) friendly 4) transient 問(wèn)題4) 當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? class Base{abstract public void myfunc();public void another(){System.out.println("Another method");} } public class Abs extends Base{public static void main(String argv[]){Abs a = new Abs();a.amethod();}public void myfunc(){System.out.println("My func");}public void amethod(){myfunc();} } 1) The code will compile and run, printing out the words "My Func" 2) The compiler will complain that the Base class is not declared as abstract. 3) The code will compile but complain at run time that the Base class has non abstract methods 4) The compiler will complain that the method myfunc in the base class has no body, nobody at all to looove it 問(wèn)題5)你為什么可能會(huì)定義一個(gè)native 方法呢? 1) To get to access hardware that Java does not know about 2) To define a new data type such as an unsigned integer 3) To write optimised code for performance in a language such as C/C++ 4) To overcome the limitation of the private scope of a method 問(wèn)題6)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? class Base{public final void amethod(){System.out.println("amethod");} } public class Fin extends Base{public static void main(String argv[]){Base b = new Base();b.amethod();} } 1) Compile time error indicating that a class with any final methods must be declared final itself 2) Compile time error indicating that you cannot inherit from a class with final methods 3) Run time error indicating that Base is not defined as final 4) Success in compilation and output of "amethod" at run time. 問(wèn)題7)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? public class Mod{public static void main(String argv[]){}public static native void amethod(); } 1) Error at compilation: native method cannot be static 2) Error at compilation native method must return value 3) Compilation but error at run time unless you have made code containing native amethod available 4) Compilation and execution without error 問(wèn)題8)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? private class Base{}public class Vis{transient int iVal;public static void main(String elephant[]){} } 1) Compile time error: Base cannot be private 2) Compile time error indicating that an integer cannot be transient 3) Compile time error transient not a data type 4) Compile time error malformed main method 問(wèn)題9)當(dāng)你試著編譯運(yùn)行下面的兩個(gè)放在同一個(gè)目錄的文件的時(shí)候,可能會(huì)發(fā)生什么? //File P1.java package MyPackage; class P1{void afancymethod(){System.out.println("What a fancy method");} } //File P2.java public class P2 extends P1{afancymethod(); } 1) Both compile and P2 outputs "What a fancy method" when run 2) Neither will compile 3) Both compile but P2 has an error at run time 4) P1 compiles cleanly but P2 has an error at compile time 問(wèn)題10)下面的哪一個(gè)聲明是合法的? 1) public protected amethod(int i) 2) public void amethod(int i) 3) public void amethod(void) 4) void public amethod(int i) 答案 答案1) 1) The code will compile and run, printing out the words "My Func" 一個(gè)abstract 類(lèi)可以有非abstract 方法,但是任何擴(kuò)展它的類(lèi)必須實(shí)現(xiàn)所有的abstract 方法。 答案2) 4) The code will compile but will complain at run time that main is not correctly defined main 的簽名包含一個(gè)String 參數(shù),而不是string 數(shù)組。 答案3) 1) public 2) private 4) transient 雖然有些文本使用friendly 來(lái)表示可見(jiàn)性,但它不是一個(gè)Java 保留字。注意,測(cè)試很可能包含要求你從列表中識(shí)別Java 關(guān)鍵字的問(wèn)題。 答案4) 2) The compiler will complain that the Base class is not declared as abstract. 當(dāng)我使用我的JDK1.1 編譯器時(shí)的真正的錯(cuò)誤信息是: Abs.java:1: class Base must be declared abstract. It does not define void myfunc() from class Base. class Base{ ^ 1 error 答案5) 1) To get to access hardware that Java does not know about 3) To write optimised code for performance in a language such as C/C++ 雖然創(chuàng)建“純正的Java”代碼值得鼓勵(lì),但是為了允許平臺(tái)的獨(dú)立性,我們不能將此作為信仰,有很多時(shí)候,我們是需要native 代碼的。 答案6) 4) Success in compilation and output of "amethod" at run time. 這段代碼調(diào)用Base 類(lèi)中的amethod 版本。如果你在Fin 中試著執(zhí)行amethod 的重寫(xiě)版本,你會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤。 答案7) 4) Compilation and execution without error 因?yàn)闆](méi)有調(diào)用native 方法,因此運(yùn)行時(shí)不會(huì)發(fā)生錯(cuò)誤。 答案8) 1) Compile time error: Base cannot be private 一個(gè)Base 類(lèi)這樣的頂級(jí)類(lèi)不能定義為private。 答案9) 4) P1 compiles cleanly but P2 has an error at compile time 雖然P2 在P1 的同一個(gè)路徑下,但是P1 用package 語(yǔ)句聲明了,所以對(duì)于P2 不可見(jiàn)。 答案10) 2) public void amethod(int i) 如果你認(rèn)為選項(xiàng)3 這樣攜帶一個(gè)void 參數(shù)是合法的,你可能需要從你的頭腦中清空一些C/C++方面的知識(shí)。選項(xiàng)4 不合法是因?yàn)榉椒ǖ姆祷仡?lèi)型必須緊跟著出現(xiàn)在方法名之前。 目標(biāo)3,默認(rèn)的構(gòu)造方法對(duì)于一個(gè)給定的類(lèi),如果有一個(gè)默認(rèn)的構(gòu)造方法被創(chuàng)建或者定義了構(gòu)造方法的原型,則類(lèi)也被確定了。 本目標(biāo)需要注意這是一個(gè)精致小巧的目標(biāo),通過(guò)輕松的俯瞰Java 語(yǔ)言,對(duì)各方面集中研究來(lái)完成它吧。 什么是構(gòu)造方法?你需要通過(guò)明白構(gòu)造方法的概念來(lái)明白本節(jié)的目標(biāo)。簡(jiǎn)單來(lái)說(shuō),構(gòu)造方法是一種在類(lèi)實(shí)例化時(shí)自動(dòng)運(yùn)行的特殊類(lèi)型的方法。構(gòu)造器通常被用來(lái)初始化類(lèi)中的值。構(gòu)造器有和類(lèi)同樣的名字并且沒(méi)有返回值。你可能會(huì)在測(cè)驗(yàn)中被問(wèn)到這樣的問(wèn)題:跟類(lèi)有同樣名字的方法,但是有整型或者字符串型的返回值。你要多加小心并確信,任何被認(rèn)為是構(gòu)造方法的方法都是 沒(méi)有返回值的。如果一個(gè)方法有了類(lèi)同樣的名字但還有返回值,它不是構(gòu)造器。這里有一個(gè)例子,一個(gè)有構(gòu)造器的類(lèi),當(dāng)類(lèi)的實(shí)例被創(chuàng)建時(shí)打印字符串“Greeting from Crowle”: public class Crowle{public static void main(String argv[]){Crowle c = new Crowle();}Crowle(){System.out.println("Greetings from Crowle");} } 何時(shí)Java 提供默認(rèn)構(gòu)造方法?如果你沒(méi)有顯式定義任何構(gòu)造方法,編譯器會(huì)插入一個(gè)“后臺(tái)”的不可見(jiàn)的無(wú)參數(shù)的構(gòu)造方法。一般來(lái)說(shuō),這只是在理論上很重要。但是,一個(gè)重要的限制作用是,如果你沒(méi)有自己創(chuàng)建構(gòu)造方法,你就只能得到默認(rèn)的無(wú)參數(shù)的構(gòu)造方法了。如果你自己創(chuàng)建了構(gòu)造方法,Java 就不支持默認(rèn)的無(wú)參數(shù)的構(gòu)造方法了。一旦你創(chuàng)建了自己的構(gòu)造方法,你就釋放了默認(rèn)的無(wú)參數(shù)構(gòu)造方法。如果接下來(lái)你想試著創(chuàng)建一個(gè)不傳送任何參數(shù)的類(lèi)的實(shí)例(也就是通過(guò)一個(gè)零參數(shù)構(gòu)造方法調(diào)用這個(gè)類(lèi)),你會(huì)得到一個(gè)錯(cuò)誤。因此,一旦你為一個(gè)類(lèi)創(chuàng)建了任何的構(gòu)造方法,你需要?jiǎng)?chuàng)建一個(gè)無(wú)參數(shù)的構(gòu)造方法。這也是像Borland/Inprise 的JBuilder 這樣的代碼產(chǎn)生器在你生成類(lèi)的框架時(shí)會(huì)創(chuàng)建一個(gè)零參數(shù)構(gòu)造方法的原因之一。下面例子中的代碼不會(huì)被編譯。當(dāng)編譯器創(chuàng)建名字為c 的Base 類(lèi)的實(shí)例時(shí),它會(huì)插入一個(gè)指向無(wú)參數(shù)的構(gòu)造方法的調(diào)用。由于Base 有一個(gè)integer 型的構(gòu)造方法,無(wú)參數(shù)的構(gòu)造方法此時(shí)不允許存在,一個(gè)編譯期錯(cuò)誤產(chǎn)生了。可以通過(guò)在Base 類(lèi)中創(chuàng)建一個(gè)“什么都不干”的零參數(shù)構(gòu)造方法來(lái)修復(fù)這個(gè)錯(cuò)誤。 //Warning: will not compile. class Base{Base(int i){System.out.println("single int constructor");} } public class Cons {public static void main(String argv[]){Base c = new Base();} } //This will compile class Base{Base(int i){System.out.println("single int constructor");}Base(){} } public class Cons {public static void main(String argv[]){Base c = new Base();} } 默認(rèn)構(gòu)造方法的原型這個(gè)目標(biāo)要求你明白默認(rèn)構(gòu)造方法的原型。它當(dāng)然不能有參數(shù),并且最明顯的是默認(rèn)構(gòu)造方法沒(méi)有指定范圍,但你可以定義構(gòu)造方法為public 或者protected。構(gòu)造方法不能是native, abstract, static, synchronized 或final上面這句話源于一個(gè)編譯錯(cuò)誤信息。看起來(lái)像是新版本Java 的錯(cuò)誤信息質(zhì)量得到提高了似的。我聽(tīng)說(shuō)IBM 的新Java 編譯器有好的錯(cuò)誤報(bào)告。你也許被忠告過(guò)去使用多個(gè)合適版本的Java 編譯器來(lái)檢查你的代碼并查找錯(cuò)誤。 問(wèn)題 問(wèn)題1) 給定下面的類(lèi)定義 class Base{Base(int i){} } class DefCon extends Base{DefCon(int i){//XX} } 如果將標(biāo)記//XX 的地方替換為下面的行,哪一行是獨(dú)立合法的? 1) super(); 2) this(); 3) this(99); 4)super(99); 問(wèn)題2)給定下面的類(lèi) public class Crowle{public static void main(String argv[]){Crowle c = new Crowle();}Crowle(){System.out.println("Greetings from Crowle");} } 構(gòu)造方法會(huì)返回哪一種數(shù)據(jù)類(lèi)型? 1) null 2) integer 3) String 4) no datatype is returned 問(wèn)題3)當(dāng)你試著編譯運(yùn)行下面的代碼的時(shí)候,可能會(huì)發(fā)生什么? public class Crowle{public static void main(String argv[]){Crowle c = new Crowle();}void Crowle(){System.out.println("Greetings from Crowle");} } 1) Compilation and output of the string "Greetings from Crowle" 2) Compile time error, constructors may not have a return type 3) Compilation and output of string "void" 4) Compilation and no output at runtime 問(wèn)題4)當(dāng)你試著編譯運(yùn)行下面的類(lèi)的時(shí)候,可能會(huì)發(fā)生什么? class Base{Base(int i){System.out.println("Base");} } class Severn extends Base{public static void main(String argv[]){Severn s = new Severn();}void Severn(){System.out.println("Severn");} } 1) Compilation and output of the string "Severn" at runtime 2) Compile time error 3) Compilation and no output at runtime 4) Compilation and output of the string "Base" 問(wèn)題5)下面的哪一句陳述是正確的? 1) The default constructor has a return type of void 2) The default constructor takes a parameter of void 3) The default constructor takes no parameters 4) The default constructor is not created if the class has any constructors of its own. 答案 答案1) 4) super(99); 由于類(lèi)Base 定義了一個(gè)構(gòu)造方法,編譯器將不會(huì)插入默認(rèn)的0 參數(shù)的構(gòu)造方法。因此,super()的調(diào)用會(huì)引起一個(gè)錯(cuò)誤。一個(gè)this()調(diào)用試著在當(dāng)前類(lèi)中調(diào)用一個(gè)不存在的0 參數(shù)構(gòu)造方法,this(99)調(diào)用會(huì)引起一個(gè)循環(huán)引用并將引起一個(gè)編譯時(shí)錯(cuò)誤。 答案2) 4) no datatype is returned 如果定義了一個(gè)沒(méi)有數(shù)據(jù)類(lèi)型的構(gòu)造方法,那么沒(méi)有返回類(lèi)型是相當(dāng)明顯的 答案3) 4) Compilation and no output at runtime 方法Crowle 因?yàn)橛幸粋€(gè)返回類(lèi)型而不是構(gòu)造方法。因此,類(lèi)將會(huì)編譯并且在運(yùn)行時(shí)方法Crowle 不會(huì)調(diào)用。 答案4) 2) Compile time error 當(dāng)類(lèi)Severn 試著在類(lèi)Base 中調(diào)用0 參數(shù)構(gòu)造方法時(shí)會(huì)產(chǎn)生一個(gè)錯(cuò)誤。 答案5) 3) The default constructor takes no parameters 4) The default constructor is not created if the class has any constructors of its own. 選項(xiàng)1 相當(dāng)明顯,因?yàn)闃?gòu)造方法不會(huì)有返回類(lèi)型。選項(xiàng)2 不容易確定,Java 沒(méi)有為方法或構(gòu)造方法提供void 類(lèi)型。 目標(biāo)四,重載和覆寫(xiě)為任意方法定義合法的返回類(lèi)型,這些方法是在本類(lèi)或父類(lèi)中聲明過(guò)的相關(guān)方法。 本目標(biāo)需要注意的這個(gè)目標(biāo)可能相當(dāng)模糊,它主要是要求你理解重載和覆寫(xiě)的不同。為了增強(qiáng)你的目的性,你需要對(duì)于方法重載和方法覆寫(xiě)有基本的理解。請(qǐng)參看第六部分:重載,覆寫(xiě),運(yùn)行時(shí)類(lèi)型和面向?qū)ο?同一個(gè)類(lèi)中的方法我假定目標(biāo)中的相關(guān)方法是指有同樣名字的方法。如果一個(gè)類(lèi)中的兩個(gè)或者多個(gè)方法有同樣的名字,就被稱為方法重載。你可以在一個(gè)類(lèi)中有兩個(gè)同樣名字的方法,但是他們必須有不同的參數(shù)類(lèi)型和順序。通過(guò)參數(shù)的順序和類(lèi)型來(lái)區(qū)分兩個(gè)重載的方法。返回類(lèi)型對(duì)區(qū)分方法沒(méi)有幫助。下面的代碼會(huì)引起一個(gè)編譯時(shí)錯(cuò)誤:編譯器認(rèn)為amethod 試圖定義同樣的方法兩次。這就引起了一個(gè)像下面這樣的錯(cuò)誤, method redefined with different return type: void amethod(int) was int amethod(int) class Same{public static void main(String argv[]){Over o = new Over();int iBase=0;o.amethod(iBase);}//These two cause a compile time errorpublic void amethod(int iOver){System.out.println("Over.amethod");}public int amethod(int iOver){System.out.println("Over int return method");return 0;} } 返回值的類(lèi)型不能幫助區(qū)分兩個(gè)方法。 子類(lèi)中的方法你可以在一個(gè)子類(lèi)中重載一個(gè)方法,所需要的就是新方法有不同的參數(shù)順序和類(lèi)型。參數(shù)的名字或者返回類(lèi)型都不作考慮。如果你想重寫(xiě)一個(gè)方法,即在子類(lèi)中完全取代它的功能,重寫(xiě)后的方法必須跟基類(lèi)中被取代的原始方法有完全相同的簽名。這就包括了返回值。如果你在子類(lèi)中創(chuàng)建了一個(gè)有同樣名字和簽名但是有不同返回值的方法,你將會(huì)得到一個(gè)跟上例同樣的錯(cuò)誤信息: method redefined with different return type: void amethod(int) was int amethod(int) 編譯器認(rèn)為這是錯(cuò)誤的嘗試方法重載,而不認(rèn)為是方法重寫(xiě)。static 方法不能被重寫(xiě)。如果你認(rèn)為重寫(xiě)只是在子類(lèi)中簡(jiǎn)單的替換了一個(gè)方法,你就很容易認(rèn)為static 方法也能被重寫(xiě)。事實(shí)上,我有很多包含人們舉例指明static 方法能被重寫(xiě)的代碼的郵件。然而,這些并沒(méi)有考慮方法重寫(xiě)在運(yùn)行時(shí)決定哪個(gè)版本的方法被調(diào)用的細(xì)節(jié)問(wèn)題。下面的代碼似乎表明static 方法是怎樣被重寫(xiě)的。 class Base{static void amethod(){System.out.println("Base.amethod");} } public class Cravengib extends Base{public static void main(String arg[]){Cravengib cg = new Cravengib();cg.amethod();}static void amethod(){System.out.println("Cravengib.amethod");} }如果你編譯并運(yùn)行這段代碼,你會(huì)發(fā)現(xiàn)輸出文本Cravengib.amethod,這似乎很好的指明了重寫(xiě)。然而,對(duì)于重寫(xiě),還有相對(duì)于在子類(lèi)中使用一個(gè)方法簡(jiǎn)單替換另一個(gè)方法更多的東西。還有運(yùn)行時(shí)決定的方法基于引用的類(lèi)的類(lèi)型的問(wèn)題,這可以通過(guò)創(chuàng)建正在被實(shí)例化的類(lèi)的引用類(lèi)型(實(shí)例初始化語(yǔ)句的左半部分)來(lái)說(shuō)明。在上面的例子中,因?yàn)槊纸衋method 的方法與類(lèi)發(fā)生了關(guān)聯(lián),而不是與特定的類(lèi)的實(shí)例相關(guān)聯(lián),它不在乎什么類(lèi)型的類(lèi)正在創(chuàng)建它,而僅僅在意引用的類(lèi)型。因此,如果你在調(diào)用amethod 前改變一下這一行, Base cg= new Cravengib() 你就會(huì)發(fā)現(xiàn)當(dāng)你運(yùn)行程序時(shí),你會(huì)得到輸出:Base.amethodcg 是一個(gè)類(lèi)Cravengib 在內(nèi)存中的一個(gè)Base 類(lèi)型的實(shí)例的引用(或者指針)。如果一個(gè)static方法被調(diào)用了,JVM 不會(huì)檢查什么類(lèi)型正在指向它,它只會(huì)調(diào)用跟Base 類(lèi)相關(guān)聯(lián)的方法的實(shí)例。 與上面的情況相對(duì)比:當(dāng)一個(gè)方法被重寫(xiě)時(shí),JVM 通過(guò)句柄檢查正在指向的類(lèi)的類(lèi)型,并調(diào)用此類(lèi)型相關(guān)的方法。可以結(jié)束這個(gè)例子了,如果你將兩個(gè)版本的amethod 方法改變?yōu)榉莝tatic,并依然創(chuàng)建類(lèi): Base cg= new Cravengib() 編譯并運(yùn)行上述代碼,你會(huì)發(fā)現(xiàn)amethod 已經(jīng)被重寫(xiě)了,并且輸出Cravengib.amethod。 問(wèn)題 問(wèn)題1)給定下面的類(lèi)定義 public class Upton{public static void main(String argv[]){}public void amethod(int i){}//Here } 下面哪一個(gè)在替換//Here 后是合法的? 1) public int amethod(int z){} 2) public int amethod(int i,int j){return 99;} 3) protected void amethod(long l){ } 4) private void anothermethod(){} 問(wèn)題2)給定下面的類(lèi)定義 class Base{public void amethod(){System.out.println("Base");} } public class Hay extends Base{public static void main(String argv[]){Hay h = new Hay();h.amethod();} } 下面在類(lèi)Hay 中的哪一個(gè)方法將會(huì)編譯并使程序打印出字符串"Hay"? 1) public int amethod(){ System.out.println("Hay");} 2) public void amethod(long l){ System.out.println("Hay");} 3) public void amethod(){ System.out.println("Hay");} 4) public void amethod(void){ System.out.println("Hay");} 問(wèn)題3)給定下面的類(lèi)定義 public class ShrubHill{public void foregate(String sName){}//Here } 下面的哪一個(gè)方法可以合法的直接替換//Here? 1) public int foregate(String sName){} 2) public void foregate(StringBuffer sName){} 3) public void foreGate(String sName){} 4) private void foregate(String sType){} 答案 答案1) 2) public int amethod(int i, int j) {return 99;} 3) protected void amethod (long l){} 4) private void anothermethod(){} 選項(xiàng)1 由于兩個(gè)原因不會(huì)被編譯。第一個(gè)相當(dāng)明顯,因?yàn)樗蠓祷匾粋€(gè)integer。另一個(gè)是試著直接在類(lèi)內(nèi)部重新定義一個(gè)方法。把參數(shù)的名字從i 換成z 是無(wú)效的,并且一個(gè)方法不能在同一個(gè)類(lèi)里重寫(xiě)。 答案2) 3) public void amethod(){ System.out.println("Hay");} 選項(xiàng)3 重寫(xiě)了類(lèi)Base 的方法,因此任何0 參數(shù)調(diào)用都調(diào)用這個(gè)版本。 選項(xiàng)1 將會(huì)返回一個(gè)表示你嘗試重新定義一個(gè)不同返回類(lèi)型的方法的錯(cuò)誤。選項(xiàng)2 將會(huì)編譯對(duì)于amethod()調(diào)用Base 類(lèi)的方法,并且輸出字符串"Base"。選項(xiàng)4 是為了抓住滿腦子C/C++的人而設(shè)計(jì)的。Java 里沒(méi)有void 方法參數(shù)這樣的事。 答案3) 2) public void foregate(StringBuffer sName){} 3) public void foreGate(String sName){} 選項(xiàng)1 是試著定義一個(gè)方法兩次,有一個(gè)int 返回值并不能幫助將它與存在的foregate 方法相區(qū)分。而像選項(xiàng)4 那樣改變方法的參數(shù)名,也不能與存在的方法相區(qū)分。注意,選項(xiàng)2里的foreGate 方法有一個(gè)大寫(xiě)的G。 第2 章 流程控制和差錯(cuò)處理 目標(biāo)一 if 和switch 語(yǔ)句用if 和switch 編寫(xiě)代碼,識(shí)別這些語(yǔ)句的合法參數(shù)類(lèi)型 If/else 語(yǔ)句在java 中If/else 結(jié)構(gòu)和你所了解的其他語(yǔ)言一樣,switch/case 語(yǔ)句有一些自己的特點(diǎn) if/else 的語(yǔ)法是 if(boolean condition){ //the boolean was true so do this }else { //do something else }與在Visual Basic 語(yǔ)句中不同,Java 中不存在"then"關(guān)鍵字花括號(hào)在Java 中是一個(gè)常用的復(fù)合語(yǔ)句的指示器,它可以使你把多行代碼作為一些判斷語(yǔ)句的一個(gè)結(jié)果來(lái)執(zhí)行。這可以被看作一個(gè)程序塊。else 部分常常是可選的。你可以像以下這樣鏈接多個(gè)if/else 語(yǔ)句(但是在鏈接了幾個(gè)之后你就要考慮使用case 結(jié)構(gòu)來(lái)代替了) int i=1; if(i==1){ //some code } else if (i==2){ //some code } else{ //some code }Java 中if 語(yǔ)句的一個(gè)特性是必須帶一個(gè)boolean 類(lèi)型的值。你不能像使用C/C++習(xí)慣的那樣使用任何非零的數(shù)值來(lái)表示true,而用零來(lái)表示false。因此,在Java 中以下語(yǔ)句將不會(huì)被編譯 int k =-1; if(k){//Will not compile!System.out.println("do something"); } 因?yàn)槟惚仨毭鞔_的使k 的判斷語(yǔ)句返回一個(gè)boolean 類(lèi)型的值,就像下面的例子 if(k == -1){System.out.println("do something"); //Compiles OK! } 當(dāng)在C/C++中時(shí),你可以去掉花括號(hào),如下 boolean k=true; if(k) System.out.println("do something"); 這有時(shí)候被認(rèn)為是不好的設(shè)計(jì)風(fēng)格,因?yàn)槿绻闵院笠薷拇a來(lái)包含更多語(yǔ)句,他們就會(huì)在條件語(yǔ)句塊外,像這樣 if(k) System.out.println("do something"); System.out.println("also do this"); 第二個(gè)輸出語(yǔ)句將總會(huì)被執(zhí)行 switch 語(yǔ)句Peter van der Lindens 對(duì)于switch 語(yǔ)句的評(píng)價(jià)概括起來(lái)就像他所說(shuō)的“毀滅于switch 語(yǔ)句”因此,這是一個(gè)你必須花費(fèi)更多的精力關(guān)注的問(wèn)題。switch 語(yǔ)句的參數(shù)必須是一個(gè)byte,char,short 或int 類(lèi)型的變量。你也許會(huì)遇到考試題中用float 或者long 做switch 語(yǔ)句的參數(shù)。有一個(gè)非常普遍的問(wèn)題似乎就是,關(guān)于在執(zhí)行switch 語(yǔ)句的過(guò)程中使用break 語(yǔ)句。這里有一個(gè)這類(lèi)問(wèn)題的例子。 int k=10; switch(k){case 10:System.out.println("ten");case 20:System.out.println("twenty"); }常識(shí)判斷,執(zhí)行case 語(yǔ)句后面的指令,然后碰到另一個(gè)case 語(yǔ)句,編譯器就應(yīng)該結(jié)束執(zhí)行switch 語(yǔ)句。但是,就像程序設(shè)計(jì)者所熟知的,case 語(yǔ)句只在碰到break 語(yǔ)句的時(shí)候才終止執(zhí)行。結(jié)果,在上面例子中,ten 和twenty 都將被輸出。可以作為一個(gè)問(wèn)題提出來(lái)的另一個(gè)小的特性就是使用default 語(yǔ)句。 注意:default 語(yǔ)句不是必須在case 語(yǔ)句的結(jié)尾處出現(xiàn) 按照慣例default 語(yǔ)句是放在case 選項(xiàng)的結(jié)尾處,所以通常代碼寫(xiě)成如下形式 int k=10;switch(k){case 10:System.out.println("ten");break;case 20:System.out.println("twenty");break;default:System.out.println("This is the default output"); }這種方法反映大多數(shù)人的思維方式。當(dāng)你嘗試其他可能情況時(shí),會(huì)執(zhí)行default 輸出。但是,如果不是被要求的話,把defalt 語(yǔ)句寫(xiě)在switch 語(yǔ)句的頂部,在語(yǔ)法上也是正確的。 int k=10; switch(k){default: //Put the default at the bottom, not hereSystem.out.println("This is the default output");break;case 10:System.out.println("ten");break;case 20:System.out.println("twenty");break; } if 和switch 語(yǔ)句的合法參數(shù)正如先前所提到的,if 語(yǔ)句只能用boolean 類(lèi)型參數(shù),而switch 語(yǔ)句只能用byte,char,short 或者int 類(lèi)型作參數(shù)。 三項(xiàng) ?操作符一些程序員主張三項(xiàng)操作符很有用。我不這么認(rèn)為。在目標(biāo)中并沒(méi)有特別提到它,所以如果在考試中出現(xiàn)的話請(qǐng)告訴我。 其他流程控制語(yǔ)句雖然公布的目標(biāo)只提到了if/else 和case 語(yǔ)句,考試中也許會(huì)涉及do/while 和while loop語(yǔ)句。 練習(xí) 習(xí)題1) 創(chuàng)建一個(gè)文件含有一個(gè)公共類(lèi)叫IfElse。創(chuàng)建一個(gè)方法叫g(shù)o,它接收main 方法的字符串?dāng)?shù)組參數(shù)作為它的參數(shù)。在這個(gè)方法中創(chuàng)建了一個(gè)if/else 程序塊,用來(lái)查看來(lái)自數(shù)組的第一個(gè)元素,用字符串的equals 方法來(lái)判斷輸出。如果為"true"則打印"ok",如果為"false"則打印"Not ok",如果是true 或false 以外的字符串則打印"Invalid command parameter",用一個(gè)if/else if/else 語(yǔ)句這樣的次序進(jìn)行設(shè)計(jì)。 習(xí)題 2) 修改這個(gè)IfElse 類(lèi),使if 語(yǔ)句可以檢查傳到go 方法的字符串?dāng)?shù)組是否是零長(zhǎng)度串,使用數(shù)組length 域來(lái)檢查。如果長(zhǎng)度為零則輸出"No parameter supplied",把現(xiàn)有的if/else if/else塊放在這個(gè)練習(xí)的else 中,使程序能實(shí)現(xiàn)原版本的功能。 答案 答案 1) public class IfElse{public static void main(String argv[]){IfElse ie = new IfElse();ie.go(argv);}public void go(String[] sa){String s = sa[0];if(s.equals("true")){System.out.println("OK");}else if(s.equals("false")){System.out.println("Not OK");}else{System.out.println("Invalid command parameter");}} } 答案 2) public class IfElse{public static void main(String argv[]){IfElse ie = new IfElse();ie.go(argv);}public void go(String[] sa){if(sa.length ==0){System.out.println("No parameter supplied");}else{String s = sa[0];if(s.equals("true")){System.out.println("OK");}else if(s.equals("false")){System.out.println("Not OK");}else{System.out.println("Invalid command parameter");}}} } 問(wèn)題 問(wèn)題1) 編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class MyIf{boolean b;public static void main(String argv[]){MyIf mi = new MyIf();}MyIf(){if(b){System.out.println("The value of b was true");}else{System.out.println("The value of b was false");}} } 1) Compile time error variable b was not initialised 2) Compile time error the parameter to the if operator must evaluate to a boolean 3) Compile time error, cannot simultaneously create and assign value for boolean value 4) Compilation and run with output of false 問(wèn)題2) 編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class MyIf{public static void main(String argv[]){MyIf mi = new MyIf();}MyIf(){boolean b = false;if(b=false){System.out.println("The value of b is"+b);}} } 1) Run time error, a boolean cannot be appended using the + operator 2) Compile time error the parameter to the if operator must evaluate to a boolean 3) Compile time error, cannot simultaneously create and assign value for boolean value 4) Compilation and run with no output 問(wèn)題3 ) 編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class MySwitch{public static void main(String argv[]){MySwitch ms= new MySwitch();ms.amethod();}public void amethod(){char k=10;switch(k){default:System.out.println("This is the default output");break;case 10:System.out.println("ten");break;case 20:System.out.println("twenty");break;}} } 1) None of these options 2) Compile time error target of switch must be an integral type 3) Compile and run with output "This is the default output" 4) Compile and run with output "ten" 問(wèn)題4) 編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class MySwitch{public static void main(String argv[]){MySwitch ms= new MySwitch();ms.amethod();}public void amethod(){int k=10;switch(k){default: //Put the default at the bottom, not hereSystem.out.println("This is the default output");break;case 10:System.out.println("ten");case 20:System.out.println("twenty");break;}} } 1) None of these options 2) Compile time error target of switch must be an integral type 3) Compile and run with output "This is the default output" 4) Compile and run with output "ten" 問(wèn)題5) 下面哪個(gè)是不能用于switch 語(yǔ)句的參數(shù)? 1) byte b=1; 2) int i=1; 3) boolean b=false; 4) char c='c'; 答案 答案 1) 4) Compilation and run with output of false 因?yàn)閎oolean b 在類(lèi)級(jí)中被創(chuàng)建,它不需明確初始化,而且它有默認(rèn)的boolean 值false。if語(yǔ)句判斷一個(gè)boolean 值,所以b 符合這個(gè)要求。 答案 2) 4) Compilation and run with no output 因?yàn)閎 是boolean 類(lèi)型,if 語(yǔ)句不會(huì)產(chǎn)生錯(cuò)誤。如果b 是任何其他的數(shù)據(jù)類(lèi)型,在你試圖賦值而不是比較的時(shí)候錯(cuò)誤就產(chǎn)生了。下列表達(dá) if(b=false) 通常是一個(gè)程序員的錯(cuò)誤。程序員大多要表現(xiàn) if (b==false) 如果b 的類(lèi)型是boolea 以外的任意類(lèi)型,會(huì)導(dǎo)致編譯期錯(cuò)誤。if 表達(dá)式的要求是必須返回一個(gè)boolean 類(lèi)型,因?yàn)?b=false)返回一個(gè)boolean 類(lèi)型,所以被接受(如果無(wú)用處)。 答案 3) 4) Compile and run with output "ten" 答案 4) 1) None of these options 因?yàn)橄戮浜笕鄙賐reak 語(yǔ)句 case 10; 實(shí)際輸出結(jié)果會(huì)是"ten"接著是"twenty" 答案 5) 1) byte b=1; 2) int i=1; 4) char c='c'; switch 語(yǔ)句可以使用byte,char 或int 作參數(shù)。 目標(biāo)二 循環(huán),break 和continue用循環(huán)格式編寫(xiě)代碼,使用帶標(biāo)簽和不帶標(biāo)簽的break 和continue 語(yǔ)句,聲明循環(huán)計(jì)數(shù)器的值在循環(huán)執(zhí)行中或循環(huán)結(jié)束時(shí) for 語(yǔ)句最常用的循環(huán)方法就是應(yīng)用for 語(yǔ)句。對(duì)于for 語(yǔ)句在其他的編程語(yǔ)言中有非常相似的結(jié)構(gòu)。比如C/C++和perl 就有for 結(jié)構(gòu)。很多程序員在循環(huán)中使用for 結(jié)構(gòu),因?yàn)槠浜?jiǎn)潔,自含,容易理解而且不容易混亂。類(lèi)似C++而與C 語(yǔ)言不同,循環(huán)控制變量可以在for 語(yǔ)句中定義和初始化。如下 public class MyLoop{public static void main(String argv[]){MyLoop ml = new MyLoop();ml.amethod();}public void amethod(){for(int K=0;K<5l;K++){System.out.println("Outer "+K);for(int L=0;L<5;L++){System.out.println("Inner "+L);}}} } 內(nèi)循環(huán)代碼在每次外循環(huán)執(zhí)行時(shí)會(huì)循環(huán)執(zhí)行五次。所以輸出為: Outer 0; Inner 0 Inner 1 Inner 2 Inner 3 inner 4 Outer 1; Inner 0 Inner 2 for 語(yǔ)句和Visual Basic 的for/next 循環(huán)一樣。你可以認(rèn)為它的語(yǔ)法是 for(initialization; conditional expression;increment)其中條件表達(dá)式必須是boolean 判斷,就像if 語(yǔ)句的簡(jiǎn)單形式。在上例的代碼中,for 語(yǔ)句緊跟著是花括號(hào)中的程序塊。類(lèi)似if 語(yǔ)句,當(dāng)不需要使用程序塊時(shí),你可以使用下面這樣的簡(jiǎn)單形式 for(int i=0;i<5;i++) System.out.println(i);在任何版本中你都不能用分號(hào)來(lái)結(jié)束一個(gè)for 行,如果你這么做,for 循環(huán)就會(huì)原地打轉(zhuǎn)直到條件滿足,然后就會(huì)以“直線”的方式執(zhí)行下面的代碼。在此例中你不是必須在for循環(huán)中定義變量,但是如果在循環(huán)中定義變量,當(dāng)跳出循環(huán)時(shí)變量也就跳出了它的作用域。按照變量作用域盡可能小的說(shuō)法,這可以看作是一個(gè)優(yōu)點(diǎn)。 for 中的塊為空在語(yǔ)法中也是正確的,這樣循環(huán)會(huì)永遠(yuǎn)進(jìn)行下去 for(;;){System.out.println("forever"); } 但是用while(true)的形式可能會(huì)更加簡(jiǎn)潔 while(true){System.out.println("true"); } while 循環(huán)和do 循環(huán),意料之中while 和do 循環(huán)的運(yùn)行就像你想象的一樣,和在其他語(yǔ)言中相同。因此,while 會(huì)依照判斷執(zhí)行零到多次,而do 會(huì)執(zhí)行一到多次。while 循環(huán)的語(yǔ)法是: while(condition){bodyOfLoop; }像if 語(yǔ)句一樣,條件是一個(gè)boolean 類(lèi)型的判斷。同樣,你不能像C/C++習(xí)慣的那樣用零來(lái)代表false,而用任意其他值來(lái)代表true。所以,你可能會(huì)像下面那樣創(chuàng)建一個(gè)while 循環(huán) while(i<4){i++;System.out.println("Loop value is :"i); }注意,如果變量i 為4,或者比4 大,當(dāng)你到達(dá)while 語(yǔ)句時(shí),將沒(méi)有輸出。相反,do 循環(huán)總是會(huì)執(zhí)行一次。所以,不管進(jìn)入循環(huán)時(shí)變量i 的值是什么,以下代碼總是會(huì)得到至少一個(gè)輸出。 goto 語(yǔ)句,科學(xué)還是迷信?Java 的設(shè)計(jì)者決定同意寫(xiě)過(guò)著名文章"Goto 有害"的編程領(lǐng)袖Edsger Dijkstra 的觀點(diǎn)。因?yàn)椴患舆x擇的使用goto 語(yǔ)句會(huì)導(dǎo)致“意大利面條似的代碼”難以維護(hù),不可使用,而且這被認(rèn)為是不好的編程風(fēng)格。“意大利面條似的代碼”是指不容易表述邏輯開(kāi)始和結(jié)束的代碼。goto 語(yǔ)句有時(shí)會(huì)被說(shuō)成“無(wú)條件跳轉(zhuǎn)”,也就是可能會(huì)寫(xiě)這樣的代碼,不進(jìn)行判斷就從程序的一部分跳轉(zhuǎn)到另外一處。這在某些情況下是有用的,即Java 為break 和continue 關(guān)鍵字提供了有標(biāo)簽和無(wú)標(biāo)簽兩個(gè)版本。 public class Br{public static void main(String argv[]){Br b = new Br();b.amethod();}public void amethod(){for(int i=0;i <3;i ++){System.out.println("i"+i+"\n");outer://<==Point of this exampleif(i>2){break outer;//<==Point of this example}//End of iffor(int j=0; j <4 && i<3; j++){System.out.println("j"+j);}//End of for}//End of for}//end of Br method } 然后,你需要挑出代碼中哪個(gè)是要輸出的字母組合。順便說(shuō)一下,"\n"是輸出一個(gè)空白行。 跳轉(zhuǎn)到標(biāo)簽在有些條件下從內(nèi)循環(huán)跳到外循環(huán)常被描述,你可以使用帶標(biāo)簽的break 和continue 語(yǔ)句來(lái)實(shí)現(xiàn)它。一個(gè)標(biāo)簽是一個(gè)簡(jiǎn)單的非關(guān)鍵字,后面跟一個(gè)冒號(hào)。通過(guò)在break 或continue后使用標(biāo)簽,你的代碼可以跳轉(zhuǎn)到此標(biāo)簽處。這是便捷的實(shí)現(xiàn)部分條件循環(huán)的方法。你當(dāng)然可以用if 語(yǔ)句,但是一個(gè)break 語(yǔ)句更方便。按照Elliotte Rusty Harold,一個(gè)著名的Java作者所說(shuō),“在整個(gè)Java1.0.1 源代碼中,只用了七個(gè)continue 語(yǔ)句寫(xiě)java 包。”這意味著在實(shí)際編程中你可能不會(huì)得到充分的練習(xí),所以為了考試你要花費(fèi)更大的精力來(lái)學(xué)好它。考試題的編寫(xiě)者好像熱愛(ài)設(shè)計(jì)費(fèi)解的網(wǎng)狀的帶有break 和continue 語(yǔ)句的循環(huán),你可能永遠(yuǎn)不會(huì)遇到有好的設(shè)計(jì)的代碼。 關(guān)鍵概念break 語(yǔ)句完全放棄執(zhí)行當(dāng)前循環(huán),continue 語(yǔ)句只放棄整個(gè)循環(huán)中當(dāng)前本次循環(huán) 做下面的例子 public class LabLoop{public static void main(String argv[]){LabLoop ml = new LabLoop();ml.amethod();}public void amethod(){outer:for(int i=0;i<2;i++){for(int j=0;j<3;j++){if(j>1)//Try this with break instead of continuecontinue outer;System.out.println("i "+ i + " j "+j);}}//End of outer forSystem.out.println("Continuing");} } 這個(gè)版本有以下輸出 i 0 j 0 i 0 j 1 i 1 j 0 i 1 j 1 Continuing 如果你用break 替換continue,i 計(jì)數(shù)器會(huì)在零處停止,因?yàn)橥庋h(huán)會(huì)被放棄,而不會(huì)簡(jiǎn)單的進(jìn)入下一個(gè)遞增。 問(wèn)題 問(wèn)題1) 編譯運(yùn)行一個(gè)方法中的下列代碼時(shí)會(huì)發(fā)生什么情況? for(int i=0;i<5;){System.out.println(i);i++;continue; } 1) Compile time error, malformed for statement 2) Compile time error continue within for loop 3) runtime error continue statement not reached 4) compile and run with output 0 to 4 問(wèn)題2)編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class LabLoop{public static void main(String argv[]){LabLoop ml = new LabLoop();ml.amethod();mainmethod:System.out.println("Continuing");}public void amethod(){outer:for(int i=0;i<2;i++){for(int j=0;j<3;j++){if(j>1)break mainmethod;System.out.println("i "+ i + " j "+j);}}//End of outer for} } 1) i 0 j 0 i 0 j 1 Continuing 2) i 0 j 0 i 0 j 1 i 1 j 0 i 1 j 1 Continuing 3) Compile time error 4) i 0 j 0 i 0 j 1 i 1 j 0 i 1 j 1 i 2 j 1 Continuing 問(wèn)題3)編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public void amethod(){outer:for(int i=0;i<2;i++){for(int j=0;j<2;j++){System.out.println("i="+i + " j= "+j);if(i >0)break outer;}}System.out.println("Continuing with i set to ="+i); } 1) Compile time error 2) i=0 j= 0 i=0 j= 1 i=1 j= 0 3) i=0 j= 0 i=0 j= 1 i=1 j= 0 i=2 j= 0 4) i=0 j= 0 i=0 j= 1 問(wèn)題4)編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? int i=0; while(i>0){System.out.println("Value of i: "+i); } do{System.out.println(i); } while (i <2); 1) Value of i: 0 followed by 0 1 2 2) 0 1 2 3) Value of i: 0 Followed by continuous output of 0 4) Continuous output of 0 問(wèn)題5) 編譯運(yùn)行下列代碼時(shí)會(huì)發(fā)生什么情況? public class Anova{public static void main(String argv[]){Anova an = new Anova();an.go();}public void go(){int z=0;for(int i=0;i<10; i++,z++){System.out.println(z);}for(;;){System.out.println("go");}} } 1) Compile time error, the first for statement is malformed 2) Compile time error, the second for statement is malformed 3) Output of 0 to 9 followed by a single output of "go" 4) Output of 0 to 9 followed by constant output of "go" 問(wèn)題6)下列代碼的輸出結(jié)果是什么? public class MyFor{public static void main(String argv[]){int i;int j;outer:for (i=1;i <3;i++)inner:for(j=1; j<3; j++) {if (j==2)continue outer;System.out.println("Value for i=" + i + " Value for j=" +j);}} } 1) Value for i=1 value for j=1 2) Value for i=2 value for j=1 3) Value for i=2 value for j=2 4) Value for i=3 value for j=1 答案 答案 1) 4) compile and run with output 0 to 4 這是一個(gè)很奇怪但是完全正確的語(yǔ)句 答案 2) 3) Compile time error 你不能武斷的跳入另一個(gè)方法,在goto 語(yǔ)句中會(huì)帶來(lái)很多有害的結(jié)果。 答案 3) 1) Compile time error 這實(shí)際上不是關(guān)于break 和continue 的問(wèn)題。這段代碼不會(huì)被編譯,因?yàn)樽兞繉?duì)for 循環(huán)外部來(lái)說(shuō)永遠(yuǎn)是不可見(jiàn)的。所以最后的System.out.println 語(yǔ)句會(huì)引起編譯時(shí)錯(cuò)誤。 答案 4) 1) Continuous output of 0 沒(méi)有值被增加,而且如果第一次判斷不為真時(shí)while 循環(huán)將不會(huì)執(zhí)行。 答案 5) 4) Output of 0 to 9 followed by constant output of "go" 第一個(gè)for 循環(huán)結(jié)構(gòu)不常用但是完全正確。 答案6) 1) Value for i=1 value for j=1 2) Value for i=2 value for j=1 目標(biāo)三 try/catch 和方法重寫(xiě)編寫(xiě)代碼合理使用異常和異常處理機(jī)制(try catch finally),定義和重寫(xiě)方法拋出異常.一個(gè)異常情況是當(dāng)程序進(jìn)入一個(gè)不是很正常的狀態(tài).異常捕獲有時(shí)是指錯(cuò)誤捕獲.一個(gè)典型的異常例子是當(dāng)程序試圖打開(kāi)一個(gè)不存在的文件時(shí)或者你試圖訪問(wèn)一個(gè)數(shù)組中不存在的元素時(shí).try 和catch 語(yǔ)句是構(gòu)建Java 異常處理的一部份.不論C/C++還是Visua Basic 都沒(méi)有直接對(duì)應(yīng)Java 異常處理的結(jié)構(gòu).C++支持異常,但是是可選的,Visial Basic 支持On Error/Goto 錯(cuò)誤捕獲,這帶有早期不靈活的BASIC 編程時(shí)代的味道。Java 異常是Java 語(yǔ)言的一個(gè)結(jié)構(gòu)。例如如果你要執(zhí)行I/O 操作,你必須把它放在錯(cuò)誤處理中。你當(dāng)然可以不把它放在處理中,這毫無(wú)作用。下面是一個(gè)小代碼片斷,我用Borland/Inprise JBuilder 臨時(shí)停止控制臺(tái)輸出,等待按任意鍵繼續(xù) public class Try{import java.io.*;public static void main(String argv[]){Try t = new Try();t.go();}//End of mainpublic void go(){try{InputStreamReader isr = new InputStreamReader(System.in);BufferedReader br = new BufferedReader(isr);br.readLine();} catch(Exception e){/*Not doing anything when exception occurs*/} //End of trySystem.out.println("Continuing");}//End of go }在這個(gè)例子中,錯(cuò)誤出現(xiàn)時(shí)沒(méi)有任何處理,但是程序員一定知道錯(cuò)誤有可能發(fā)生。如果你移去try 和catch 字句,代碼將完全不會(huì)被編譯。編譯器知道I/O 方法會(huì)引發(fā)異常而且需要異常處理代碼。 與Visal Basic 和C/C++比較Visal Basic 或C/C++允許拋出混合“快且臟”的程序,假裝沒(méi)有錯(cuò)誤發(fā)生過(guò),Java 比它們嚴(yán)格些。記得DOS 的最初版本被他的創(chuàng)作者叫做QDOS,因?yàn)槭强烨遗K的DOS,看看我們已經(jīng)在這樣的環(huán)境下生活了多久。當(dāng)你開(kāi)始把快且臟的程序放到try/catch 塊當(dāng)中,也就開(kāi)始了真正的錯(cuò)誤跟蹤。這不是完全的束縛和編程律條,這只是勸說(shuō)你“做正確的事”。 方法重寫(xiě),拋出異常在子類(lèi)中一個(gè)重寫(xiě)的方法可能只拋出父類(lèi)中聲明過(guò)的異常或者異常的子類(lèi)。這只適用于方法重寫(xiě)而不適用于方法重載。所以如果如果一個(gè)方法有完全相同的名稱和參數(shù),它只能拋出父類(lèi)中聲明過(guò)的異常或者異常的子類(lèi)。但是它拋出很少或者不拋出異常。所以下面的例子 將不被編譯 import java.io.*; class Base{public static void amethod()throws FileNotFoundException{} } public class ExcepDemo extends Base{ //Will not compile, exception not in base version of methodpublic static void amethod()throws IOException{} } 如果在父類(lèi)中有拋出IOException 異常的方法, 在子類(lèi)中的方法拋出FileNotFoundException,代碼將編譯通過(guò)。再次,記住只適用于方法重寫(xiě),在方法重載中沒(méi)有類(lèi)似規(guī)定。一個(gè)在子類(lèi)中重寫(xiě)的方法可能會(huì)拋出異常。 throw 子句我們?cè)诖a中需要包含可能拋出異常的try/catch 塊的一個(gè)原因就是,你的代碼可以開(kāi)始展現(xiàn)出什么可能發(fā)生,而不是什么應(yīng)該發(fā)生。你可以通過(guò)使用throws 字句作為方法聲明的一部分來(lái)把異常放到堆棧中。這就有效的說(shuō)明“當(dāng)一個(gè)錯(cuò)誤發(fā)生時(shí),這個(gè)方法拋出這個(gè)異常,并且這個(gè)異常必須被調(diào)用它的方法捕獲”。這有一個(gè)使用throw 子句的例子 import java.io.*; public class Throws{public static void main(String argv[]){Throws t = new Throws();try{t.amethod();}catch (IOException ioe){}}public void amethod() throws IOException{FileInputStream fis = new FileInputStream("Throws.java");} } 問(wèn)題 問(wèn)題 1) 編譯運(yùn)行以下代碼會(huì)發(fā)生什么情況? import java.io.*; class Base{public static void amethod()throws FileNotFoundException{} } public class ExcepDemo extends Base{public static void main(String argv[]){ExcepDemo e = new ExcepDemo();}public static void amethod(){}protected ExcepDemo(){ try{ DataInputStream din = new DataInputStream(System.in); System.out.println("Pausing"); din.readChar(); System.out.println("Continuing"); this.amethod(); }catch(IOException ioe) {} } } 1) Compile time error caused by protected constructor 2) Compile time error caused by amethod not declaring Exception 3) Runtime error caused by amethod not declaring Exception 4) Compile and run with output of "Pausing" and "Continuing" after a key is hit 問(wèn)題2) 編譯運(yùn)行以下代碼會(huì)發(fā)生什么情況? import java.io.*; class Base{ public static void amethod()throws FileNotFoundException{} } public class ExcepDemo extends Base{ public static void main(String argv[]){ ExcepDemo e = new ExcepDemo(); } public static void amethod(int i)throws IOException{} private ExcepDemo(){ try{ DataInputStream din = new DataInputStream(System.in); System.out.println("Pausing"); din.readChar(); System.out.println("Continuing"); this.amethod(); }catch(IOException ioe) {} } } 1) Compile error caused by private constructor 2) Compile error caused by amethod declaring Exception not in base version 3) Runtime error caused by amethod declaring Exception not in base version 4) Compile and run with output of "Pausing" and "Continuing" after a key is hit 問(wèn)題3) 編譯運(yùn)行以下代碼會(huì)發(fā)生什么情況? import java.io.*; class Base{public static void amethod()throws FileNotFoundException{} } public class ExcepDemo extends Base{public static void main(String argv[]){ExcepDemo e = new ExcepDemo();}public static void amethod(int i)throws IOException{}private boolean ExcepDemo(){try{DataInputStream din = new DataInputStream(System.in);System.out.println("Pausing");din.readChar();System.out.println("Continuing");this.amethod();return true;}catch(IOException ioe) {}finally{System.out.println("finally");}return false;} } 1) Compilation and run with no output. 2) Compilation and run with output of "Pausing", "Continuing" and "finally" 3) Runtime error caused by amethod declaring Exception not in base version 4) Compile and run with output of "Pausing" and "Continuing" after a key is hit 問(wèn)題4) 以下哪個(gè)要求程序員添加外部的try/catch 異常處理。 1)Traversing each member of an array 2) Attempting to open a file 3) Attempting to open a network socket 4) Accessing a method in other class 問(wèn)題5) 編譯運(yùn)行以下代碼會(huì)發(fā)生什么情況? import java.io.*; class granary{public void canal() throws IOException{System.out.println("canal");} } public class mmill extends granary{public static void main(String argv[]){System.out.println("mmill");}public void canal(int i) throws Exception{System.out.println("mmill.canal");}public void canal(long i) {System.out.print("i");} } 1) Compile time error 2) Runtime errors 3) Compile error, mmill version of canal throws Exception not in granary version 4) Compilation and run with output of mmill 答案 問(wèn)題1) 答案 4) Compile and run with output of "Pausing" and "Continuing" after a key is hit 在子類(lèi)中的重寫(xiě)方法不能拋出在基類(lèi)中沒(méi)有拋出的異常。在這個(gè)例子中的方法amethod 沒(méi)有拋出異常,所以編譯不會(huì)出現(xiàn)問(wèn)題。構(gòu)造器不能是protect 類(lèi)型的。 問(wèn)題2) 答案 4) Compile and run with output of "Pausing" and "Continuing" after a key is hit 在這個(gè)版本中amethod 被重寫(xiě)了,沒(méi)有限制拋出或不拋出異常。 問(wèn)題3) 答案 1) Compilation and run with no output. 好的,我有點(diǎn)跑題了,注意構(gòu)造器有一個(gè)返回值。這把它變成了一個(gè)普通方法,而且當(dāng)沒(méi)有實(shí)例被創(chuàng)建時(shí)它將不會(huì)被調(diào)用。 問(wèn)題4) 答案 2) Attempting to open a file 3) Atempting to open a network socket 通常來(lái)說(shuō),所有的I/O 操作都需要外在的使用try/catch 塊的異常處理。JDK1.4 考試不明確的覆蓋I/O,但是也許會(huì)提到錯(cuò)誤處理的內(nèi)容。 問(wèn)題5) 答案 4) Compilation and run with output of mmill 什么樣的異常可以被拋出的限制只是應(yīng)用于被重寫(xiě)的方法,不用于被重載的方法。因?yàn)閏anal方法在mmill 版本中被重載(也就是它帶有了不同的參數(shù)類(lèi)型),所以不會(huì)有編譯或運(yùn)行錯(cuò)誤。 目標(biāo)四 什么情況下產(chǎn)生異常識(shí)別發(fā)生在代碼片斷指定位置的異常產(chǎn)生的結(jié)果。注意:異常必須是運(yùn)行時(shí)異常,一個(gè)被檢查的異常或者一個(gè)錯(cuò)誤(代碼可能包括try,catch 或者finally 子句,在任何可能的組合中) 目標(biāo)注釋這個(gè)目標(biāo)要求你理解可控的和不可控異常(一種你要寫(xiě)代碼捕獲,另一種不用),理解finally 子句如何工作。 檢查和非檢查異常雖然Java 強(qiáng)調(diào)你把捕獲異常代碼插入到他們可能發(fā)生的地方像I/O 操作等,這樣比較方便,但是如果你必須把這些代碼插入到程序員應(yīng)該控制程序狀態(tài)的地方,就不方便了。這種情況的一個(gè)例子就是遍歷數(shù)組的每一個(gè)元素。Java 中一個(gè)優(yōu)美的地方就是它不需要程序員的介入而明確的報(bào)告這種異常類(lèi)型的方式。這種自動(dòng)異常處理是由把異常分為可控和不可控異常實(shí)現(xiàn)的。像內(nèi)存耗盡或者訪問(wèn)到數(shù)組末尾這種情況會(huì)自動(dòng)識(shí)別,而試圖打開(kāi)不存在的文件就需要明確的try/catch 異常捕獲。 默認(rèn)的非檢查信息非檢查異常出現(xiàn)的一個(gè)默認(rèn)結(jié)果就是一個(gè)信息會(huì)被發(fā)送到控制臺(tái)。例如下面代碼 public class GetArg{public static void main(String argv[]){System.out.println(argv[0]);} } 如果編譯運(yùn)行代碼而不輸入命令行參數(shù),你會(huì)在控制臺(tái)得到一個(gè)錯(cuò)誤信息 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException at GetArg.main(GetArg.java:3) 同時(shí)這種情況是可以用于學(xué)習(xí)目的編寫(xiě)的程序的,在實(shí)際程序中,用戶大概不會(huì)訪問(wèn)控制臺(tái),也不會(huì)理解這樣的信息。最好編寫(xiě)代碼來(lái)檢查可能產(chǎn)生非運(yùn)行異常的數(shù)值。所以代碼可以修改成 public class GetArg{public static void main(String argv[]){if(argv.length ==0){System.out.println("Usage: GetArg param");}else{System.out.println(argv[0]);}} } 檢查異常要求程序員編寫(xiě)代碼處理檢查異常。如果沒(méi)有代碼處理檢查異常可能會(huì)出現(xiàn)編譯不通過(guò)。就像前面的代碼帶有try/catch 塊結(jié)構(gòu),但是寫(xiě)一個(gè)空catch 塊,不對(duì)產(chǎn)生的異常進(jìn)行任何處理也是可以的。當(dāng)然這通常不是好的設(shè)計(jì),當(dāng)你碰到要在異常時(shí)編寫(xiě)代碼的問(wèn)題時(shí),你可能還是要在這些代碼中做些有用的事。在catch 塊常做的兩件事是產(chǎn)生出錯(cuò)信息和打印錯(cuò)誤跟蹤。異常系統(tǒng)提供一個(gè)非常方便的方法,通過(guò)getMessage 方法產(chǎn)生常見(jiàn)的有意義的錯(cuò)誤信息。 看看以下代碼 import java.io.*;public class FileOut{public static void main(String argv[]){try{FileReader fr = new FileReader("FileOut.txt");}catch(Exception e){System.out.println(e.getMessage());}} } 如果你在一個(gè)沒(méi)有FileOut.txt 文件的目錄中編譯運(yùn)行這段代碼,你會(huì)得到一個(gè)錯(cuò)誤信息 FileOut.txt (No such file or directory) finally 子句在考試中你可能會(huì)被問(wèn)到,在什么情況try/catch 子句后的finally 方法會(huì)被執(zhí)行。簡(jiǎn)單回答就是finally 子句總是會(huì)被執(zhí)行,甚至當(dāng)你覺(jué)得它可能不會(huì)被執(zhí)行時(shí)。所以說(shuō),try/catch/finally 語(yǔ)句的執(zhí)行順序是你應(yīng)該注意確認(rèn)它在什么情況下是怎么執(zhí)行的。 關(guān)鍵概念不論在try/catch 部分是不是有返回,try/catch 塊的finally 子句總會(huì)執(zhí)行。少數(shù)情況下當(dāng)有如下調(diào)用時(shí),finally 子句不會(huì)被執(zhí)行 System.exit(0);考試往往不會(huì)在這個(gè)規(guī)則上考你。考試更可能給你一個(gè)包括return 語(yǔ)句的例子,來(lái)誤導(dǎo)你認(rèn)為代碼不執(zhí)行finally 語(yǔ)句就返回。不要被誤導(dǎo),finally 子句總是會(huì)執(zhí)行。try/catch 子句在它結(jié)構(gòu)正確時(shí)一定會(huì)捕獲錯(cuò)誤。所以你不能試圖在捕獲特殊的IOException 之前寫(xiě)一個(gè)捕獲一般異常的catch,來(lái)捕獲所有的Exception. 下面代碼將不會(huì)通過(guò)編譯 try{DataInputStream dis = new DataInputStream(System.in);dis.read(); }catch (Exception ioe) {} catch (IOException e) {//Compile time error cause} finally{} 這段代碼會(huì)在編譯時(shí)發(fā)出錯(cuò)誤信息,更特殊的IOException 將不會(huì)被達(dá)到。 問(wèn)題 問(wèn)題1) 下列那個(gè)需要建立try/catch 塊或者重新拋出異常? 1) Opening and reading through a file 2) Accessing each element of an array of int values 3) Accessing each element of an array of Objectts 4) Calling a method defined with a throws clause 問(wèn)題2) 編譯運(yùn)行下列代碼會(huì)發(fā)生什么情況? import java.io.*; class Base{public static void amethod()throws FileNotFoundException{} } public class ExcepDemo extends Base{public static void main(String argv[]){ExcepDemo e = new ExcepDemo();}public boolean amethod(int i){try{DataInputStream din = new DataInputStream(System.in);System.out.println("Pausing");din.readChar();System.out.println("Continuing");this.amethod();return true;}catch(IOException ioe) {}finally{System.out.println("Doing finally");}return false;}ExcepDemo(){amethod(99);} } 1) Compile time error amethod does not throw FileNotFoundException 2) Compile, run and output of Pausing and Continuing 3) Compile, run and output of Pausing, Continuing, Doing Finally 4) Compile time error finally clause never reached 答案 答案 1) 打開(kāi)讀一個(gè)文件 4) Calling a method defined with a throws clather Resources on this topic 數(shù)組元素的類(lèi)型對(duì)錯(cuò)誤處理沒(méi)有任何影響。通過(guò)定義throws 子句在方法中的使用,可能會(huì)拋出一個(gè)異常,這個(gè)異常類(lèi)型會(huì)被使用它的代碼捕獲或者再拋出。 答案 2) 3) Compile, run and output of Pausing, Continuing, Doing Finally finally 子句總是會(huì)運(yùn)行。 目標(biāo)五、六 使用斷言編寫(xiě)正確使用斷言的代碼,并且區(qū)分適當(dāng)和不適當(dāng)?shù)臄嘌允褂谩WR(shí)別關(guān)于斷言機(jī)制的正確論述。 目標(biāo)的評(píng)論斷言是隨著2002 年中期JDK1.4 考試版本的發(fā)布而添加到Sun Certified Java Programmers 考試目標(biāo)中的。由于它們是考試的新特性,你一定會(huì)在考試碰到這類(lèi)題目。斷言是其他面向?qū)ο笳Z(yǔ)言的特性,在它被加入到Java 中的時(shí)候一度面臨很大的壓力。斷言是因JDK1.4 的發(fā)布而添加到Java 語(yǔ)言中的,所以在寫(xiě)著此書(shū)的時(shí)候沒(méi)有太多關(guān)于這個(gè)主題的筆墨。 斷言為何存在C++中可以使用斷言,但C 或Visual Basic(或者據(jù)我了解還有Pascal)中沒(méi)有,所以很多人都沒(méi)有使用過(guò)。如果事情到了很多C++程序都沒(méi)有使用過(guò)它們的地步。斷言是一個(gè)相當(dāng)簡(jiǎn)單的概念,你只需要寫(xiě)一個(gè)始終都是true 的語(yǔ)句,但是在形式上它們可以從最終的編譯版本中去除,所以不會(huì)導(dǎo)致運(yùn)行時(shí)開(kāi)銷(xiāo)。使用JDK1.4 之前模擬斷言功能的結(jié)構(gòu)來(lái)書(shū)寫(xiě)代碼是完全可以的,但是以這種方式做起來(lái)會(huì)很困難,它們?cè)谶\(yùn)行時(shí)會(huì)被關(guān)閉。在C/C++語(yǔ)言中,斷言可以使用語(yǔ)言預(yù)處理器來(lái)創(chuàng)建,在新聞組中有大量關(guān)于Java 語(yǔ)言是否應(yīng)該有預(yù)處理系統(tǒng)的討論。觀點(diǎn)的分歧在于有人認(rèn)為預(yù)處理宏是惡魔的東西,它帶來(lái)了創(chuàng)造過(guò)于復(fù)雜結(jié)構(gòu)的機(jī)會(huì),有人認(rèn)為它會(huì)給語(yǔ)言帶來(lái)不可思議的力量。無(wú)論是哪種方式,Java 設(shè)計(jì)者傾向于實(shí)現(xiàn)預(yù)處理機(jī)制,并且在JDK1.4 中包含了斷言。 如何使用斷言何地以及如何使用斷言大概需要一種類(lèi)似于何地及如何使用注釋的判斷方式。一些程序員從來(lái)不使用注釋,這種風(fēng)格的程序被廣泛認(rèn)為是糟糕的程序。因?yàn)榇蠹s80%的代碼是由其他人而不是原作者維護(hù)的,所以注釋是很重要的。斷言可以被認(rèn)為是對(duì)注釋的擴(kuò)展,因?yàn)樗喈?dāng)于告訴人們閱讀一段始終都是true 的代碼的注釋。使用斷言,而不是通過(guò)注釋指定一個(gè)語(yǔ)句始終為true,你可以使用斷言來(lái)聲明它始終是true 的。然后,如果你運(yùn)行含有斷言的代碼,不必像使用注釋那樣依賴于仔細(xì)代碼,但運(yùn)行代碼的時(shí)候?qū)?huì)檢查你的斷言是否為true,如果它們不是斷言錯(cuò)誤就會(huì)被拋棄。正如名字暗示的那樣,斷言被用來(lái)斷定某些東西應(yīng)該始終都是true 的。當(dāng)程序正常運(yùn)行時(shí),斷言就失效了,不會(huì)帶來(lái)性能開(kāi)銷(xiāo)。當(dāng)程序員在查找問(wèn)題時(shí),斷言可以被激活,如果任何斷言語(yǔ)句不為true,斷言異常就會(huì)被拋出。斷言是JDK1.4 中的關(guān)鍵部分,而且不需要在源文件中加入額外的import 語(yǔ)句。但是,因?yàn)檫^(guò)去程序員已經(jīng)使用了單詞assert 來(lái)創(chuàng)建他們自己的斷言,編譯過(guò)程需要一個(gè)命令行的參數(shù)來(lái)告訴編譯器使用JDK1.4 中的斷言。這需要如下的形式 javac –source1.4 Myprog.java 如果你以如下形式正常運(yùn)行程序 java Myprog 斷言失效了,不會(huì)拋出斷言異常。如果你后來(lái)需要查明一個(gè)問(wèn)題,并確定所有的斷言條目都是true,你可以像下面那樣激活斷言來(lái)運(yùn)行程序。 java –enableassertions Myprog 你應(yīng)該斷言什么為true?斷言可以被用在任何你認(rèn)為應(yīng)該始終為true 的地方。例如,一個(gè)人的年紀(jì)大于0 應(yīng)該始終是true 的。如果一個(gè)人的年紀(jì)小于0,你的程序或輸出就會(huì)有很大的問(wèn)題。另一個(gè)例子,如果你正在登記人們死亡的日期,你的程序(或你的道德)可能會(huì)有死亡日期是否可以在未來(lái)的問(wèn)題,所以,你可以對(duì)未來(lái)的死亡日期進(jìn)行斷言。例如,如果你正面臨case 語(yǔ)句或一組if/else 語(yǔ)句,你可能相信代碼始終會(huì)在到達(dá)最后的測(cè)試之前退出。想象一下,如果你有一個(gè)處理媒體類(lèi)型的程序。你的程序希望能夠處理jpg,mpg,avi 或gif 文件。你設(shè)置了一個(gè)根據(jù)文件類(lèi)型分支的case 語(yǔ)句。因?yàn)槟阆嘈盼募?lèi)型將始終是這些類(lèi)型之一,如果你到達(dá)了case 語(yǔ)句的末尾而沒(méi)有分支了,這將是一個(gè)很明顯的問(wèn)題,你可以在缺省選項(xiàng)處放置一個(gè)斷言語(yǔ)句。 你應(yīng)該在哪里使用斷言?斷言不可以用來(lái)強(qiáng)制程序的公共接口。一個(gè)最常見(jiàn)的程序公共接口是它的命令行參數(shù)。因此,傳統(tǒng)上程序員會(huì)通過(guò)查看從命令行傳入的String 數(shù)組args 中的值來(lái)檢查傳遞給Java程序的命令行。典型地,如果數(shù)組沒(méi)有包含程序期望類(lèi)型的值,程序?qū)⑼顺霾⒋蛴〕鲋该髡_命令行格式的消息。斷言機(jī)制的引入不會(huì)改變這些。使用斷言來(lái)檢查程序的命令行參數(shù)是 不合適的,因?yàn)閿嘌杂肋h(yuǎn)都不會(huì)被激活。使用斷言來(lái)檢查傳遞給公共方法的參數(shù)是不合適的。因?yàn)槟愕墓卜椒赡軙?huì)被別人寫(xiě)的程序使用,你無(wú)法確定他們是否激活了斷言,因此正常運(yùn)行程序會(huì)出錯(cuò)。但是,使用斷言來(lái)檢查傳遞給私有方法的參數(shù)是恰當(dāng)?shù)?#xff0c;因?yàn)檫@些方法通常是由能夠訪問(wèn)源代碼的人來(lái)調(diào)用的。同樣的假定可以作用于受保護(hù)或同一個(gè)包中的受保護(hù)方法。如你所見(jiàn),這些僅僅是指導(dǎo)方針,但是考試可能會(huì)詢問(wèn)基于這些指導(dǎo)方針的問(wèn)題。 斷言語(yǔ)法 斷言語(yǔ)句由兩種格式 簡(jiǎn)單的 assert somebooleantest 和 assert somebooleantest : someinformativemethod 在第一個(gè)簡(jiǎn)單版本中,斷言測(cè)試某物是true 的,如果它不是斷言錯(cuò)誤則拋棄。例如, 如果你正在測(cè)試一個(gè)人的年齡是否大于0,你可能創(chuàng)建如下形式的斷言 assert (iAge>); 復(fù)雜的版本可能是如下形式 assert (iAge) : “age must be greater than zero”; 這個(gè)例子很簡(jiǎn)單,因?yàn)橛沂诌叺谋磉_(dá)式只是一個(gè)簡(jiǎn)單的字符串,但是這可以是任何有返回值 的函數(shù)調(diào)用,例如一個(gè)除返回值為void 以外的方法。 課后測(cè)試題 問(wèn)題1)下面哪些論斷是正確? 1) Using assetions requires importing the java.util.assert package 2) Assertions should be used to check the parameters of public methods 3) Assertions can be used as a substitute for the switch/case construct 4) An assertion that a persons date of death > date of birth is appropriate 問(wèn)題 2)如果如下代碼沒(méi)有顯式激活斷言而成功的編譯并運(yùn)行,會(huì)發(fā)生什么呢? class Language {public static final int java = 1;public static final int pascal = 2;public static final int csharp = 3; } public class Mgos {static int lang = 0;public static void main (String argv []) {switch (lang) {case Language.java:System.out.println (“java”);break;case Language.pascal:System.out.println (“pascal”);break;case Language.csharp:System.out.println (“csharp”);break;default:assert false: lang;}} } 1) An unmatched parameter exception will be thrown 2) An assert exception will be thrown 3) The program will run with not output 4) Output of “csharp” 問(wèn)題 3)下面哪些是值得使用斷言結(jié)構(gòu)的候選? 1) An input form has a field for a person’s age. If the person entering the date of birth and the date of death enters an age of death that is before the age of birth the assertion mechanism is used to cause a dialog warning box to be shown and the data will not enter the system. 2) A Text Editing program has a file save mechanism. The assert mechanism is used to check if the drive that is being save to really exists. If the drive does not exist an assertion will be thrown generating a warning to the program operator. 3) A program is being created for food preparation that involves cooking meat. Code is included so that if the value of a temperature variable reading appears to be negative an assert exception is thrown. 4) A school attendance system is being created. In the system is code that will throw an assert exception if a child’s age is calculated to be less than zero. 問(wèn)題 4)如果激活JDK1.4 的斷言,編譯如下代碼會(huì)發(fā)生什么? public class Bbridge {int iRunningTotal = 0;public static void main (String argv []) {Bbridge bb = new Bbrige ();bb.go (argv [0]);}public void go (String s) {int i = Integer.parseInt (s);setRunningTotal (i);assert (iRunningTotal > 0) : getRunningTotal ();}public String getRunningTotal () {return “Value of iRunningTotal “ + iRunningTotal;}public int setRunningTotal (int i) {iRunningTotal += i;return iRunningTotal;} } 1) Compile time error, getRunningTotal does not return a Boolean 2) Compile time error malformed assert statement 3) Compilation and no output given a command parameter of 1 4) Compilation and assert error given a command parameter of 0 問(wèn)題 5)下面的論斷哪些是正確的? 1) The assert system introduces no backward compatibility issues 2) The assert system should be used to enforce command line usage 3) Asserts should be used to check for conditions that should never happen 4) Asserts can be used to enforce argument constraints on private methods. 答案 答案1) 4)An assertion that a persons date of death > date of birth is appropriate 使用斷言不需要引入任何包,但是它確實(shí)要求JDK1.4 或更高版本,并且需要對(duì)JDK 工具使用命令行參數(shù)。斷言不能用來(lái)檢查方法參數(shù)的值,因?yàn)樵谡?#xff08;非測(cè)試)模式中斷言檢查會(huì)被禁用。一個(gè)人死亡的日期比出生日期大的概念始終是正確的,所以使用斷言結(jié)構(gòu)是恰當(dāng)?shù)摹?答案 2) 3)The program will run with no output 運(yùn)行程序時(shí)沒(méi)有從命令行顯式激活斷言將不會(huì)產(chǎn)生斷言錯(cuò)誤。 答案 3) 3)A program is being created for food preparation that involves cooking meat. Code is included so that if the value of a temperature variable reading appears to be negative an assert exception is thrown. 4) A school attendance system is being created. In the system is code that will throw an assert exception if a child’s age is calculated to be less than zero. 選項(xiàng)1 和2,以及2 和4 之間的重要不同點(diǎn)在于,選項(xiàng)1 和2 中斷言機(jī)制在程序正常運(yùn)行過(guò)程中是必需的。斷言對(duì)于正常運(yùn)行程序或標(biāo)準(zhǔn)運(yùn)行時(shí)檢查不是必要的。當(dāng)然,對(duì)于選項(xiàng)3和4 你可能希望包含運(yùn)行時(shí)檢查而不是斷言,但是因?yàn)槊枋鰶](méi)有指明程序正常運(yùn)行的產(chǎn)出依賴于這個(gè)測(cè)試,所以使用斷言是恰當(dāng)。 答案 4) 3)Compilation and no output given a command parameter of 1 4) Compilation and assert error given a command parameter of 0 答案 5) 3)Asserts should be used to check for conditions that should never happen 4) Asserts can be used to enforce argument constraints on private methods. 最少的對(duì)于程序設(shè)計(jì)語(yǔ)言運(yùn)行方式的知識(shí)就能指出新特性都會(huì)引起向后兼容問(wèn)題。如果程序員在JDK1.4 之前使用了單詞assert 作為變量,你將需要在使用JDK1.4 時(shí)傳遞一個(gè)命令行參數(shù)來(lái)指出這一點(diǎn)。使用斷言來(lái)檢查命令行參數(shù)是不合適的,因?yàn)閿嘌詸z查永遠(yuǎn)都不會(huì)被打開(kāi)。 第3 章 垃圾收集 為什么想收集垃圾你可能是一位經(jīng)驗(yàn)非常豐富的Java 程序員,但是你未必想過(guò)弄清楚垃圾收集的來(lái)龍去脈。的確垃圾收集在Java 程序中有點(diǎn)奇怪。本章中垃圾收集是指釋放前面分配的內(nèi)存,這些內(nèi)存不會(huì)再被程序繼續(xù)使用。當(dāng)內(nèi)存已經(jīng)變得沒(méi)用的時(shí)候,我們把它們叫做垃圾,它們的存在還會(huì)使得其他可用內(nèi)存空間變得混亂。Java 語(yǔ)言設(shè)計(jì)的非常出色,其中之一就是你不用擔(dān)心垃圾收集。C/C++程序員必須要手動(dòng)分配和釋放內(nèi)存,這會(huì)導(dǎo)致一個(gè)問(wèn)題出現(xiàn)就是“內(nèi)存泄露”。有些版本的Windows 程序,比如Word 和Excel,可能幾次簡(jiǎn)單的打開(kāi)和關(guān)閉應(yīng)用程序就會(huì)引起某些問(wèn)題出現(xiàn)。有時(shí)候內(nèi)存泄露可能最終導(dǎo)致系統(tǒng)死機(jī),你不得不重新啟動(dòng)電腦。在成千上萬(wàn)的C/C++代碼中,程序員很可能分配一塊內(nèi)存卻忘記釋放它。 Java 和垃圾與C/C++不同,Java 語(yǔ)言會(huì)自動(dòng)釋放不再使用的引用。你不用從成千上萬(wàn)代碼中苦苦查找不會(huì)再使用的內(nèi)存。你也不需要知道如何分配合適大小的空間給不同的數(shù)據(jù)類(lèi)型,以確保程序的兼容性。因此,看起來(lái)你沒(méi)有必要知道垃圾收集的細(xì)節(jié)知識(shí)。有一種情況例外,就是你想通過(guò)考試或者想了解垃圾收集的真實(shí)情況。如果你編寫(xiě)程序過(guò)程中需要?jiǎng)?chuàng)建大量的對(duì)象和變量,這時(shí)候如果知道引用什么時(shí)候會(huì)被釋放是非常重要的。你需要知道自動(dòng)垃圾收集的工作原理,你可以建議或者鼓勵(lì)虛擬機(jī)進(jìn)行垃圾收集,但是記住你不能強(qiáng)迫它作這個(gè)工作。 finalizeJava 語(yǔ)言保證一個(gè)對(duì)象的finalize 方法在對(duì)象被回收之前會(huì)調(diào)用。與其他類(lèi)似垃圾回收的行為不同的是,這里是“保證”。但是finalize 方法到底做什么呢?乍一看,finalization 像是C/C++語(yǔ)言中的析構(gòu)器,在對(duì)象銷(xiāo)毀之前清理其資源。不同的是Java 語(yǔ)言不需要釋放資源,因?yàn)槔厥掌鲿?huì)處理內(nèi)存分配。但是如果你引用了其他外部資源,比如文件信息,那么就有必要在finalization 中釋放資源了,這也是在JDK 1.4 里面提出的參考。當(dāng)垃圾收集器判斷出已經(jīng)沒(méi)有引用指向這個(gè)對(duì)象的時(shí)候,垃圾收集器就會(huì)調(diào)用對(duì)象的finalize 方法。因?yàn)槔占骰厥绽男袨槭遣淮_定的,你不知道什么時(shí)候他們會(huì)執(zhí)行來(lái)收集垃圾。因此你也就沒(méi)有辦法知道什么時(shí)候finalize 方法會(huì)被調(diào)用。但是,你一定想知道考試對(duì)垃圾回收這部分的要求,我們往下看。垃圾收集的確是一個(gè)考點(diǎn)陷阱,因?yàn)槟銢](méi)有明顯的方法來(lái)決定什么時(shí)候垃圾收集可用。因此你不能編寫(xiě)下面的代碼: if(EligibleForGC(Object){ //Not real code System.out.print("Ready for Garbage"); } 正因?yàn)槿绱?#xff0c;你必須掌握下面的原則。一旦一個(gè)對(duì)象不被其他任何對(duì)象引用的時(shí)候,它就變成可回收的對(duì)象了。你可以使用System.gc()來(lái)建議垃圾回收器收集垃圾,但是這并不能保證執(zhí)行。在方法中聲明的本地變量在方法退出的時(shí)候就無(wú)效了,這個(gè)時(shí)候方法中的本地變量就成為了可回收的,方法每次執(zhí)行的時(shí)候本地變量都會(huì)被重新創(chuàng)建。 無(wú)法訪問(wèn)當(dāng)代碼已經(jīng)無(wú)法再訪問(wèn)對(duì)象的時(shí)候,這個(gè)對(duì)象就成為了可垃圾回收的。有兩種情況下會(huì)出現(xiàn)對(duì)象無(wú)法再被訪問(wèn),第一,對(duì)象的引用設(shè)置為null;第二,指向這個(gè)對(duì)象的引用指向了其他的對(duì)象。有這樣一種考試題目,在代碼的某個(gè)部分把引用設(shè)置為null,你必須找出在哪里對(duì)象成為了可垃圾回收的。這種類(lèi)型的題目比較簡(jiǎn)單。但是另外一種情況就不是這么明顯了,我們看看下面的代碼例子。 class Base{String s;Base(String s){this.s = s;}public void setString(String s){this.s = s;} } public class UnReach{public static void main(String argv[]){UnReach ur = new UnReach();ur.go();}public void go(){Base b1 = new Base("One");b1.setString("");Base b2 = new Base("Two");b1 = b2;} } 什么時(shí)候b1 成為可垃圾回收的呢?假設(shè)你不被b1 設(shè)置為空字符串所影響,那么你就可以判斷出當(dāng)b1 指向b2 的時(shí)候,原來(lái)的b1 成為可垃圾回收的了。 課后測(cè)試題 問(wèn)題1)下面哪段代碼可以建議虛擬機(jī)執(zhí)行垃圾收集? 1) System.free(); 2) System.setGarbageCollection(); 3) System.out.gc(); 4) System.gc(); 問(wèn)題2) 在下面的代碼片斷中插入一行代碼確保Integer 對(duì)象被垃圾收集器回收。 public class Rub{Integer i= new Integer(1);Integer j=new Integer(2);Integer k=new Integer(3);public static void main(String argv[]){Rub r = new Rub();r.amethod();}public void amethod(){System.out.println(i);System.out.println(j);System.out.println(k);} } 1) System.gc(); 2) System.free(); 3) Set the value of each int to null 4) None of the above 問(wèn)題3)下面那句話是正確的? 1)You cannot be certain at what point Garbage collection will occur 2) Once an object is unreachable it will be garbage collected 3) Both references and primitives are subject to garbage collection. 3) Garbage collection ensures programs will never run out of memory 問(wèn)題4)在哪里第8 行創(chuàng)建的sb 對(duì)象成為可垃圾回收的? public class RJMould{StringBuffer sb;public static void main(String argv[]){RJMould rjm = new RJMould();rjm.kansas();}public void kansas(){sb = new StringBuffer("Manchester");StringBuffer sb2 = sb;StringBuffer sb3 = new StringBuffer("Chester");sb=sb3;sb3=null;sb2=null;} } 1) Line 11 2) Line 9 3) Line 12 4) Line 13 答案 答案1) 4) System.gc(); 答案2) 4) None of the above 你只能建議垃圾回收器運(yùn)行,但是無(wú)法決定他會(huì)在代碼的哪個(gè)部分執(zhí)行。注意只有對(duì)象的實(shí)例才可能成為垃圾回收對(duì)象,原始數(shù)據(jù)類(lèi)型不會(huì)。 答案3) 1) You cannot be certain at what point Garbage collection will occur 一旦一個(gè)對(duì)象不能在被訪問(wèn),那么他將成為可垃圾回收的。但是你不能確定它什么時(shí)候會(huì)被回收。垃圾回收機(jī)制只對(duì)對(duì)象有效,對(duì)原始類(lèi)型無(wú)效。你應(yīng)該知道垃圾收集不能確保程序不會(huì)出現(xiàn)內(nèi)存不足的情況。但是他能保證不再被使用的內(nèi)存可以成為可用的。 答案4) 4) Line 13 第9 行創(chuàng)建的sb2 指向了第8 行創(chuàng)建的對(duì)象,直到它成為不可到達(dá)的對(duì)象的時(shí)候,sb 才成為可垃圾回收的。 答案5) 1) finalize will always run before an object is garbage collected 對(duì)象在垃圾回收之前,它的finalize 方法會(huì)被調(diào)用。Finalize 方法不能在對(duì)象被回收后調(diào)用,因?yàn)槟菚r(shí)候?qū)ο笠呀?jīng)不存在了。當(dāng)一個(gè)對(duì)象不能訪問(wèn)的時(shí)候,他就成為了可垃圾回收的,但是你無(wú)法保證它什么時(shí)候會(huì)被回收。選項(xiàng)4 在java 中是不正確的,在C++中正確。 第4 章 語(yǔ)言基礎(chǔ) 目標(biāo)一 包,引入,內(nèi)部類(lèi),接口正確識(shí)別結(jié)構(gòu)化的包聲明,引入子句,類(lèi)聲明(包含內(nèi)部類(lèi)在內(nèi)的所有形式),接口聲明,方法聲明(包括類(lèi)運(yùn)行入口的main 方法),變量聲明和標(biāo)識(shí)符。 目標(biāo)的注解這是一個(gè)奇怪的使用短語(yǔ)表達(dá)的目標(biāo)。它似乎在要求你理解何時(shí),如何以及為何使用引入子句和包子句,以及應(yīng)該將接口子句和變量子句放在什么地方。 包名稱package意味著類(lèi)的集合,有點(diǎn)類(lèi)似于類(lèi)庫(kù)。使用包也有點(diǎn)像使用目錄。如果你在一個(gè)文件中放置一個(gè)包子句,此文件只對(duì)同一個(gè)包中的其他類(lèi)可見(jiàn)。包有助于解決命名沖突問(wèn)題。因?yàn)槟阒荒苁褂眠@么多有意義的名字作為類(lèi)名,最終,你可能需要使用或創(chuàng)建相同名稱的類(lèi)。通過(guò)在類(lèi)之前附加一個(gè)完整的包名,你可以多次使用相同的名字。包名的使用慣例是用組織的internet域名來(lái)創(chuàng)建類(lèi)。因此,當(dāng)創(chuàng)建一個(gè)叫做Question的類(lèi)來(lái)表示一個(gè)虛擬的測(cè)試題時(shí),我使用我的網(wǎng)站域名www.jchq.net來(lái)創(chuàng)建目錄結(jié)構(gòu)。WWW 部分無(wú)法唯一標(biāo)識(shí)網(wǎng)站的任何信息,所以使用的域名將是net.jchq。為了在我唯一的包中創(chuàng)建類(lèi),我創(chuàng)建了目錄net,并在此之下創(chuàng)建一個(gè)叫做jchq 的目錄。接著,在那個(gè)目錄中我可以創(chuàng)建叫做Question 的類(lèi),類(lèi)的開(kāi)頭為如下包定義: package net.jchq.*; 這將賦予你訪問(wèn)此包/目錄中任何類(lèi)的權(quán)利。可選地,你可以僅僅指定一個(gè)需要獲取訪問(wèn)權(quán)限的類(lèi),使用如下行: package net.jchq.Question; 引入import 子句必須出現(xiàn)在任何package 子句之后和任何代碼之前。引入子句不能出現(xiàn)在類(lèi)中,類(lèi)聲明之后或其他任何地方。import 子句允許你直接使用類(lèi)名,而不必使用完整的包名來(lái)限定它。一個(gè)例子就是類(lèi)名java.awt.Button 通常被簡(jiǎn)寫(xiě)為Button,只要你已經(jīng)將如下子句放在文件的起始位置: import java.awt.*; 如果我隨后想要?jiǎng)?chuàng)建我的Question 類(lèi)的實(shí)例,我只需要引入包或指定此類(lèi)的完整包名。為了引入其他包中的類(lèi),我將需要如下行: import net.jchq.*; 為了指定類(lèi)的完整包名,我需要使用如下風(fēng)格的語(yǔ)法。 jchq.net.Question question = new net.jchq.Question(); 你可以想象,經(jīng)常性地輸入完全限定的包名顯得不夠靈活,所以引入類(lèi)通常是首選的方案。請(qǐng)注意,使用引入子句對(duì)性能沒(méi)有影響。這類(lèi)似于在DOS(或Unix)環(huán)境中設(shè)定一個(gè)路徑聲明。這只是簡(jiǎn)單的為類(lèi)設(shè)定有效性或路徑,并不是直接將代碼引入程序中。僅僅在程序中實(shí)際地使用類(lèi)才會(huì)影響性能。你可以在包自己之前放置一段注釋,但不能是其他任何內(nèi)容。你可能會(huì)遇到將引入子句放于包子句前面的考試題。 //你可以在包子句之前放置一段注釋 package MyPack; public class MyPack {} 下面的代碼會(huì)導(dǎo)致錯(cuò)誤 import java.awt.*; //錯(cuò)誤:將引入子句放于包子句前面 //語(yǔ)句會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤 package MyPack; public class MyPack {} package 子句可能會(huì)包含點(diǎn)號(hào)來(lái)指定包層次。因此如下代碼將不會(huì)導(dǎo)致編譯錯(cuò)誤 package myprogs.MyPack; public class MyPack {}記住,如果你沒(méi)有在源文件中放置包子句,這將被認(rèn)為有一個(gè)相當(dāng)于當(dāng)前目錄的缺省包。這與在“1.2 節(jié) 定義和訪問(wèn)控制”中提到的可見(jiàn)性有關(guān)。 類(lèi)和內(nèi)部類(lèi)聲明一個(gè)文件只能包含一個(gè)外部public 類(lèi)。如果你試圖創(chuàng)建一個(gè)包含多個(gè)public 類(lèi)的文件,編譯器將會(huì)報(bào)告特定的錯(cuò)誤。一個(gè)文件可以包含多個(gè)非公共類(lèi),但是記住這將為每個(gè)類(lèi)生成單獨(dú)的.class 輸出文件。公共類(lèi)在文件中的放置位置是沒(méi)有關(guān)系的,只要在文件中僅有一個(gè)公共類(lèi)。內(nèi)部類(lèi)是在JDK1.1 中提出的。這個(gè)想法是為了允許一個(gè)類(lèi)在另一個(gè)類(lèi)中定義,在一個(gè)方法中定義,以及創(chuàng)建匿名內(nèi)部類(lèi)。這會(huì)帶來(lái)一些有趣的影響,特別是對(duì)于可見(jiàn)性。 這是一個(gè)簡(jiǎn)單的內(nèi)部類(lèi)的例子: class Outer {class inner{} } 這會(huì)導(dǎo)致生成如下名稱的類(lèi)文件 Outer.class Outer$Inner.class 內(nèi)部類(lèi)的定義僅僅在現(xiàn)有的Outer 類(lèi)的上下文中可見(jiàn)。因此,如下代碼會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤 class Outer {class Inner{} } class Another {public void amethod () {Inner I = new Inner();} }涉及到類(lèi)Another 的時(shí)候,類(lèi)Inner 是不存在的。它只能存在于Outer 類(lèi)實(shí)例的上下文中。因此如下代碼運(yùn)行良好,因?yàn)樵趧?chuàng)建Inner 實(shí)例的時(shí)候,有一個(gè)指向外部類(lèi)的this 實(shí)例。 class Outer {public void mymethod () {Inner I = new Inner();}public class Inner {} }但是,如果Outer 類(lèi)的this 實(shí)例不存在時(shí)會(huì)發(fā)生什么呢。為了弄清楚為此提供的相當(dāng)古怪的語(yǔ)法的含義,試著將如上例子中的new 關(guān)鍵字看作屬于this 實(shí)例當(dāng)前的上下文。這樣,你可以改變創(chuàng)建實(shí)例的代碼行,如下 Inner i = this.new Inner ();這樣,如果你需要從一個(gè)static 方法或其他沒(méi)有this 對(duì)象的地方創(chuàng)建Inner 的實(shí)例,你可以把new 當(dāng)作屬于外部類(lèi)的一個(gè)方法來(lái)使用 class Outer {public class Inner {} } class another {public void amethod () {Outer.Inner i = new Outer ().new Inner ();} }盡管有了我口齒伶俐的解釋,我發(fā)現(xiàn)這個(gè)語(yǔ)法不夠直觀,并在學(xué)完5 分鐘后就忘記了。你很有可能會(huì)在考試中遇到這個(gè)問(wèn)題,所以請(qǐng)給予額外的注意。內(nèi)部類(lèi)的一個(gè)好處是內(nèi)部類(lèi)一般可以訪問(wèn)它的嵌套類(lèi)(或外部類(lèi))的域。不像外部類(lèi),內(nèi)部類(lèi)可以是private 或static。主考者似乎有可能問(wèn)一些歸結(jié)為“一個(gè)內(nèi)部類(lèi)可以是static或private”的問(wèn)題。靜態(tài)內(nèi)部類(lèi)的方法當(dāng)然可以訪問(wèn)其嵌套類(lèi)的任何靜態(tài)域,因?yàn)槟切┯驅(qū)⒅粫?huì)有一個(gè)實(shí)例。 聲明在方法中的內(nèi)部類(lèi)內(nèi)部類(lèi)可以在方法中創(chuàng)建。這是像Borland JBuilder 那樣的GUI 生成工具在創(chuàng)建事件處理器時(shí)花很多功夫做的事情。 這是一個(gè)這種自動(dòng)生成的代碼的例子 buttonControl1.addMouseListener (new java.awt.event.MouseAdapter () {ublic void mouseClicked (MouseEvent e) {ttonControl1_mouseClicked (e);}); 請(qǐng)注意第一個(gè)圓括號(hào)之后的new 關(guān)鍵字。它指出在方法addMouseListener 中一個(gè)匿名內(nèi)部類(lèi)正在被定義。通常地,這個(gè)類(lèi)可以使用一個(gè)名字定義,這可能會(huì)使它更容易被人們讀懂,但是由于在其他地方不需要對(duì)其進(jìn)行處理,取名字不會(huì)有太多幫助。如果你手動(dòng)創(chuàng)建這些代碼,很容易會(huì)被數(shù)字以及花括號(hào)和圓括號(hào)的層次弄糊涂。請(qǐng)注意完整的結(jié)構(gòu)為何是以分號(hào)結(jié)束的,因?yàn)檫@實(shí)際上是一個(gè)方法調(diào)用的結(jié)束。如你猜的那樣,一個(gè)匿名內(nèi)部類(lèi)不能由程序員給定構(gòu)造函數(shù)。考慮一下,構(gòu)造函數(shù)是一個(gè)沒(méi)有返回值,并且名字與類(lèi)名相同的方法。咄!我們?cè)谡務(wù)摏](méi)有名字的類(lèi)。一個(gè)匿名類(lèi)可以繼承其他類(lèi)或?qū)崿F(xiàn)單一接口。這個(gè)特別的限制似乎不會(huì)在考試中考到。 在方法中定義的類(lèi)的域可見(jiàn)性義在方法中的類(lèi)只能訪問(wèn)嵌套方法中的域,如果他們是被定義為final 的。這是因?yàn)槎x在方法中的變量通常被認(rèn)為是自治的(automatic),例如他們僅當(dāng)方法執(zhí)行時(shí)才存在。在創(chuàng)建在方法中的類(lèi)中定義的域可能比嵌套方法的生命周期長(zhǎng)。因?yàn)閒inal 變量不能被修改,JVM 可以確保它的值保持恒定,甚至在外部方法運(yùn)行終止之后。你很可能在考試中遇到這方面的問(wèn)題,包括考察作為參數(shù)傳遞給方法的變量狀態(tài)的問(wèn)題(是的,他們也必須是final 的)。 創(chuàng)建接口口是Java 用來(lái)解決缺少多繼承的方式。有趣的是,Visual Basic 使用關(guān)鍵字interface并以與Java 相似的方式來(lái)使用此概念。有時(shí)候接口方法被認(rèn)為面向契約編程。通過(guò)關(guān)鍵字“implements”來(lái)使用接口。因此,類(lèi)可以被聲明為 class Malvern implements Hill, Well {ublic } 主方法為java 中的所有代碼必須存在于類(lèi)中,必須有一個(gè)特別的或“有魔力的”的方法來(lái)引導(dǎo)程序開(kāi)始運(yùn)行。這個(gè)方法具有如下署名 public static void main (String argv[]) 聲明中的每一項(xiàng)來(lái)分析,關(guān)鍵字public 意味著方法到處可見(jiàn)。static 部分意味著方法屬于類(lèi)本身,而不是屬于任何特定的實(shí)例。這意味著不需要?jiǎng)?chuàng)建類(lèi)的實(shí)例就可以調(diào)用它。單詞void 意味著方法沒(méi)有返回值。注意單詞main 都是小寫(xiě)的。在圓括號(hào)中的部分指出方法接受一個(gè)String 數(shù)組的參數(shù)。當(dāng)然,單詞String 必須以大寫(xiě)S 開(kāi)頭。參數(shù)arg 的名字沒(méi)有關(guān)系,你可以叫它bicycle 或trousers 或任何正確的變量名稱,它都可以正確運(yùn)行。但是參數(shù)命名為arg 是一個(gè)值得堅(jiān)持的慣例。因?yàn)閿?shù)組的方括號(hào)可以跟在名字或類(lèi)型之后,將參數(shù)聲明為String [] arg 也是可以接受的。注意,為了Sun Certified Java Programmers,這是正確的署名。你可能發(fā)現(xiàn)其他類(lèi)似的署名在現(xiàn)實(shí)中也可以運(yùn)行,但是為了考試(以及未來(lái)兼容性的目的)你應(yīng)該使用這種署名。因?yàn)檫@個(gè)方法是靜態(tài)的,所以它被調(diào)用(或被Java 環(huán)境有效跟蹤)的時(shí)候不需要?jiǎng)?chuàng)建類(lèi)的實(shí)例。同樣,因?yàn)樗庆o態(tài)的,你不可以操作非靜態(tài)的方法或數(shù)據(jù)。因?yàn)檫@樣,main 方法經(jīng)常包含極少的代碼,典型地,它包含代碼來(lái)創(chuàng)建嵌套類(lèi)的實(shí)例,然后調(diào)用真正使程序完成工作的非靜態(tài)方法。系統(tǒng)傳遞給main 方法的String 數(shù)組包含任何在程序開(kāi)始時(shí)從命令行傳入的參數(shù)。當(dāng)然,有了現(xiàn)代圖形用戶界面環(huán)境,更普通的方法是通過(guò)點(diǎn)擊圖標(biāo)來(lái)啟動(dòng)程序,這些都不會(huì)給傳遞參數(shù)帶來(lái)變化。試的目標(biāo)4.2 明確地要求你理解命令行參數(shù)是如何傳遞給main 方法的,以及如何訪問(wèn)它們。這就是說(shuō)……述傳遞給main 方法的參數(shù)數(shù)組的下標(biāo)值與命令行參數(shù)的對(duì)應(yīng)關(guān)系。 課后測(cè)試題 問(wèn)題1)假設(shè)有如下代碼 public class FinAc {static int l = 4;private int k = 2;public static void main (String argv [] ) {FinAc a = new FinAc();a.amethod();}public void amethod () {final int i = 99;int j = 6;class CInMet {public void mymethod (int q) {// Here}// end of mymethod}// End of CInMetCInMet c = new CInMet ();c.mymethod (i);}// End of amethod } 如下變量中,哪些在由注釋//Here 標(biāo)記的行上是可見(jiàn)的? 1) l 2) k 3) i 4) j 問(wèn)題 2)下面哪個(gè)選項(xiàng)可以正確編譯? 1) // A Comment import java.awt.*; class Base {} 2) import java.awt.*; package Spot; class Base (); 3) // Another comment package myprogs.MyPack; public class MyPack {} 4) class Base {} import java.awt.*; public class Tiny {} 問(wèn)題 3)如下論述哪些是正確的? 1) An inner class may be defined as static 2) An inner class may NOT be define as private 3) An anonymous class may have only one constructor 4) An inner class may extend another class 問(wèn)題 4)從不存在當(dāng)前this 引用的代碼中如何創(chuàng)建內(nèi)部類(lèi)的實(shí)例? 1) Outer.Inner i = new Outer ().new Inner (); 2) Without a this reference an inner class cannot be created 3) Outer.Inner i = Outer ().new new Inner (); 4) Outer i = Outer.new ().Inner (); 問(wèn)題 5)如下哪些是開(kāi)始執(zhí)行Java 程序的main 方法的正確形式? 1) public static void main (String[] bicycle); 2) public void main (String argv[]); 3) public static int main (String args[]) 4) public static void main (String args[]); 問(wèn)題 6)試圖編譯如下代碼時(shí)會(huì)發(fā)生什么? abstract class Base {abstract public void getValue (Base b); } public class Robinwood extends Base {public static void main (String argv[]) {Robinwood rw = new Robinwood();rw.main();}public void main () {getValue (this);}public void getValue (Base b) {} } 1) Compile error, only methods can be marked as abstract 2) Compile error, the name “main” is reserved for the startup method 3) Compile error, the parameter to the getValue call is of the wrong type 4) Compilation without error 問(wèn)題 7) // located in the East end package spital; abstract class Spital {public Spital (int i) {} } public class Mudchute extends Spital {public static void main (String argv[]) {Mudchute ms = new Mudchute ();ms.go ();}public Mudchute () {super (10);}public void go () {island();}public void island () {System.out.println (“island”);} } 1) Compile time error, any package declaration must appear before anything else 2) Output of 10 followed by island 3) Output of 10 followed by “spital island” 4) Compile time error 問(wèn)題 8)對(duì)于定義在方法中的類(lèi),什么規(guī)則管理對(duì)于嵌套類(lèi)中變量的訪問(wèn)? 1) The class can access any variable 2) The class can only access static variables 3) The class can only access transient variables 4) The class can only access final variables 答案 答案1) 1) l 2) k 3) i 定義在方法中的類(lèi)只能看到來(lái)自嵌套方法中的final 域。但是它可以看到嵌套類(lèi)中包括私有域在內(nèi)的域。域j 沒(méi)有被定義為final。 答案 2) 1) //A Comment import java.awt.*; class Base {}; 3) //Another comment package myprogs.MyPack; public class MyPack {} 任何包子句必須是文件中的第一個(gè)條目(除去注釋)。引入子句必須在任何包子句之后和代碼之前。 答案 3) 1) An inner class may be defined as static 2) An inner class may extend another class 一個(gè)匿名類(lèi)怎么能有構(gòu)造函數(shù)呢?內(nèi)部類(lèi)可以被定義為私有的。 答案 4) 1) Outer.Inner i = new Outer ().new Inner (); 答案 5) 1) public static void main (String[] bicycle); 2) public static void main (String args[]); 選項(xiàng)2 可以編譯,但是不能成為程序的啟動(dòng)方法,因?yàn)樗鼪](méi)有聲明為static。選項(xiàng)3 不能編譯,因?yàn)樗宦暶鳛榉祷豬nt 值。 答案 6) 4)Compilation without error 為一個(gè)非啟動(dòng)方法取名為“main”在語(yǔ)法上是正確的,但這是很糟糕的風(fēng)格。因?yàn)轭?lèi)Robinwood 繼承自類(lèi)Base,所以可以將其作為參數(shù)傳遞給一個(gè)期望得到Base 類(lèi)型參數(shù)的方法。 答案 7) 2)Output of 10 followed by island 包聲明必須出現(xiàn)在除注釋外的任何內(nèi)容之前,注釋可以出現(xiàn)在任何地方。 答案 8) 4)The class can only access final variables 注意這一限制適用于嵌套方法中的變量,而不是嵌套類(lèi)中的變量。 目標(biāo)二 使用接口識(shí)別正確實(shí)現(xiàn)了接口的類(lèi),這些接口既可以是java.lang.Runnable,也可以是試題中完整指定的接口。 接口——面向契約編程接口是總所周知的“面向契約編程”的一部分。這意味著一個(gè)程序員創(chuàng)造了一些東西來(lái)迫使其他程序員遵循一組條件。接口同樣也被認(rèn)為是Java 用來(lái)無(wú)缺點(diǎn)獲取一些多繼承好處的方式。C++語(yǔ)言具有多繼承,這意味這一個(gè)類(lèi)可以有多個(gè)父類(lèi)。多繼承與單繼承的優(yōu)缺點(diǎn)是編程理論家之間廣泛爭(zhēng)論的話題。 Runnable 接口Runnable 接口是線程機(jī)制的一部分,線程機(jī)制將在其他的考試目標(biāo)中進(jìn)行明確地陳述。Runnable 接口指定實(shí)現(xiàn)了它的類(lèi)必須定義一個(gè)具有如下署名的方法 public void run () 使用接口的關(guān)鍵字是implements,因此,如果你打算創(chuàng)建一個(gè)實(shí)現(xiàn)Runnable 接口的類(lèi),代碼類(lèi)似于 public class MyClass implements Runnable {public void run () {} }當(dāng)然,為了做一些有用的事,你需要在run 方法體中添加一些代碼,但是僅僅創(chuàng)建一個(gè)具有合適署名的run 方法已經(jīng)足夠完成Runnable 接口要求的契約了。除非你具有一個(gè)完全正確署名的方法,否則你會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤。 目標(biāo)三 從命令行傳遞值陳述傳遞給main 方法的參數(shù)數(shù)組的下標(biāo)值與命令行參數(shù)的對(duì)應(yīng)關(guān)系。注意:這似乎是一個(gè)微小的主題,幾乎不值得使之成為一個(gè)目標(biāo)。這個(gè)主題可以找出更具經(jīng)驗(yàn)的C/C++程序員,因?yàn)閍rgv []的第一個(gè)元素是命令行中程序名稱后面的第一個(gè)字符串。因此,如果程序運(yùn)行如下。 java myprog myparm 元素argv [0]將包含“myparm”。如果你具有C/C++背景,你可能認(rèn)為它包含“java”。Java 不包含與Visual Basic 中的Option Base 等價(jià)的元素(譯者注:VB 中可以使用Option Base來(lái)限定下標(biāo)的缺省下界),并且所有的數(shù)組都是從元素0 開(kāi)始。 以如下程序?yàn)槔?public class MyParm {public static vooid main (String argv []) {String s1 = argv [1];System.out.println (s1);} } 為了強(qiáng)調(diào)argv 是一個(gè)String 數(shù)組,我將參數(shù)1 傳給一個(gè)String。如果你使用如下命令運(yùn)行程序 java MyParm hello there 輸出結(jié)果將是there,而不是MyParm 或hello。 課后測(cè)試題 問(wèn)題 1)假設(shè)類(lèi)Cycle 中有如下主方法,并且有命令行 java Cycle one two 輸出是什么? public static void main (String bicycle []) {System.out.println (bicycle [0]); } 1) None of these options 2) Cycle 3) one 4) two 問(wèn)題 2)如何從命令行中獲取傳遞給主方法的值? 1) Use the System.getParms () method 2) Assign an element of the argument to a string 3) Assign an element of the argument to a char array 4) None of these options 答案 答案 1) 3) one 答案2) 2) Assign an element of the argument to a string 目標(biāo)四 識(shí)別關(guān)鍵字識(shí)別所有Java 程序設(shè)計(jì)語(yǔ)言的關(guān)鍵字。注意:不會(huì)出現(xiàn)關(guān)于關(guān)鍵字與描述常量之間的 深層次區(qū)別的問(wèn)題。目標(biāo)的注解:你可能希望在學(xué)習(xí)較少使用的關(guān)鍵字的基礎(chǔ)上解決這個(gè)目標(biāo),確保你沒(méi)有延續(xù)來(lái)自其他你所知道的語(yǔ)言中的“壞朋友”,特別是C/C++。考試特別強(qiáng)調(diào)識(shí)別關(guān)鍵字。此目標(biāo)的第二部分提到的深層次區(qū)別是在JDK1.4 版本的考試中加入的目標(biāo)。似乎越來(lái)越多的人擔(dān)心true/false 和null 是不是關(guān)鍵字。我想你可以從評(píng)論中斷定你不會(huì)被詢問(wèn)關(guān)于true/false 和null 的問(wèn)題。 Java 關(guān)鍵字 abstract boolean break byte case catch char class const * continue default do double else extends final finally float for goto * if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while 通過(guò)使用語(yǔ)言,你將會(huì)逐漸認(rèn)識(shí)大部分的Java 關(guān)鍵字,但是,在考試中可能會(huì)出現(xiàn)極少使用的例外和保留詞。一些極少使用的詞(當(dāng)然是對(duì)于初學(xué)者而言)的例子 volatile transient native strictfp 帶星號(hào)的詞是保留的,現(xiàn)在并沒(méi)有被使用。注意所有的關(guān)鍵字都是小寫(xiě)的,所以for 是關(guān)鍵字而FOR不是。 課后測(cè)試題 問(wèn)題 1)下面哪些是Java 關(guān)鍵字? 1) double 2) Switch 3) then 4) instanceof 問(wèn)題 2)下面哪些不是Java 關(guān)鍵字? 1) volatile 2) sizeOf 3) goto 4) try 答案 答案 1) 2) double 4)instanceof 注意,switch 中的大寫(xiě)字母S 意味著它不是關(guān)鍵字,單詞then 是Visual Basic 中的一部分而不是Java。 答案 2) 2) sizeOf 這是C/C++中用來(lái)決定一個(gè)原始數(shù)據(jù)在某個(gè)特定平臺(tái)上的大小的關(guān)鍵字。因?yàn)镴ava 中原始數(shù)據(jù)在所有平臺(tái)上都具有相同大小,所以這個(gè)關(guān)鍵字沒(méi)有被使用。 目標(biāo)五 未賦值的變量陳述當(dāng)沒(méi)有顯式賦值時(shí),使用任何類(lèi)型的變量或數(shù)組元素的影響。 變量你可以學(xué)習(xí)使用Java 編程而不必真正理解這個(gè)目標(biāo)背后的議程,但是它確實(shí)代表著有價(jià)值的實(shí)際知識(shí)。本質(zhì)上,類(lèi)級(jí)別的變量總是會(huì)被賦予一個(gè)缺省值,而一個(gè)成員變量(包含在方法中)將不會(huì)被賦予任何缺省值。如果你試圖訪問(wèn)一個(gè)未賦值的變量會(huì)發(fā)生錯(cuò)誤。例如 class MyClass {public static void main (String argv[]) {int p;int j = 10;j = p;} } 這段代碼將會(huì)導(dǎo)致如下錯(cuò)誤: “error variable p might not have been assigned” 從C/C++給你足夠的自由給p 留下一個(gè)任意值的傾向來(lái)看,這被認(rèn)為是一個(gè)很受歡迎的改變。如果p 定義在類(lèi)級(jí)別,它就會(huì)被賦予其缺省值,而不會(huì)有錯(cuò)誤產(chǎn)生。 class MyClass {static int p;public static void main (String argv []) {int j = 10;j = p; System.out.println (j);} } 整型數(shù)的缺省值是0,所以這將會(huì)打印出0。 數(shù)字類(lèi)型的缺省值是0,布爾型為false,對(duì)象引用唯一的缺省值類(lèi)型是null。 數(shù)組學(xué)習(xí)這部分目標(biāo)需要理解一個(gè)簡(jiǎn)單的規(guī)則。任何基本類(lèi)型的數(shù)組元素的值將總是被初始化為缺省值,無(wú)論數(shù)組是否被定義。無(wú)論數(shù)組定義為類(lèi)級(jí)別還是方法級(jí)別,元素值都會(huì)被設(shè)定為缺省值。你可能會(huì)遇到詢問(wèn)一個(gè)數(shù)組的某個(gè)特定元素包含什么值的問(wèn)題。除非是對(duì)象數(shù)組,否則答案都不會(huì)是null(或者如果它們被特別的設(shè)定為NULL)。 課后測(cè)試題 問(wèn)題 1)假設(shè)有如下代碼,元素b [5]包含什么? public class MyVal {public static void main (String argv []) {MyVal m = new MyVal ();m.amethod ();}public void amethod () {boolean b [] = new Boolean [5];} } 1)1 2)null 3)"" 4)none of these options 問(wèn)題 2) 假設(shè)有如下構(gòu)造函數(shù),mycon 的元素1 包含什么? MyCon () {int [] mycon = new int [5]; } 1)0 2)null 3)"" 4)None of these options 問(wèn)題 3)試圖編譯和運(yùn)行如下代碼時(shí)會(huì)發(fā)生什么? public class MyField {int i = 99;public static void main (String argv []) {MyField m = new MyField ();m.amethod ();}void amethod () {int i;System.out.println (i);} } 1) The value 99 will be output 2) The value 0 will be output 3) Compile time error 4) Run time error 問(wèn)題 4)試圖編譯和運(yùn)行如下代碼時(shí)會(huì)發(fā)生什么? public class MyField {String s;public static void main (String argv []) {MyField m = new MyFeild ();m.amethod ();}void amethod () {System.out.println (s);} } 1) Compile time error s has not been initialized 2) Runtime error s has not been initialized 3) Blank output 4) Output of null 答案 答案 1) 4)none of these options 數(shù)組元素從0 開(kāi)始編號(hào),因此數(shù)組沒(méi)有元素5。如果你試圖運(yùn)行 System.out.println (b [5]) 你會(huì)得到一個(gè)異常。 答案 2) 1)0 這種情況下,構(gòu)造函數(shù)產(chǎn)生的效果與其他方法沒(méi)有什么不同。無(wú)論在哪里創(chuàng)建,一個(gè)整型數(shù)組的所有元素都將被初始化為0。 答案 3) 3) Compile time error 你會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤,指出變量i 沒(méi)有被初始化。類(lèi)級(jí)別的變量i 會(huì)轉(zhuǎn)移你的注意力,因?yàn)樗鼤?huì)被方法級(jí)別版本所覆蓋。方法級(jí)別的變量不會(huì)被初始化為缺省值。 答案 4) 4) Output of null 創(chuàng)建在類(lèi)級(jí)別的變量總是會(huì)被賦予一個(gè)缺省值。對(duì)象引用的缺省值是null,并且使用System.out.println 隱式調(diào)用toString 方法時(shí)會(huì)打印出null。 目標(biāo)六 數(shù)據(jù)類(lèi)型的范圍和格式陳述所有原始數(shù)據(jù)類(lèi)型的范圍,聲明String 文字以及使用所有允許的格式,基數(shù)和表示法來(lái)聲明原始數(shù)據(jù)類(lèi)型。 目標(biāo)的注解這是一個(gè)有點(diǎn)令人煩惱但又很容易遇到的目標(biāo)。你可以書(shū)寫(xiě)大量Java 代碼而不需要了解原始數(shù)據(jù)類(lèi)型的范圍,但是要記住這些細(xì)節(jié)也不會(huì)花很多時(shí)間。對(duì)能使用所有格式的要求要小心,不要忽略了八進(jìn)制格式。 整數(shù)原始數(shù)據(jù)類(lèi)型的大小當(dāng)這個(gè)目標(biāo)要求原始數(shù)據(jù)類(lèi)型的范圍時(shí),我假定它只要求以2 的冪次表示,而不是確實(shí)表示的數(shù)值。由于byte 的大小很直觀,基于我對(duì)PC 的基本經(jīng)驗(yàn),是8 比特,在我的記憶中只有三種整型類(lèi)型需要學(xué)習(xí), 整型數(shù)據(jù)類(lèi)型的范圍 Name Size Range byte 8 bit -27 to 27-1 short 16 bit -215 to 215-1 int 32 bit -231 to 231-1 long 64 bit -263 to 263-1 聲明整型數(shù)有三種方式聲明整型數(shù)。缺省的,如你所期望的是十進(jìn)制。這里是一些選項(xiàng) 聲明18 為整型數(shù) Decimal 18 Octal 022 (Zero not letter O) Hexadecimal 0x12 如果你編譯并運(yùn)行這個(gè)小類(lèi),你每次都會(huì)得到18 的輸出。 public class Lit {public static void main (String [] argv) {int i = 18;int j = 022; //Octal version: Two eights plus twoint k = 0x12; //Hex version: One sixteen plus twoSystem.out.println (i);System.out.println (j);System.out.println (k);} }Roberts 和Heller 描述了6 種聲明整型數(shù)的方法,因?yàn)閷?duì)于Java 來(lái)說(shuō)是很少見(jiàn),字母X不是大小寫(xiě)敏感的,16 進(jìn)制符號(hào)中的字母A 到F 也是這樣。我覺(jué)得僅僅記住有三種方式以及字母是非大小寫(xiě)敏感的要更容易一點(diǎn)。 浮點(diǎn)原始數(shù)據(jù)類(lèi)型的大小浮點(diǎn)數(shù)是有點(diǎn)奇怪的“野獸”,因?yàn)樵谟?jì)算的時(shí)候會(huì)出現(xiàn)意想不到的結(jié)果。引用Peter VanDer Linden 的話“The exact accuracy depends on the number being represented”。為補(bǔ)償變量的精確度,你確實(shí)會(huì)接觸到巨大得超出想象的數(shù)據(jù)。因此,最大的double 可以存儲(chǔ)達(dá)到17 后面跟307 個(gè)0 的數(shù)。所以你甚至可以存儲(chǔ)經(jīng)濟(jì)報(bào)刊上說(shuō)的Bill Gates 所擁有的價(jià)值那么大的數(shù)值(直到Linux 得到了全世界的控制權(quán),那么整數(shù)就能很好的完成任務(wù)了)。 浮點(diǎn)類(lèi)型的范圍 float 32 bit double 64 bit記住,一個(gè)具有小數(shù)部分的數(shù)據(jù)的缺省類(lèi)型是double 而不是float。這會(huì)有點(diǎn)讓人困惑,因?yàn)槟憧赡軙?huì)認(rèn)為“浮點(diǎn)數(shù)”的缺省類(lèi)型是float。你可能會(huì)在考試中遇到類(lèi)似于如下形式的題目。 如下能通過(guò)編譯嗎? float i = 1.0; 直覺(jué)會(huì)告訴你這是可以編譯成功的。不幸地是考試不是為考察你的直覺(jué)而設(shè)計(jì)的。這會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤,因?yàn)樗噲D將double 值賦給一個(gè)float 類(lèi)型。你可以這樣修改代碼 float i = 1.0F; 或者甚至是 float i = (float) 1.0; 使用后綴字母指定數(shù)據(jù)類(lèi)型如上一節(jié)中演示的那樣,你可以通過(guò)一個(gè)后綴字母告訴Java 一段數(shù)字文字的類(lèi)型。如下那些是可用的指定數(shù)據(jù)類(lèi)型的后綴 float F long L double D 布爾值和字符值布爾和字符原始數(shù)據(jù)類(lèi)型有點(diǎn)古怪。如果你有C/C++的背景,請(qǐng)?zhí)貏e注意boolean 并確保沒(méi)有從其他語(yǔ)言中帶來(lái)任何“壞朋友”。boolean 數(shù)不能被賦予除true 或false 以外的其他的值。true 或false 的值不等價(jià)于0,-1 或其他任何數(shù)字。char 是Java 中唯一的未賦值的原始數(shù)據(jù)類(lèi)型,它是16 位長(zhǎng)的。char 類(lèi)型可以用來(lái)表示一個(gè)Unicode 字符。Unicode 是ASCII 碼的替代方案,它使用2 個(gè)字節(jié)來(lái)存儲(chǔ)字符而不是ASCII 碼中的1 個(gè)字節(jié)。這提供了65K 個(gè)字符,盡管不足以覆蓋所有文本,但已經(jīng)是對(duì)255字符的ASCII 碼的很大的改進(jìn)了。國(guó)際化是一個(gè)完全屬于其本身的話題,而且僅僅因?yàn)槟隳軌蛟谥形幕蛟侥险Z(yǔ)中表示字符,并不能表示他們可以在標(biāo)準(zhǔn)的英文風(fēng)格的操作系統(tǒng)上正常顯示。 可以通過(guò)將字符包含在單引號(hào)中來(lái)創(chuàng)建char,如下 char a = ‘z’; 注意使用的是單引號(hào)'而不是雙引號(hào)"。這在以英文為中心的世界中運(yùn)行良好,但是由于Java 是一個(gè)全球的系統(tǒng),char 可能會(huì)包含任何存在于Unicode 系統(tǒng)中的字符。這可以使用在16 進(jìn)制數(shù)之前放置\u 完成,并將整個(gè)表達(dá)式放入單引號(hào)中。因此,空格符可以被表示為 char c = ‘\u0020’ 如果你給一個(gè)char 賦予普通的數(shù)字,它將輸出為文字字符。這樣,如下程序?qū)⒋蛴〕鲎帜?A(ASCII 碼為65)和空格。 public class MyChar {public static void main (String argv []) {char i = 65;char c = ‘\u0020’;System.out.println (i);System.out.println (“This” + c + “Is a space”);} } 聲明字符串文字String 類(lèi)型不是原始數(shù)據(jù)類(lèi)型但它是如此重要以至于在某些場(chǎng)合Java 把它當(dāng)作原始數(shù)據(jù)。特性之一就是可以聲明字符串文字而不需要使用new 來(lái)初始化類(lèi)。String 文字相當(dāng)易懂。確定你記住了String 文字包含在雙引號(hào)中,而char 文字使用單引號(hào)。像這樣 String name = “James Bond” 更多關(guān)于String 類(lèi)的信息請(qǐng)看目標(biāo)9.3 和5.2。 課后測(cè)試題 問(wèn)題 1)下面哪些可以編譯成功? 1) float f = 10f; 2) float f = 10.1; 3) float f = 10.1f; 4) byte b = 10b; 問(wèn)題2)下面哪些可以編譯成功? 1) short myshort = 99S; 2) String name = ‘Excellent tutorial Mr Green’; 3) char c = 17c; 4) int z = 015; 問(wèn)題 3)下面哪些可以編譯成功? 1) boolean b = -1; 2) Boolean b2 = false; 3) int i = 019; 4) char c = 99; 答案 答案 1) 1) float f = 10f; 2) float f = 10.1f; 沒(méi)有這樣的byte 表達(dá)形式。選項(xiàng)2 將會(huì)導(dǎo)致錯(cuò)誤,因?yàn)橐粋€(gè)含有小數(shù)部分的數(shù)字的缺省類(lèi)型是double。 答案 2) 4)int z = 015; 不存在字母c 和s 這種文字指示符,一個(gè)String 必須包含在雙引號(hào)種,而不是例子中的單引號(hào)。 答案 3) 2)boolean b2 = false; 4)char c = 99; 選項(xiàng)1 顯然是錯(cuò)的,因?yàn)閎oolean 只能被賦予true 或false。選項(xiàng)3 有點(diǎn)狡猾,因?yàn)檫@是正確的聲明八進(jìn)制數(shù)的方式,但是在八進(jìn)制中你只能使用數(shù)字0 到7 而不能是9。或許這兒有點(diǎn)小把戲。 第5 章 運(yùn)算符和賦值 目標(biāo)1,應(yīng)用運(yùn)算符確定使用任意運(yùn)算符的結(jié)果,包括運(yùn)算符賦值和instanceof 來(lái)操作任意類(lèi)型類(lèi)的作用域或可達(dá)域以及以上的組合。 instanceof 運(yùn)算符instanceof 運(yùn)算符是一個(gè)很陌生的東西,在我眼里,它更像是一個(gè)方法而不是一個(gè)運(yùn)算符。你可能沒(méi)用過(guò)它而寫(xiě)了大量的Java 代碼,但為了考試的目的,你需要弄懂它。instanceof運(yùn)算符在運(yùn)行時(shí)測(cè)試一個(gè)類(lèi)的類(lèi)型然后返回一個(gè)布爾值。它一般用來(lái)說(shuō):這個(gè)類(lèi)是一個(gè) instanceof 的那個(gè)類(lèi)嗎? 如果你像下面這樣的小地方用它,看起來(lái)不是很有用 public class InOf {public static void main(String argv[]){InOf i = new InOf();if(i instanceof InOf){System.out.println("It's an instance of InOf");}//End if}//End of main } 你可能會(huì)認(rèn)為這段代碼會(huì)輸出 "It's an instance of InOf" 但是當(dāng)你訪問(wèn)了一個(gè)涉及到向下多層的對(duì)象引用的時(shí)候,情況可能會(huì)改變。你可能有一個(gè)把組件作為參數(shù)的方法,它可能真正指向一個(gè)Button,Label 或其他任何東西。這種情況下,instanceof 運(yùn)算符就可以用來(lái)測(cè)試對(duì)象的類(lèi)型,執(zhí)行匹配的角色,然后調(diào)用適當(dāng)?shù)姆椒ā?用下面的代碼舉例說(shuō)明: import java.awt.*; public class InOfComp {public static void main(String argv[]){}//End of mainpublic void mymethod(Component c){if( c instanceof Button){Button bc = (Button) c;bc.setLabel("Hello");}elseif (c instanceof Label){Label lc = (Label) c;lc.setText("Hello");}}//End of mymethod }如果運(yùn)行時(shí)的測(cè)試和角色匹配沒(méi)有執(zhí)行適當(dāng)?shù)姆椒?#xff0c;setLabel 和setText 將不可用。注意,instanceof 測(cè)試反對(duì)一個(gè)類(lèi)名但不反對(duì)類(lèi)的對(duì)象引用。 +運(yùn)算符 像你期望的一樣,+運(yùn)算符會(huì)把兩個(gè)數(shù)相加。因此下面的代碼將會(huì)輸出10 int p=5; int q=5; System.out.println(p+q);+運(yùn)算符在Java 中是一個(gè)罕見(jiàn)的操作符重載的例子。C++程序員習(xí)慣于能夠重載運(yùn)算符為他們定義的任何意義。Java 程序員沒(méi)有這種便利,但是由于對(duì)于字符串來(lái)講,加號(hào)用來(lái)做串聯(lián)是很有用的。因此,下面的代碼將編譯 String s = "One"; String s2 = "Two" String s3 = ""; s3 = s+s2; System.out.println(s3); 這段代碼將會(huì)輸出字符串OneTwo。注意,兩個(gè)連接的字符串中間沒(méi)有空格。如果你是Visual Basic 背景的程序員,下面的語(yǔ)法可能不熟悉 s2+=s3 這句代碼在Java 中可以表達(dá)成更接近于Visual Basic 形式的語(yǔ)句 s2= s2+s3在某些情況下,Java 可能在后臺(tái)調(diào)用toString 方法。就像名字顯示的那樣,這個(gè)方法會(huì)試著轉(zhuǎn)換為一個(gè)String 表達(dá)。對(duì)一個(gè)整型來(lái)講,這就意味著數(shù)字10 在toString 調(diào)用后會(huì)返回字符串“10”這點(diǎn)在下面的代碼中有所展現(xiàn)。 int p = 10; String s = "Two"; String s2 = ""; s2 = s + p; System.out.printlns(s2); 這段代碼會(huì)輸出Two10 記住,只有+運(yùn)算符可以對(duì)字符串進(jìn)行重載,如果你對(duì)字符串使用除號(hào)和減號(hào)(/ -),你會(huì)得到一個(gè)錯(cuò)誤。 為不同類(lèi)型的原始型變量賦值一個(gè)布爾類(lèi)型不能賦值給除了布爾類(lèi)型外的變量。對(duì)于C/C++程序員來(lái)講,記得這說(shuō)明一個(gè)布爾類(lèi)型不能賦值為-1 或0,而一個(gè)Java 布爾類(lèi)型不能用零或非零來(lái)替換。除了布爾型之外,我們學(xué)習(xí)這個(gè)目標(biāo)的一般規(guī)則就是擴(kuò)展轉(zhuǎn)換是允許的,當(dāng)然不考慮精確性的危險(xiǎn)。由于縮小轉(zhuǎn)換會(huì)產(chǎn)生降低精確性的結(jié)果,因此是不允許的。對(duì)于擴(kuò)展,我的意思是一個(gè)例如byte 這樣的變量,占用一個(gè)byte(8bit),可以賦值給一個(gè)integer 這樣占用多個(gè)bit的變量。如果你試著將一個(gè)integer 賦值給一個(gè)byte,你會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤 byte b= 10; int i = 0; b = i;原始類(lèi)型可能賦值給一個(gè)“更寬”的數(shù)據(jù)類(lèi)型,一個(gè)boolean 只能賦值給另一個(gè)boolean。你可能預(yù)料到你不能將一個(gè)原始類(lèi)型賦值給一個(gè)對(duì)象或相反的操作,這包括原始類(lèi)型的包裝類(lèi)。因此,下面的代碼是非法的 int j=0; Integer k = new Integer(99); j=k; //Illegal assignment of an object to a primitive賦值對(duì)象和賦值原始類(lèi)型的一個(gè)重要區(qū)別是,原始類(lèi)型在編譯時(shí)被檢查而對(duì)象在運(yùn)行時(shí)被檢查。我們將會(huì)在后面提到,當(dāng)一個(gè)對(duì)象在編譯時(shí)沒(méi)有被完全處理的話,還會(huì)有重要的含義。你當(dāng)然可以運(yùn)行一個(gè)角色來(lái)阻止一個(gè)變量適應(yīng)一個(gè)窄一些的數(shù)據(jù)類(lèi)型。一般不建議你放任精確性降低。但是如果你真的想這樣干,Java 使用C/C++的習(xí)慣,將數(shù)據(jù)類(lèi)型封裝進(jìn)()中,因此,下面的代碼將會(huì)編譯運(yùn)行。 public class Mc{public static void main(String argv[]){byte b=0;int i = 5000;b = (byte) i;System.out.println(b);} } 輸出結(jié)果是 -120 可能不是很有必要這么干。給不同類(lèi)型的對(duì)象引用賦值將一個(gè)對(duì)象引用賦值給另一個(gè)的一般規(guī)則是,你可以對(duì)繼承樹(shù)向上賦值但不能向下復(fù)制。你可以這樣想,如果你將一個(gè)子類(lèi)的實(shí)例賦值給基類(lèi),Java 知道哪個(gè)方法會(huì)在子類(lèi)中。但是一個(gè)子類(lèi)可能有基類(lèi)沒(méi)有的額外方法。你可以使用操作符強(qiáng)制轉(zhuǎn)換。對(duì)象引用可以從子類(lèi)向基類(lèi)賦值。下面舉例說(shuō)明如何向上轉(zhuǎn)換對(duì)象引用。 class Base{}public class ObRef extends Base{public static void main(String argv[]){ObRef o = new ObRef();Base b = new Base();b=o;//This will compile OK/*o=b; This would cause an error indicatingan explicit cast is needed to cast Baseto ObRef */} } ++和-運(yùn)算符你可能因?yàn)榘l(fā)現(xiàn)在一個(gè)并非微不足道的Java 程序沒(méi)有使用++或-運(yùn)算符而感到巨大的壓力,因而很容易認(rèn)為你知道的一切只是為了測(cè)試的目的而需要知道的知識(shí)。這些運(yùn)算符可以用于變量的前增或者后增。如果你不明白它們的差別,可能會(huì)因?yàn)橐粋€(gè)相當(dāng)容易的問(wèn)題而導(dǎo)致測(cè)試丟分。舉例來(lái)說(shuō),下面的代碼編譯運(yùn)行后,你認(rèn)為它會(huì)向控制臺(tái)發(fā)送什么呢? public class PostInc{static int i=1;public static void main(String argv[]){System.out.println(i++);} } 如果你編譯運(yùn)行了這段代碼,可以看到輸出是1 而不是2。這是因?yàn)?#43;+放在i 的后面,因此自增(加1)會(huì)在這行運(yùn)行后發(fā)生,對(duì)于-運(yùn)算符也是一樣的規(guī)則。 位移運(yùn)算符我恨整個(gè)的位移部分。它需要你的大腦充滿了非直覺(jué)的能力,因此很少有程序員用它。現(xiàn)實(shí)中的典型應(yīng)用的例子是密碼系統(tǒng)和低級(jí)別的鏡像文件。你可能寫(xiě)了大量的Java 程序但卻從沒(méi)自己移動(dòng)過(guò)一個(gè)bit。但是學(xué)習(xí)它的大部分的原因是為了針對(duì)測(cè)試而不是為了其他想法。測(cè)試一般會(huì)有至少一個(gè)問(wèn)題是關(guān)于位移運(yùn)算符的。如果你具有C++背景,你可能會(huì)被誤導(dǎo)成認(rèn)為你的所有C++知識(shí)都可以直接轉(zhuǎn)換成Java 語(yǔ)言。為了清楚的理解這點(diǎn),你必須用二進(jìn)制方式思考,也就是知道每個(gè)bit 位的值 32, 16, 8, 4, 2, 1不僅僅是欣賞二進(jìn)制,你還需要從整體上把握“高度褒揚(yáng)的二進(jìn)制”編號(hào)系統(tǒng)。在這個(gè)系統(tǒng)描述中,第一bit 位表示這個(gè)數(shù)是正數(shù)還是負(fù)數(shù)。盡量依靠你的直覺(jué),你會(huì)發(fā)現(xiàn)當(dāng)你理解二進(jìn)制系統(tǒng)的工作原理類(lèi)似于汽車(chē)?yán)锍瘫頃r(shí),事情開(kāi)始變得奇怪了。想象每個(gè)輪子有一個(gè)1 或者0 在上面。如果你想把下面的顯示回退的時(shí)候 00000000 00000000 00000000 00000001 如果后退了一下,就會(huì)顯示 11111111 11111111 11111111 11111111 11111111 這表示-1。如果再后退一下,會(huì)顯示 11111111 11111111 11111111 11111111 1111110這些例子有些過(guò)于單純化了。直到我學(xué)習(xí)Java 程序測(cè)試,我都只是認(rèn)為二進(jìn)制系統(tǒng)只是利用第一位來(lái)表明標(biāo)記部分。但你可以看到,實(shí)際情況要復(fù)雜的多。為了幫你多理解一點(diǎn)關(guān)于符號(hào)的知識(shí),我寫(xiě)了一個(gè)相當(dāng)簡(jiǎn)單的程序,它會(huì)顯示一個(gè)在命令行給定的數(shù)的bit 模式。也可以改進(jìn)為八進(jìn)制形式,但同樣很容易的得到大體的結(jié)果。 public class Shift{public static void main(String argv[]){int i = Integer.parseInt(argv[0]);System.out.println(Integer.toBinaryString(i));} }如果你是從C/C++背景轉(zhuǎn)到Java,你可以為Java 中的右移運(yùn)算符比C/C++稍微明確一點(diǎn)而得到一點(diǎn)安慰。在C/C++中,右移運(yùn)算符可能是取決于編譯器執(zhí)行的有符號(hào)數(shù)或無(wú)符號(hào)數(shù)。如果你是從Visual Basic 背景轉(zhuǎn)來(lái),恭喜你可以在低級(jí)別的情況下編程了。注意,本節(jié)的目標(biāo)僅僅是要求你理解應(yīng)用這些運(yùn)算符到int 值后的結(jié)果。這比應(yīng)用運(yùn)算符到byte 或者short 上顯得容易一些,尤其是負(fù)數(shù),更有可能得到不可預(yù)料的結(jié)果。 正數(shù)的無(wú)符號(hào)右移我以無(wú)符號(hào)右移開(kāi)始是因?yàn)樗亲罟之惖奈灰?#xff0c;而且需要對(duì)二進(jìn)制表示法有充分的理解。而處理負(fù)數(shù)時(shí)會(huì)更加的怪異,因此我以正數(shù)開(kāi)始。無(wú)符號(hào)右移操作將一個(gè)數(shù)字作為一個(gè)純粹的bit 模式,忽略特定的標(biāo)識(shí)位。記住,一旦你開(kāi)始將一個(gè)數(shù)字作為一系列bit 時(shí),任何bit 級(jí)別的操作可能帶給你將它作為一個(gè)普通數(shù)字時(shí)想不到的結(jié)果。無(wú)符號(hào)右移操作包括兩個(gè)操作數(shù),第一個(gè)數(shù)字是需要位移的數(shù),跟在運(yùn)算符后面的數(shù)字是需要位移的位置,例如下面 3 >>> 1表示你將數(shù)字3 的bit 向右移動(dòng)一位。二進(jìn)制補(bǔ)碼方式的系統(tǒng)意味著數(shù)字開(kāi)頭的bit 位表示它是正數(shù)還是負(fù)數(shù)。如果是0,表示數(shù)字是正數(shù),如果是1,表示它是負(fù)數(shù)。無(wú)符號(hào)數(shù)的右移的第一位總是被0 補(bǔ)上,這就意味著一個(gè)無(wú)符號(hào)數(shù)右移操作總是得到正數(shù)的結(jié)果。如果你能想起數(shù)字3 的二進(jìn)制形式011并且將它右移一位3 >> 1你會(huì)得到001注意,有新值的位離開(kāi)了數(shù)的末端并被有效的拋棄了。如果你執(zhí)行了兩位的右移,你可能因?yàn)閿?shù)字變成了0 并且0 覆蓋了所有的bit 位置而感到有點(diǎn)驚訝。如果你一直增加右移的數(shù)量,例如6 位,10 位或20 位,你會(huì)發(fā)現(xiàn)像你預(yù)料的那樣結(jié)果一直是0。可是如果你堅(jiān)持到3 >>>32得到了令人驚訝的結(jié)果3,為什么會(huì)這樣?在移動(dòng)之前的后臺(tái),一個(gè)模32 被作為操作數(shù)。模運(yùn)算符,在Java 中指一個(gè)數(shù)被另一個(gè)數(shù)通過(guò)%字符除,然后返回余數(shù)。同時(shí)一個(gè)數(shù)如果小于求模的數(shù),將會(huì)直接返回原值。同時(shí),如果一個(gè)數(shù)位移不到32 時(shí),模運(yùn)算符不會(huì)注意到這種情況,一旦到了32,模運(yùn)算符就會(huì)起作用了。因此,32%32 返回0,當(dāng)作沒(méi)有東西剩余而使運(yùn)算符作用于3 >>> 32的值是3,也就是說(shuō)3 被移動(dòng)了0 位。我開(kāi)始依靠直覺(jué)沒(méi)能發(fā)現(xiàn)這點(diǎn),因此我寫(xiě)了下面的代碼 public class shift{static int i=2;public static void main(String argv[]){System.out.println(32 % 32);System.out.println( 3 >>> 32);} } 這段代碼的輸出是 0 3 一個(gè)模32 在位移操作數(shù)執(zhí)行時(shí)起作用,這影響超過(guò)32 位的位移。 負(fù)數(shù)的無(wú)符號(hào)右移一個(gè)負(fù)數(shù)的無(wú)符號(hào)右移通常會(huì)得到一個(gè)正數(shù)的結(jié)果。我說(shuō)的通常是因?yàn)橛幸粋€(gè)例外,就是你移動(dòng)的包括符號(hào)位的原始數(shù)剛好是在32 位結(jié)束。像剛才解釋的,你通常得到一個(gè)正數(shù)的原因是無(wú)符號(hào)右移把第一個(gè)符號(hào)位用0 代替了,這表明是一個(gè)正數(shù)。一個(gè)負(fù)數(shù)的無(wú)符號(hào)右移有時(shí)看起來(lái)很怪異。看下面的代碼 System.out.println( -3 >>> 1); 你可能認(rèn)為會(huì)得到這樣的數(shù) 1 也就是說(shuō)符號(hào)位被0 代替,使它成為正數(shù),然后右移一位。但這是不會(huì)發(fā)生的,真正的結(jié)果是 2147483646 有些奇怪但卻是事實(shí)。這種奇怪的結(jié)果的背后原因跟二進(jìn)制數(shù)的表示方式有關(guān)。如果你將數(shù)字的表示想象成汽車(chē)?yán)锍瘫泶淼妮喿?#xff0c;當(dāng)你從最大可能的數(shù)開(kāi)始往下數(shù)到0 會(huì)發(fā)生什么呢?然后再回到低于0 的第一個(gè)數(shù)?所有的數(shù)位包括表示負(fù)數(shù)的符號(hào)位都會(huì)變成1。當(dāng)你執(zhí)行無(wú)符號(hào)右移時(shí),你打破了這種數(shù)字表示方式,僅僅把符號(hào)位當(dāng)作另一個(gè)數(shù)。因此,即使你從例子中-3 這樣的很小的負(fù)數(shù)開(kāi)始,你也會(huì)得到一個(gè)很大的正數(shù)。你可能會(huì)在測(cè)試中遇到這樣的問(wèn)題,問(wèn)你一個(gè)負(fù)數(shù)的無(wú)符號(hào)右移的結(jié)果。正確的答案可能看起來(lái)很不可靠。一個(gè)很小的負(fù)數(shù)通過(guò)無(wú)符號(hào)右移,可能得到一個(gè)很大的正數(shù)返回值。 有符號(hào)移動(dòng)運(yùn)算符<<和>><<和>>運(yùn)算符用0 設(shè)置新位。因此,下面的例子 System.out.println(2 << 1) 這句代碼將數(shù)字2 左移一位然后將最右邊一位置0。因此,值010變成100,即十進(jìn)制4。你可以認(rèn)為這個(gè)操作每次將原數(shù)翻倍。因此下面的代碼 System.out.println(2 << 4) 結(jié)果是32 你可以這樣認(rèn)為 2*2=4(第一次位移) 2*4=8(第二次位移) 2*8=16(第三次位移) 2*16=32(第四次位移) 當(dāng)你移動(dòng)到數(shù)字結(jié)尾時(shí),這種思考可能導(dǎo)致非常錯(cuò)誤的結(jié)果,因此下面的代碼 System.out.println(2<<30) 結(jié)果是-2147483648這看起來(lái)相當(dāng)不符合直覺(jué),但是你可以想到數(shù)字2 可以移動(dòng)最多的位置,現(xiàn)在變成了二 進(jìn)制整型可以表示的最大的負(fù)數(shù)。如果你再移動(dòng)一位 system.out.println(2 << 31) 結(jié)果是0,因?yàn)槊恳晃欢甲兂闪?,而數(shù)字2 已經(jīng)到了末端而被拋棄了。隨著有符號(hào)右移,左邊的數(shù)位(新的)在移動(dòng)前帶著符號(hào)位(作為對(duì)照,左移的新位置被置0)。這意味著右移將不會(huì)影響結(jié)果數(shù)的符號(hào)位。 2 >> 2; 這次將數(shù)字2 的所有位右移兩個(gè)位置,因此,值0010變成0000,或者說(shuō)是十進(jìn)制的0(我認(rèn)為在所有進(jìn)制中0 都是這樣)這跟執(zhí)行一個(gè)重復(fù)的integer 除法等價(jià)。在這個(gè)例子中,所有的位置都置0。有符號(hào)右移運(yùn)算符的結(jié)果數(shù)字有同樣的符號(hào)位。我創(chuàng)建了一個(gè)applet,允許你嘗試各種移動(dòng)運(yùn)算符,并且查看十進(jìn)制或者bit 模式的結(jié)果。我已經(jīng)在網(wǎng)頁(yè)中包含了這個(gè)applet 代碼,你可以看看它是如何工作的。 運(yùn)算符優(yōu)先 運(yùn)算符優(yōu)先是指哪個(gè)運(yùn)算符執(zhí)行的優(yōu)先權(quán)的順序。下表是運(yùn)算符優(yōu)先的總結(jié) Operator Precedence () ++expr --expr +expr -expr ~ ! * / % + - << >> >>> < > <= >= instanceof == != & ^ | && || ? : = += -= *= /= %= &= ^= |= <<= >>= >>>=我放在同一行的運(yùn)算符有同樣的優(yōu)先權(quán)。通常在真正編程的時(shí)候,你將會(huì)用圓括號(hào)指定你期望的執(zhí)行的表達(dá)式的順序。這意味著你可以不必真正掌握優(yōu)先級(jí)的順序并且可以讓讀你代碼的其他程序員更清楚。但你可能在測(cè)驗(yàn)中遇到建立在運(yùn)算符優(yōu)先級(jí)上的問(wèn)題,尤其是常用的運(yùn)算符如+,-,*。如果運(yùn)算符優(yōu)先級(jí)的概念對(duì)你沒(méi)什么意義,你可以試試下面的代碼會(huì)輸出什么 public class OperPres{public static void main(String argv[]){System.out.println(2 + 2 * 2);System.out.println(2 + (2 * 2));System.out.println(8 / 4 + 4);System.out.println(8 /(4 +4));int i = 1;System.out.println(i++ * 2); }第一條語(yǔ)句2+2*2 意味著2+2 的值乘2 得到輸出結(jié)果8,還是表示2*2 再加2 得到結(jié)果6 呢?關(guān)于其他計(jì)算的類(lèi)似問(wèn)題可能被問(wèn)到。順便說(shuō)一下,這個(gè)程序的輸出結(jié)果是66612。 問(wèn)題 問(wèn)題1)在下面給定的類(lèi)中,哪一個(gè)能夠不出錯(cuò)的編譯? interface IFace{} class CFace implements IFace{} class Base{} public class ObRef extends Base{public static void main(String argv[]){ObRef ob = new ObRef();Base b = new Base();Object o1 = new Object();IFace o2 = new CFace();} } 1) o1=o2; 2) b=ob; 3) ob=b; 4) o1=b; 問(wèn)題2)在下面給定的包含變量的語(yǔ)句,哪一個(gè)能夠不出錯(cuò)的編譯? String s = "Hello"; long l = 99; double d = 1.11; int i = 1; int j = 0; 1) j= i <<s; 2) j= i<<j; 3) j=i<<d; 4)j=i<<l; 問(wèn)題3)給定下面的變量 char c = 'c'; int i = 10; double d = 10; long l = 1; String s = "Hello"; 哪一個(gè)能夠不出錯(cuò)的的編譯? 1) c=c+i; 2) s+=i; 3) i+=s; 4) c+=s; 問(wèn)題4)下面的語(yǔ)句會(huì)輸出什么? System.out.println(-1 >>>1); 1) 0 2) -1 3) 1 4) 2147483647 問(wèn)題5)下面的語(yǔ)句會(huì)輸出什么? System.out.println(1 <<32); 1) 1 2) -1 3) 32 4)-2147483648 問(wèn)題6)下面的哪個(gè)語(yǔ)句是正確的? 1) System.out.println(1+1); 2) int i= 2+'2'; 3) String s= "on"+'one'; 4) byte b=255; 問(wèn)題7)當(dāng)你試著編譯運(yùn)行下面的代碼時(shí)會(huì)出現(xiàn)什么情況? Public class Pres{public static void main(String argv[]){System.out.println( 2 * 2 | 2);} } 1) Compile time errors, operators cannot be chained together in this manner 2) Compilation and output of 4 3) Compilation and output of 6 4) Compilation and output of 2 問(wèn)題8)當(dāng)你試著編譯運(yùn)行下面的代碼時(shí)會(huì)出現(xiàn)什么情況? public class ModShift{ static int i = 1; static int j =1; static int k = 0; public static void main(String argv[]){i = i << 32;j = j >>32;k = i + j;System.out.println(k++); } 1 )Compile time error 2) Compilation and output of 3 3) Compilation and output of -3 4) Compilation and output of 2 問(wèn)題9)當(dāng)你試著編譯運(yùn)行下面的代碼時(shí)會(huì)出現(xiàn)什么情況? public class Shift{static int i;static int j;public static void main(String argv[]){i = 2;j = i <<31;i =i++;System.out.println(j);System.out.println(i);} } 1) -2147483648 followed by 2 2) -2147483648 followed by 3 3) 0 followed by 3 4) 0 followed by 2 問(wèn)題10)下面程序的輸出是什么? public class Mac{public static void main(String argv[]){System.out.println( -1 >>>1 & 2);} } 1) 2147483647 2) -1 3) 10 4) 2 答案 答案1) 1)o1=o2; 2)b=ob; 4)o1=b; 答案2) 2)j= i<<j; 4)j=i<<l; 答案3) 2)s+=i; 如果你想測(cè)試各種可能出現(xiàn)的情況,可以試著編譯下面的代碼 public class Llandaff{public static void main(String argv[]){Llandaff h = new Llandaff();h.go();}public void go(){char c = 'c';int i = 10;double d = 10;long l = 1;String s = "Hello";//Start commenting these out till it all compilesc=c+i;s+=i;i+=s;c+=s;} } 答案4) 4) 2147483647 即使你的大腦中可能沒(méi)有出現(xiàn)這個(gè)數(shù)字,對(duì)于無(wú)符號(hào)右移的理解也能告訴你其他答案是錯(cuò)誤的。 答案5) 1) 1 bit 位會(huì)隨著左移運(yùn)算符“滾動(dòng)”,因此System.out.println(1 <<31);的結(jié)果將是-2147483648 答案6) 1) System.out.println(1+1); 2) int i= 2+'2'; 第三個(gè)選項(xiàng)是不正確的,因?yàn)閱我?hào)指明一個(gè)字符而不是字符串。第四個(gè)選項(xiàng)不會(huì)被編譯,因?yàn)?55 超過(guò)了一個(gè)byte 的范圍 答案7) 3) Compilation and output of 6 *運(yùn)算符的優(yōu)先級(jí)高于|運(yùn)算符。因此,該計(jì)算等價(jià)于(2*)|2,或者認(rèn)為是4|2。|運(yùn)算符比較每個(gè)位置的bit 位,如果任意數(shù)字的該位為1,則此位的輸出也是1。4 的順序是100,2 的順序是10,因此|運(yùn)算符的結(jié)果為110,即十進(jìn)制的6 答案8) 4) Compilation and output of 2 當(dāng)你移動(dòng)數(shù)字的位置為32 位時(shí),模32 會(huì)在移位時(shí)執(zhí)行。32%32 是說(shuō)如果你用32 除32 時(shí)會(huì)剩多少,答案是0。因此數(shù)字被移動(dòng)0 位。也就是返回原數(shù)。這個(gè)問(wèn)題有點(diǎn)陰險(xiǎn),因?yàn)檩敵鼋Y(jié)果中使用了后增運(yùn)算符++。這表示在當(dāng)前行結(jié)束執(zhí)行后數(shù)字被增加,因此1 加1 得到2被輸出。 答案9) 4) 0 followed by 2 這道題沒(méi)有看起來(lái)那么難。結(jié)合int 型數(shù)的位數(shù)及數(shù)字2 的bit 模式和對(duì)于有符號(hào)左移運(yùn)算符的理解,得知數(shù)字2 的唯一一個(gè)bit 位會(huì)被拋棄,所有bit 位置0。輸出結(jié)果中包括2 而不是3 是因?yàn)楹笤鲞\(yùn)算符在=運(yùn)算符賦值后將i 加1。 答案10) 4) 2 這個(gè)問(wèn)題可能讓你有些發(fā)狂的舉動(dòng)。但是如果你有正確的背景知識(shí)的話,你可以得到答案的。如果你了解二進(jìn)制表示法,你應(yīng)該知道-1 是在int 型數(shù)的每一位都是1。根據(jù)運(yùn)算符優(yōu)先級(jí),你可以知道>>>運(yùn)算符在&前執(zhí)行。如果你去掉&運(yùn)算符,你會(huì)發(fā)現(xiàn)輸出結(jié)果是選項(xiàng)1 中給出的最大數(shù)。但是由于&執(zhí)行,結(jié)果是2,表示輸出中的bit 位當(dāng)兩個(gè)數(shù)字的位都為1 時(shí)才 為1。因此輸出為2。 目標(biāo)二 equals 方法確定在任何java.lang.String,java.lang.Boolean 和java.lang.Object 類(lèi)的對(duì)象應(yīng)用布爾類(lèi)型的equals(Object)方法的結(jié)果。如果你有Visal Basic 的背景(像我一樣),用"="號(hào)比較兩部分變量的想法是很奇怪的.然而事實(shí)上,對(duì)于通常應(yīng)用的字符串的引用來(lái)講,這是相當(dāng)重要的。為了測(cè)驗(yàn)的目的,你可能被問(wèn)到關(guān)于equals 操作符對(duì)于對(duì)象引用的引用和布爾類(lèi)型的引用這樣的問(wèn)題.注意是關(guān)于布爾類(lèi)的問(wèn)題而不是關(guān)于布爾基本類(lèi)型(你不能用它來(lái)調(diào)用方法). equals 和==的不同點(diǎn)equals 方法可以被認(rèn)為是對(duì)兩個(gè)對(duì)象值的深層比較,而==操作是淺層比較。equals 方法比較兩個(gè)對(duì)象的所指對(duì)象,而不是兩個(gè)指針本身(如果你承認(rèn)Java 有指針)。這種間接方式對(duì)C++程序員可能很清楚,但是在Visal Basic 中沒(méi)有直接的比較方式。 將equals 方法用于字符型equals 方法返回一個(gè)布爾型基本類(lèi)型值。這表明它可以被用于if,while 或者其他循環(huán)語(yǔ)句。它可以被用于使用==操作符比較基本類(lèi)型的情況。當(dāng)比較字符串時(shí)equals 方法和==操作的執(zhí)行有一些不同結(jié)果。對(duì)于字符串恒量和它被Java 處理的方式是很混亂的。Java 中有兩種方法創(chuàng)建字符串。一個(gè)方法是用new 操作符,這是通常的字符串創(chuàng)建方法 String s = new String("Hello"); 但是更簡(jiǎn)短的方法是 String s= "GoodBye"; 通常這兩種創(chuàng)建字符串的方法有些微不同,但是在考試中往往會(huì)問(wèn)到這個(gè)不同。兩種創(chuàng)建字符串的方法,當(dāng)用于字符串時(shí)有相同的結(jié)果,但不用new 關(guān)鍵字會(huì)創(chuàng)建Java字符串池中指向同一個(gè)字符串的指針。字符串池是Java 存儲(chǔ)資源的一種方法。舉例說(shuō)明這 個(gè)結(jié)果 String s = "Hello"; String s2 = "Hello"; if (s==s2){System.out.println("Equal without new operator"); } String t = new String("Hello"); string u = new String("Hello"); if (t==u){System.out.println("Equal with new operator"); }在上一個(gè)目標(biāo)中你可能認(rèn)為第一個(gè)輸出"Equal without new operator"不會(huì)出現(xiàn),因?yàn)閟 和s2 時(shí)不同的對(duì)象,但是==運(yùn)算符判斷兩個(gè)對(duì)象的指針,而不是他們的值。因?yàn)镴ava 存儲(chǔ)資源的方式是重用不用new 關(guān)鍵字創(chuàng)建的字符串,所以s 和s2 有相同的“地址”,所以會(huì)輸出以下字符串。 "Equal without new operator" 但是對(duì)于第二組字符串t 和u,new 操作符迫使Java 創(chuàng)建不同的字符串。因?yàn)?#61;=操作符之比較兩個(gè)對(duì)象的地址而不是值,t 和u 有不同的地址,所以"Equal with new operator"將不會(huì)輸出。 關(guān)鍵概念equals 方法用于字符串,但是字符串被創(chuàng)建后,會(huì)執(zhí)行字符和字符的比較。應(yīng)用字符串池的作用,以及使用==和equals 方法的區(qū)別不是很顯而易見(jiàn),尤其是如果你有Visal Basic 的背景。理解它的最好方法是自己寫(xiě)個(gè)例子看它是如何工作的。試試用new 和不用new 方法創(chuàng)建字符串。 在布爾型上應(yīng)用equals 方法理解在java.lang.Boolean 上應(yīng)用equals 運(yùn)算是可能的要求。Boolean 是boolean 基本類(lèi)型的封裝類(lèi)型。它是對(duì)象型的可以在其上應(yīng)用equals 方法。依照J(rèn)DK 文檔,equals 方法用于Boolean 封裝類(lèi)型,“只是在參數(shù)非空而且布爾對(duì)象有相同的布爾值時(shí)返回真”。 例如 Boolean b1 = new Boolean(true); Boolean b2 = new Boolean(true); if(b1.equals(b2)){System.out.println("We are equal"); } boolean 和Boolean 只有微小的區(qū)別,當(dāng)你對(duì)Java 的if 運(yùn)算非常熟悉,你就知道不能像C/C++程序員一樣應(yīng)用隱含的方式,就像這樣 int x =1; if(x){ //do something, but not in Java } 這在Java 中不能執(zhí)行,因?yàn)閕f 操作的變量必須是boolean 判斷,Java 沒(méi)有C/C++中任何非空值都可被認(rèn)為真的概念。但是以下Java 代碼可以通過(guò) boolean b1=true; if(b1){ //do something in java } 雖然這是不好的編程方法,但是在語(yǔ)法上是正確的。因?yàn)閕f 操作的變量是boolean 類(lèi)型。 在對(duì)象類(lèi)型上應(yīng)用equals 方法對(duì)于Java 的基本設(shè)計(jì),任何類(lèi)的實(shí)例也是java.lang.Object 的實(shí)例。試試equals 判斷對(duì)象類(lèi)型的返回值應(yīng)用toString()方法.對(duì)于對(duì)象變量toString 方法簡(jiǎn)單返回內(nèi)存地址。所以與使用==操作的結(jié)果一樣。因?yàn)镴ava 不是設(shè)計(jì)成操作內(nèi)存地址和指針的,所以這不是個(gè)有用的判斷。 看下面的例子 public class MyParm{public static void main(String argv[]){Object m1 = new Object();Object m2 = new Object();System.out.println(m1);System.out.println(m2);if (m1.equals(m2)){System.out.println("Equals");}else{System.out.println("Not Equals");}} } 如果你編譯運(yùn)行這段代碼,會(huì)得到如下輸出 java.lang.Object@16c80b java.lang.Object@16c80a Not Equals 這些奇怪的值是內(nèi)存地址,大概根本不是你想得到的。 問(wèn)題 問(wèn)題 1)編譯運(yùn)行以下代碼時(shí)會(huì)發(fā)生什么情況? public class MyParm{public static void main(String argv[]){String s1= "One";String s2 = "One";if(s1.equals(s2)){System.out.println("String equals");}boolean b1 = true;boolean b2 = true;if(b1.equals(b2)){System.out.println("true");}} } 1) Compile time error 2) No output 3) Only "String equals" 4) "String equals" followed by "true" 問(wèn)題 2) 編譯運(yùn)行以下代碼時(shí)會(huì)發(fā)生什么情況? String s1= "One"; String s2 = new String("One"); if(s1.equals(s2)){System.out.println("String equals"); } Boolean b1 = new Boolean(true); Boolean b2 = new Boolean(true); if(b1==b2){System.out.println("Boolean Equals"); } 1) Compile time error 2) "String equals" only 3) "String equals" followed by "Boolean equals" 4) "Boolean equals" only 問(wèn)題 3)編譯運(yùn)行以下代碼的結(jié)果是什么? What will be the result of attempting to compile and run the following code? Object o1 = new Object(); Object o2 = new Object(); o1=o2; if(o1.equals(o2))System.out.println("Equals"); } 1) Compile time error 2) "Equals" 3) No output 4) Run time error 答案 答案 1) 1) Compile time error b1.equals() 這一行會(huì)引發(fā)錯(cuò)誤,因?yàn)閎1 是簡(jiǎn)單類(lèi)型,簡(jiǎn)單類(lèi)型沒(méi)有任何方法。如果創(chuàng)建基本類(lèi)型的封裝類(lèi)Boolean 你就可以應(yīng)用equals 方法。 答案 2 ) 2) "String equals" only 用==操作符簡(jiǎn)單判斷基本類(lèi)型的封裝類(lèi)Boolean 的一個(gè)實(shí)例的內(nèi)存地址。 答案 3) 2) "Equals" 因?yàn)橐粋€(gè)對(duì)象的實(shí)例可以賦值給另一個(gè)對(duì)象用 o1=o2; 它們現(xiàn)在就指向同一個(gè)內(nèi)存地址,equals 方法判斷將返回true。 目標(biāo)三 &、|、&&和||運(yùn)算符在一個(gè)包含運(yùn)算符&、|、&&、||和值已知的變量的表達(dá)式中,指出哪個(gè)運(yùn)算符被求值,表達(dá)式的值是多少?很容易忘記哪個(gè)邏輯運(yùn)算用哪個(gè)運(yùn)算符和它們所做的操作,確保你可以在考試中說(shuō)出它們的區(qū)別。如果你初次接觸這些運(yùn)算符,你可能值得花時(shí)間好好記憶你才不會(huì)對(duì)它們這些按位運(yùn)算符和邏輯運(yùn)算符的操作搞亂。你可能會(huì)記得“雙邏輯”這種表達(dá)很奇怪。 邏輯運(yùn)算符的短路效應(yīng)邏輯運(yùn)算符(&&、||)在用于“短路”邏輯像C/C++的AND 和邏輯OR 操作時(shí)有一點(diǎn)特別的結(jié)果。如果你來(lái)自Visal Basic 背景這就有些奇怪了,因?yàn)閂isal Basic 會(huì)計(jì)算所有的操作數(shù)的值。如果你理解AND,你就會(huì)理解Java 的方法,如果第一個(gè)操作數(shù)為假,第二個(gè)操作數(shù)的值就沒(méi)有作用了,所有的結(jié)構(gòu)都為假。對(duì)于邏輯OR 也是,如果第一個(gè)操作數(shù)為真,所有的計(jì)算結(jié)果將為真,因?yàn)橹灰粋€(gè)操作數(shù)為真最后的結(jié)果就為真。這種依靠一邊結(jié)果的壓縮計(jì)算可能有一個(gè)結(jié)果。請(qǐng)看下面的例子 public class MyClass1{public static void main(String argv[]){int Output=10;boolean b1 = false;if((b1==true) && ((Output+=10)==20)){System.out.println("We are equal "+Output);}else{System.out.println("Not equal! "+Output);}} } "Not equal 10"會(huì)被輸出。這說(shuō)明Output+=10 這個(gè)運(yùn)算永遠(yuǎn)不會(huì)被執(zhí)行,因?yàn)樵诘谝粋€(gè)操作數(shù)的值為false 時(shí)運(yùn)算就停止了。如果你把b1 的值改成true,運(yùn)算就會(huì)像你想得一樣執(zhí)行,輸出會(huì)是"We are equal 20".當(dāng)你真的不想在有任何值為false 時(shí)進(jìn)行其它運(yùn)算時(shí),這也許有時(shí)是便捷的方法,但是當(dāng)你完全不熟悉時(shí)這也許會(huì)產(chǎn)生意外的結(jié)果。 按位運(yùn)算符&和|運(yùn)算符用于做整形的按位與和或操作.在考試中你會(huì)遇到這樣的問(wèn)題,給出一個(gè)十進(jìn)制的數(shù),然你用按位與和或運(yùn)算計(jì)算.要執(zhí)行這些操作你需要熟悉從十進(jìn)制到二進(jìn)制的轉(zhuǎn)換,并且知道其比特形式.這有一個(gè)典型的例子 下面運(yùn)算的結(jié)果是什么? 3 | 4 3 的二進(jìn)制比特形式是11 4 的二進(jìn)制比特形式是100 要執(zhí)行二進(jìn)制或運(yùn)算,兩個(gè)數(shù)的每個(gè)比特都要互相比較.如果任一個(gè)比特為1 則結(jié)果中的比特?cái)?shù)為1.所以這個(gè)操作的結(jié)果的二進(jìn)制形式是111,也就是十進(jìn)制的7.目標(biāo)沒(méi)有特別要求你知道按位XOR 運(yùn)算,用^符號(hào)執(zhí)行。 用二進(jìn)制思考如果你感到用二進(jìn)制思考不舒服(我更習(xí)慣用十進(jìn)制思考),你也可能想做一些練習(xí)來(lái)掌握這個(gè)問(wèn)題和二進(jìn)制轉(zhuǎn)換操作。如果你使用windows 你可能發(fā)現(xiàn)用計(jì)算器的科學(xué)模式很有用。你可以在標(biāo)準(zhǔn)模式下選則View 和switch 來(lái)變成科學(xué)模式。在科學(xué)模式中,你可以轉(zhuǎn)換數(shù)值來(lái)看它的十進(jìn)制和二進(jìn)制模式,這會(huì)顯示數(shù)的二進(jìn)制形式。有一個(gè)很方便的竅門(mén),我如果在寫(xiě)比特轉(zhuǎn)換applet 之前知道就好了,就是怎樣用整形來(lái)表示比特形式。這里有一個(gè)實(shí)現(xiàn)這個(gè)的小程序。 public class BinDec{public static void main(String argv[]){System.out.println(Integer.parseInt("11",2));System.out.println(Integer.toString(64,2));} } 如果你編譯運(yùn)行這段程序,將會(huì)得到輸出 3 1000000 注意程序怎樣把二進(jìn)制11 轉(zhuǎn)換成十進(jìn)制的對(duì)應(yīng)數(shù)3,又是怎樣把十進(jìn)制數(shù)64 轉(zhuǎn)換成相應(yīng)的比特形式。每個(gè)方法的第二個(gè)參數(shù)是基數(shù)。所以在這個(gè)例子中會(huì)把書(shū)轉(zhuǎn)換成2 為基數(shù)的,而我們常常會(huì)用10 為基數(shù)的數(shù)。 問(wèn)題 問(wèn)題 1 )你嘗試編譯運(yùn)行以下代碼時(shí)會(huì)發(fā)生什么情況 int Output=10; boolean b1 = false; if((b1==true) && ((Output+=10)==20)){System.out.println("We are equal "+Output); }else {System.out.println("Not equal! "+Output); } 1) Compile error, attempting to perform binary comparison on logical data type 2) Compilation and output of "We are equal 10" 3) Compilation and output of "Not equal! 20" 4) Compilation and output of "Not equal! 10" 問(wèn)題 2 )下面一行代碼會(huì)有什么輸出 System.out.println(010|4); 1) 14 2) 0 3) 6 4) 12 問(wèn)題 3 )下面哪項(xiàng)編譯沒(méi)有錯(cuò)誤 1) int i=10; int j = 4; System.out.println(i||j); 2) int i=10; int j = 4; System.out.println(i|j); 3) boolean b1=true; boolean b2=true; System.out.println(b1|b2); 4) boolean b1=true; boolean b2=true; System.out.println(b1||b2); 答案 答案 1) 4) Compilation and output of "Not equal! 10" 輸出是"Not equal 10". 這表明運(yùn)算Output+=10 沒(méi)有被執(zhí)行,因?yàn)檫\(yùn)算在第一個(gè)操作數(shù)被算出為true 時(shí)就停止了。如果你把b1 的值改成true,運(yùn)算會(huì)像你想得那樣進(jìn)行,輸出結(jié)果為 "We are equal 20";. 答案2 ) 4) 12 和二進(jìn)制OR 目標(biāo)相同,這個(gè)問(wèn)題要求你理解開(kāi)頭的零表示八進(jìn)制符號(hào),第一個(gè)1 表示數(shù)中有一個(gè)8,沒(méi)有其他數(shù)。所以十進(jìn)制運(yùn)算是 8|4 轉(zhuǎn)換成二進(jìn)制形式為 1000 0100 ---- 1100 |運(yùn)算符表示兩個(gè)數(shù)每一位進(jìn)行運(yùn)算,其中有一個(gè)為1,相應(yīng)位的結(jié)果就為1。 答案 3) 2,3,4 選項(xiàng)一不會(huì)通過(guò)編譯,因?yàn)樗噲D在整型上運(yùn)行邏輯OR 操作。邏輯或只能用于操作boolean類(lèi)型參數(shù)。 目標(biāo)四 在方法中傳遞對(duì)象和基本類(lèi)型值判斷傳遞對(duì)象和基本類(lèi)型參數(shù)到方法中的結(jié)果,在方法中進(jìn)行賦值或者其它的修改操作。 目標(biāo)中注意事項(xiàng)目標(biāo)可能會(huì)問(wèn)你是否理解傳遞值到方法中時(shí)會(huì)發(fā)生什么結(jié)果。如果方法中的代碼改變了變量,對(duì)外部的方法是否可見(jiàn)?直接引用Peter van der Lindens 的Java 程序員解答的一段話(在http://www.afu.com)//引用所有的變量(基本類(lèi)型值和對(duì)象的引用值)都是值傳遞。但是這不是全部情況,對(duì)象是經(jīng)常通過(guò)Java 的引用變量來(lái)操作的。所以也可以說(shuō)對(duì)象是通過(guò)引用傳遞的(引用變量通過(guò)值傳遞)。這是變量不使用對(duì)象的值而是像前一個(gè)問(wèn)題那樣描述的使用對(duì)象引用的值的結(jié)果。最后一行:調(diào)用者對(duì)基本類(lèi)型參數(shù)(int,char 等)的拷貝在相應(yīng)參數(shù)變化時(shí)不會(huì)改變。但是,在被調(diào)用方法改變相應(yīng)作為參數(shù)傳遞的對(duì)象(引用)字段時(shí),調(diào)用者的對(duì)象也改變其字段。 //引用結(jié)束如果你來(lái)自C++背景,你可能對(duì)值傳遞參數(shù)和用&符號(hào)傳遞引用參數(shù)熟悉。在Java 中沒(méi)有這樣的選擇,所有的都是值傳遞。但是看起來(lái)并不總是這樣。如果你傳遞的對(duì)象是對(duì)象的引用,你不能直接對(duì)對(duì)象的引用進(jìn)行操作。所以如果你操作一個(gè)傳遞到方法的對(duì)象的字段,結(jié)果就好像你按引用傳遞(任何改變結(jié)果都會(huì)返回到調(diào)用函數(shù))。將對(duì)象引用作為方法變量 請(qǐng)看下面的例子 class ValHold{public int i = 10; } public class ObParm{public static void main(String argv[]){ObParm o = new ObParm();o.amethod();}public void amethod(){ValHold v = new ValHold();v.i=10;System.out.println("Before another = "+ v.i);another(v);System.out.println("After another = "+ v.i);}//End of amethodpublic void another(ValHold v){v.i = 20;System.out.println("In another = "+ v.i);}//End of another } 程序的輸出結(jié)果是 Before another = 10 In another = 20 After another = 20 看變量i 是怎么被修改的。如果Java 總是值傳遞(也就是對(duì)變量的拷貝),它是怎么被修改的呢?過(guò)程是這樣的方法收到了句柄的拷貝或者對(duì)象的引用,但是這個(gè)引用的作用類(lèi)似于指向真實(shí)的指針。對(duì)這個(gè)字段的改變會(huì)反映到它所指的值。這有些像是在C/C++中指針的自動(dòng)間接應(yīng)用的的作用。 基本類(lèi)型作為方法參數(shù) 當(dāng)你對(duì)方法傳遞基本類(lèi)型參數(shù),是直接傳遞值。方法得到它的拷貝,任何修改都不會(huì)在外部方法得到反映。請(qǐng)看以下例子 public class Parm{public static void main(String argv[]){Parm p = new Parm();p.amethod();}//End of mainpublic void amethod(){int i=10;System.out.println("Before another i= " +i);another(i);System.out.println("After another i= " + i);}//End of amethodpublic void another(int i){i+=10;System.out.println("In another i= " + i);}//End of another } 程序的輸出結(jié)果如下 Before another i= 10 In another i= 20 After another i= 10 習(xí)題 習(xí)題 1) 以下所給代碼的輸出是什么? class ValHold{public int i = 10; } public class ObParm{public static void main(String argv[]){ObParm o = new ObParm();o.amethod();}public void amethod(){int i = 99;ValHold v = new ValHold();v.i=30;another(v,i);System.out.println(v.i);}//End of amethodpublic void another(ValHold v, int i){i=0;v.i = 20;ValHold vh = new ValHold();v = vh;System.out.println(v.i+ " "+i);}//End of another } 1) 10,0, 30 2) 20,0,30 3) 20,99,30 4) 10,0,20 答案 答案 1) 4) 10,0,20 第6 章 重載,重寫(xiě),運(yùn)行時(shí)類(lèi)型和OO 目標(biāo)一 封裝和OO 設(shè)計(jì)陳述封裝在面向?qū)ο笤O(shè)計(jì)中的好處,并編寫(xiě)實(shí)現(xiàn)緊密封裝的類(lèi)的代碼,陳述“is a”和“has a”的關(guān)系。 “Is a”和“has a”關(guān)系這是一個(gè)很基礎(chǔ)的OO 問(wèn)題,你很可能在考試中碰到一個(gè)題目。本質(zhì)上,它是為了考察你是否理解何時(shí)在談?wù)搶?duì)象所屬的類(lèi)結(jié)構(gòu)以及何時(shí)是在談?wù)撘粋€(gè)類(lèi)擁有的方法或域。因此,貓是動(dòng)物的一種(IS A),貓有尾巴(HAS A)。當(dāng)然,區(qū)別可能會(huì)模糊不清。如果你是一名動(dòng)物學(xué)家并且知道動(dòng)物種類(lèi)群的正確名字,你可能會(huì)說(shuō)貓是(IS A)longlatinwordforanimalgroupwithtails(一個(gè)很長(zhǎng)的表示有尾巴的動(dòng)物群的拉丁單詞)。但是出于考試的目的,這不在考慮范圍之內(nèi)。考試題目趨向于這種類(lèi)型:根據(jù)一段對(duì)于潛在層次結(jié)構(gòu)的描述,你會(huì)得到諸如什么應(yīng)該是域,什么應(yīng)該是新的子類(lèi)的問(wèn)題。這些問(wèn)題乍一看比較復(fù)雜,但是如果你仔細(xì)閱讀的話都十分明顯。 封裝Java1.1 的目標(biāo)中沒(méi)有特別提到封裝,雖然你會(huì)被急切的要求學(xué)習(xí)Java 而不沒(méi)有機(jī)會(huì)接觸概念。封裝包含將類(lèi)的接口從實(shí)現(xiàn)中分離出來(lái)。這意味著你無(wú)法“偶然地”破壞某個(gè)域的值,你必須使用方法來(lái)修改值。通常,要實(shí)現(xiàn)這一點(diǎn),需要?jiǎng)?chuàng)建私有變量(域),它們只能通過(guò)方法來(lái)更新和提取。這些方法的標(biāo)準(zhǔn)命名規(guī)范是setFieldName,getFieldName 例如,你要改變形狀的顏色,你會(huì)創(chuàng)建如下形式的方法對(duì) public void setColor (Color c) {cBack = c; } public Color getColor () {return cBack; } 控制變量訪問(wèn)的主要關(guān)鍵字為 public private protected不要受到誤導(dǎo)而認(rèn)為訪問(wèn)控制系統(tǒng)與安全有關(guān)。它不是為防止程序員攻擊變量而設(shè)計(jì)的,而是為了幫助避免不期望的修改。使用上面Color 例子的標(biāo)準(zhǔn)方法是將cBack 域設(shè)為私有的。一個(gè)私有域只在當(dāng)前類(lèi)內(nèi)部可見(jiàn)。這意味著程序員不能偶然地在另一個(gè)類(lèi)中寫(xiě)代碼來(lái)修改它的值。這有助于減少bug的引入。接口與實(shí)現(xiàn)的分離使得在一個(gè)類(lèi)中修改代碼而不破壞其他代碼變得更簡(jiǎn)單。對(duì)于類(lèi)的設(shè)計(jì)者這使他們能夠修改類(lèi)而不必破壞使用它的程序。類(lèi)的設(shè)計(jì)者可以為域修改的“安全檢查”插入額外的檢查流程。我曾經(jīng)致力于保險(xiǎn)項(xiàng)目,此項(xiàng)目中的客戶的年齡值可能小于0。如果這個(gè)值被保存在簡(jiǎn)單的域中,比如整數(shù),就沒(méi)有明顯的地方可以存放檢查流程。如果年齡只可以通過(guò)set 和get 方法訪問(wèn),就可以通過(guò)這種不破壞現(xiàn)存代碼的方式來(lái)對(duì)插入進(jìn)行0 或負(fù)數(shù)年齡檢查。當(dāng)然,隨著開(kāi)發(fā)的進(jìn)行,會(huì)發(fā)現(xiàn)更多需要檢查的情況。對(duì)于類(lèi)的最終用戶,這意味著他們不需要理解內(nèi)部工作,呈現(xiàn)在他們面前的是一個(gè)清晰的處理數(shù)據(jù)的接口。最終用戶可以相信更新類(lèi)代碼不會(huì)破壞他們現(xiàn)有的代碼。 運(yùn)行時(shí)類(lèi)型因?yàn)槎鄳B(tài)機(jī)制允許在運(yùn)行時(shí)選擇執(zhí)行方法的版本,有時(shí)候?qū)⒁\(yùn)行的方法并不明顯的。 以如下代碼為例。 class Base {int i = 99;public void amethod () {System.out.println (“Base.amethod ()”);} } public class RType extends Base {int i = -1;public static void main (String argv []) {Base b = new RType (); //<= Note the typeSystem.out.println (b.i);b.amethod ();}public void amethod () {System.out.println (“RType.amethod ()”);} } 注意,b 引用的類(lèi)型是Base,但是實(shí)際的類(lèi)型是類(lèi)RType。對(duì)amethod 的調(diào)用將啟動(dòng)RType 中的版本,但是b.i 輸出的調(diào)用將引用Base 類(lèi)中的域i。 課后測(cè)試題 問(wèn)題1)假設(shè)你被給予如下設(shè)計(jì) “一個(gè)人有姓名,年齡,地址和性別。你將要設(shè)計(jì)一個(gè)類(lèi)來(lái)表示一類(lèi)叫做病人的人。這種人可以被給予診斷,有配偶并且可能活著”。假設(shè)表示人的類(lèi)已經(jīng)創(chuàng)建了,當(dāng)你設(shè)計(jì)病人類(lèi)時(shí)如下哪些應(yīng)該被包含在內(nèi)? 1) registration date 2) age 3) sex 4) diagnosis 問(wèn)題 2)當(dāng)你試圖編譯并運(yùn)行如下代碼時(shí)會(huì)發(fā)生什么? class Base {int i = 99;public void amethod () {System.out.println (“Base.amethod ()”);}Base () {amethod ();} } public class RType extends Base {int i = -1;public static void main (String argv []) {Base b = new RType ();System.out.println (b.i);b.amethod ();}public void amethod () {System.out.println (“RType.amethod ()”);} } 1) RType.amethod -1 RType.amethod 2) RType.amethod 99 RType.amethod 3) 99 RType.amethod 4) Compile time error 問(wèn)題 3)你的首席軟件設(shè)計(jì)者向你展示了她正要?jiǎng)?chuàng)建的新電腦部件系統(tǒng)的草圖。在層次結(jié)構(gòu)的頂端是一個(gè)叫做Computer 的類(lèi),在此之下是兩個(gè)子類(lèi)。一個(gè)叫做LinuxPC,另一個(gè)叫做WindowsPC。兩者之間最大的不同點(diǎn)是一個(gè)運(yùn)行Linux 操作系統(tǒng),另一個(gè)運(yùn)行Windows系統(tǒng)(當(dāng)然另一個(gè)不同在于一個(gè)需要不停的重啟,另一個(gè)則能夠可靠的運(yùn)行)。在WindowsPC 之下是兩個(gè)子類(lèi),一個(gè)叫做Server,另一個(gè)叫做Workstation。你如何評(píng)價(jià)你的設(shè)計(jì)者的工作? 1) Give the go ahead for further design using the current scheme 2) Ask for a re-design of the hierarchy with changing the Operation System to a field rather than Class type 3) Ask for the option of WindowsPC to be removed as it will soon be absolete 4) Change the hierarchy to remove the need for the superfluous Computer Class. 問(wèn)題 4)假設(shè)有如下類(lèi) class Base {int Age = 33; } 關(guān)于對(duì)Age 域的訪問(wèn),你會(huì)如何修改來(lái)改進(jìn)這個(gè)類(lèi)? 1) Define the variable Age as private 2) Define the variable Age as protected 3) Define the variable Age as private and create a get method that returns it and a set method that updates it 4) Define the variable Age as protected and create a set method that returns it and a get method that updates it 問(wèn)題 5)下面哪些是封裝的好處? 1) All variables can be manipulated as Objects instead of primitives 2) by making all variables protected they are protected from accidental corruption 3) The implementation of a class can be changed without breaking code that uses it 4) Making all methods protected prevents accidental corruption of data 問(wèn)題 6)指出三個(gè)面向?qū)ο缶幊痰闹饕攸c(diǎn)? 1) encapsulation, dynamic binding, polymorphism 2) polymorphism, overloading, overriding 3) encapsulation, inheritance, dynamic binding 4) encapsulation, inheritance, polymorphism 問(wèn)題 7)你如何在類(lèi)中實(shí)現(xiàn)封裝? 1) make all variables protected and only allow access via methods 2) make all variables private and only allow access via methods 3) ensure all variables are represented by wrapper classes 4) ensure all variables are accessed through methods in an ancestor class 答案 答案1) 1) registration date 2) diagnosis 對(duì)于病人來(lái)說(shuō),注冊(cè)日期是一個(gè)合理的添加域,并且設(shè)計(jì)明確地指出病人應(yīng)該有診斷報(bào)告。由于病人是人的一種,它應(yīng)該有域age 和sex(假設(shè)它們沒(méi)有被聲明為私有的)。 答案 2) 2) RType.amethod 99 RType.amethod 如果這個(gè)答案看起來(lái)靠不住,試著編譯并運(yùn)行代碼。原因是這段代碼創(chuàng)建了一個(gè)RType 類(lèi)的實(shí)例但是把它賦予一個(gè)指向Base 類(lèi)的引用。在這種情況下,涉及的任何域,比如i,都會(huì)指向Base 類(lèi)中的值,但是方法的調(diào)用將會(huì)指向?qū)嶋H類(lèi)中的方法而不是引用句柄中的方法。 答案 3) 2) Ask for a re-design of the hierarchy with changing the Operating System to a field rather than Class type 答案 4) 3) Define the variable Age as private and create a get method that returns it and a set method that updates it 答案 5) 3) The implementation of a class can be changed without breaking code that uses it 答案 6) 4) encapsulation, inheritance, polymorphism 我曾經(jīng)在一次工作面試上遇到這個(gè)問(wèn)題。我得到了那份工作。不能保證你一定會(huì)在考試中遇到類(lèi)似的問(wèn)題,但是知道的話會(huì)很有用。 答案 7) 2)make all variables private and only allow access via methods 目標(biāo)二 重寫(xiě)和重載編寫(xiě)調(diào)用重寫(xiě)或重載的方法以及父類(lèi)的或重載過(guò)的構(gòu)造函數(shù);并且描述調(diào)用這些方法的效果。 目標(biāo)的評(píng)論術(shù)語(yǔ)重載(overloaded)和重寫(xiě)(overridden)太相近了以至于會(huì)造成混淆。我記憶的方式是想象某物被踐踏(overridden)字面上的意思是被沉重的交通工具壓過(guò)并且不再是其原來(lái)的樣子。某物負(fù)載過(guò)重(overloaded)仍然在移動(dòng),但是負(fù)擔(dān)過(guò)重的功能將使其花費(fèi)巨大的努力。這只是一個(gè)區(qū)別兩者的小竅門(mén),跟Java 中實(shí)際的操作沒(méi)有任何關(guān)系。 重載方法重載是Java 中實(shí)現(xiàn)面向?qū)ο?#xff0c;多態(tài)機(jī)制等概念的方式之一。多態(tài)性(Polymorphism)是由多個(gè)單詞組成的詞語(yǔ),Ply 意為“很多”,“morphism”暗示著含義。因此,重載允許同一個(gè)方法名稱具有多種意思或用途。方法重載是編譯器的技巧,依賴于不同的參數(shù),它允許你使用相同的名稱來(lái)完成不同的動(dòng)作。這樣做的好處是Java 可以在運(yùn)行時(shí)決定調(diào)用的方法而不是在編譯時(shí)決定。因而,設(shè)想一下你正在為模擬Java 認(rèn)證考試設(shè)計(jì)系統(tǒng)接口。答案可能作為整數(shù),布爾數(shù)或文本字符串得到。你可以為每一個(gè)參數(shù)類(lèi)型創(chuàng)建一個(gè)方法,并給予相應(yīng)的名字,比如 markanswerboolean (Boolean answer) { } markanswerint (int answer) { } markanswerString (String answer ) { } 這樣可以正常運(yùn)行,但這也意味著類(lèi)的未來(lái)用戶需要知道更多不必要的方法名。使用一個(gè)單一的方法名會(huì)更實(shí)用,編譯器可以根據(jù)參數(shù)類(lèi)型和數(shù)目來(lái)決定調(diào)用的實(shí)際代碼。進(jìn)行方法重載不需要記住任何關(guān)鍵字,你只要?jiǎng)?chuàng)建多個(gè)具有不同數(shù)目或類(lèi)型的參數(shù)的同名方法就可以了。參數(shù)的名稱并不重要,但是數(shù)目和類(lèi)型必須不同。如下是一個(gè)markanswer方法重載的例子 void markanwwer (String answer) { } void markanswer (int answer) { } 如下不是重載的實(shí)例,它會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤,指出這是重復(fù)的方法聲明。 void markanswer (String answer) { } void markanswer (String title) { } 返回值類(lèi)型并不是實(shí)現(xiàn)重載署名的要素。因此,改變?nèi)缟洗a使其放回int 值仍然會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤,但是這一次指出方法不能 用不同的返回值類(lèi)型進(jìn)行重新定義。 重寫(xiě)方法重寫(xiě)方法意味著它的所有功能被完全取代了。重寫(xiě)是在子類(lèi)中對(duì)一個(gè)定義在父類(lèi)中的方法進(jìn)行修改。為了重寫(xiě)方法,要在子類(lèi)中定義一個(gè)與父類(lèi)中具有完全相同署名的方法。這樣做會(huì)覆蓋父類(lèi)中的方法,并且此方法的功能再也不能被直接訪問(wèn)了。Java 提供了一個(gè)重寫(xiě)的例子,就是每個(gè)類(lèi)都從最高父類(lèi)Object 中繼承的equals 方法。繼承的equals 版本僅僅在內(nèi)存中比較類(lèi)引用的實(shí)例。這通常不是我們想要的,特別是對(duì)于String。對(duì)于String,你通常希望通過(guò)逐個(gè)字符的比較來(lái)確定兩個(gè)字符串是否相同。為了做到這一點(diǎn),String 中的equals 版本進(jìn)行了重寫(xiě),并能執(zhí)行逐個(gè)字符的比較。 調(diào)用基類(lèi)的構(gòu)造函數(shù)構(gòu)造函數(shù)是一種在每次創(chuàng)建類(lèi)的實(shí)例時(shí)自動(dòng)運(yùn)行的特殊方法。Java 能夠識(shí)別構(gòu)造函數(shù),因?yàn)樗鼈兙哂信c類(lèi)本身相同的名字,并不需要返回值。與其他方法一樣,構(gòu)造函數(shù)可以接受參數(shù),并且根據(jù)如何初始化類(lèi),你可以傳遞不同的參數(shù)。如此,以AWT 包中的Button 類(lèi)為例,通過(guò)重載提供了兩個(gè)構(gòu)造函數(shù)的版本。一個(gè)是 Button () Button (String label) 因此,你可以創(chuàng)建一個(gè)沒(méi)有標(biāo)簽的按鈕,并在稍后設(shè)定,或者使用普通的版本在創(chuàng)建的時(shí)候就設(shè)定標(biāo)簽。但是,構(gòu)造函數(shù)是不能被繼承的,所以如果你想從父類(lèi)中獲得一些有用的構(gòu)造函數(shù),缺省是不可用的。因此,如下代碼將不能編譯通過(guò) class Base {public Base () {}public Base (int i) {} } public class MyOver extends Base {public static void main (String argv []) {MyOver m = new MyOver (10); // Will Not compile} } 要從父類(lèi)中得到構(gòu)造函數(shù),你需要使用神奇的關(guān)鍵字super。這個(gè)關(guān)鍵字可以被當(dāng)作一個(gè)方法來(lái)使用,并且傳遞適當(dāng)?shù)膮?shù)使之與你要求的父類(lèi)中的構(gòu)造函數(shù)相吻合。在以下修改了上述代碼的例子中,關(guān)鍵字super 被用來(lái)調(diào)用基類(lèi)中接受integer 參數(shù)的構(gòu)造函數(shù)版本,這段代碼編譯時(shí)不會(huì)報(bào)錯(cuò)。 class Base {public Base () {}public Base (int i) {} } public class MyOver extends Base {public static void main (String arg []) {MyOver m = new MyOver (10);}MyOver (int i) {super (i);} } 使用this ()調(diào)用構(gòu)造函數(shù)與使用super ()調(diào)用基類(lèi)中構(gòu)造函數(shù)的方式相同,你可以使用this 調(diào)用當(dāng)前類(lèi)中的其他構(gòu)造函數(shù)。這樣,在前面的例子中你可以像下面那樣定義另一個(gè)構(gòu)造函數(shù) MyOver (String s, int i) {this (i); }如你猜測(cè)的,這將會(huì)調(diào)用當(dāng)前類(lèi)中那個(gè)只接受一個(gè)整數(shù)參數(shù)的構(gòu)造函數(shù)。如果你在構(gòu)造函數(shù)中使用super ()或this (),必須第一個(gè)調(diào)用它。由于只有一個(gè)能被第一個(gè)調(diào)用,你不能在構(gòu)造函數(shù)中既使用super ()又使用this ()。因此,如下代碼會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤。 MyOver (String s, int i) {this (i);super (); // Causes a compile time error }基于構(gòu)造函數(shù)不能被繼承的知識(shí),很明顯重寫(xiě)是不切合實(shí)際的。如果你有一個(gè)叫做Base的基類(lèi),你創(chuàng)建了一個(gè)繼承它的子類(lèi),對(duì)于要重寫(xiě)構(gòu)造函數(shù)的子類(lèi),它的名字必須跟父類(lèi)相同。這會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤。這是一個(gè)沒(méi)有層次意義的例子。 class Base {} class Base extends Base {} //Compile time error! 構(gòu)造函數(shù)和類(lèi)層次構(gòu)造函數(shù)總是從層次結(jié)構(gòu)的頂端開(kāi)始稱作向下。在考試中,你很可能會(huì)遇到一些題目涉及到在類(lèi)層次中多次調(diào)用this 和super,你必須指出輸出什么內(nèi)容。當(dāng)你遇到復(fù)雜的層次結(jié)構(gòu)時(shí)請(qǐng)格外小心,這可能跟構(gòu)造函數(shù)沒(méi)有關(guān)系,可能由于構(gòu)造函數(shù)同時(shí)調(diào)用了this 和super,而導(dǎo)致編譯時(shí)錯(cuò)誤。有如下例子 class Mammal {Mammal () {System.out.println (“Creating Mammal”);} } public class Human extends Mammal {public static void main (String argv []) {Human h = new Human ();}Human () {System.out.println (“Create Humn”);} } 當(dāng)運(yùn)行代碼時(shí),由于隱式調(diào)用了基類(lèi)中的無(wú)參構(gòu)造函數(shù),首先會(huì)輸出字符串“Create Mammal”。 課后測(cè)試題 問(wèn)題 1)假設(shè)有如下類(lèi)定義,如下哪些方法可以合法放置在“//Here”的注釋之后? public class Rid {public void amethod (int i, String s) {}// Here } 1) public void amethod (String s, int i) {} 2) public int amethod (int i, String s) {} 3) public void amethod (int i, String mystring) {} 4) public void Amethod (int i, String s) {} 問(wèn)題 2)假設(shè)有如下類(lèi)定義,哪些代碼可以被合法放置在注釋“//Here”之后? class Base {public Base (int i) {} } public class MyOver extends Base {public static void main (String arg []) {MyOver m = new MyOver (10);}MyOver (int i) {super (i);}MyOver (String s, int i) {this (i);//Here} } 1) MyOver m = new MyOver (): 2) super (); 3) this (“Hello”, 10); 4) Base b = new Base (10); 問(wèn)題 3)假設(shè)有如下類(lèi)定義 class Mammal {Mammal () {System.out.println (“Mamml”);} } class Dog extends Mammal {Dog () {System.out.println (“Dog”);} } public class Collie extends Dog {public static void main (String argv []) {Collie c = new Collie ();}Collie () {this (“Good Dog”);System.out.println (“Collie”);}Collie (String s) {System.out.println (s);} } 將會(huì)輸出什么? 1) Compile time error 2) Mammal, Dog, Good Dog, Collie 3) Good Dog, Collie, Dog, Mammal 4) Good Dog, Collie 問(wèn)題 4)下面哪些論述是正確的? 1) Constructors are not inherited 2) Constructors can be overridden 3) A parental constructor can be invoked using this 4) Any method may contain a call to this or super 問(wèn)題 5)試圖編譯并運(yùn)行下面代碼會(huì)發(fā)生什么? class Base {public void amethod (int i, String s) {System.out.println (“Base amethod”);}Base () {System.out.println (“Base Constructor”);} } public class Child extends Base {int i;String Parm = “Hello”;public static void main (String argv []) {Child c = new Child ():c.amethod ():}void amethod (int i, String Parm) {super.amethod (i, Parm);}public void amethod () {} } 1) Compile time error 2) Error caused by illegal syntax super.amethod (i, Parm) 3) Output of “Base Constructor” 4) Error caused by incorrect parameter names in call to super.amethod 問(wèn)題 6)試圖編譯并運(yùn)行如下代碼時(shí)將發(fā)生什么? class Mammal {Mammal () {System.out.println (“Four”);}public void ears () {System.out.println (“Two”);} } class Dog extends Mammal {Dog () {super.ears ();System.out.println (“Three”);} } public class Scottie extends Dog {public static void main (String argv []) {System.out.println (“One”);Scottie h = new Scottie ();} } 1) One, Three, Two, Four 2) One, Four, Three, Two 3) One, Four, Two, Three 4) Compile time error 答案 答案 1) 1) public void amethod (String s, int i) {} 4)public void Amethod (int i, String s) {} Amethod 中的大寫(xiě)字母A 意味著這是不同的方法。 答案 2) 4)Base b = new Base (10); 任何this 或super 的調(diào)用都必須是構(gòu)造函數(shù)中的第一行。由于方法已經(jīng)調(diào)用了this,不能有別的調(diào)用插入了。 答案 3) 2) Mammal, Dog, Good Dog, Collie 答案 4) 1) Constructors are not inherited 父類(lèi)的構(gòu)造函數(shù)應(yīng)該使用super 調(diào)用,而不是this。 答案 5) 1) Compile time error 這會(huì)導(dǎo)致一個(gè)錯(cuò)誤,意思是說(shuō)“你不能重寫(xiě)方法使其訪問(wèn)權(quán)限更靠近私有”。基類(lèi)的amethod版本被明確的標(biāo)注為public,但是在子類(lèi)中沒(méi)有標(biāo)識(shí)符。好了,所以這不是在考察你的構(gòu)造函數(shù)重載的知識(shí),但是他們也沒(méi)在考試中告訴你主題。若這段代碼沒(méi)有省略關(guān)鍵字public,將會(huì)輸出“Base constructor”,選項(xiàng)3。 答案 6) 3)One, Four, Two, Three 類(lèi)是從層次的根部往下創(chuàng)建的。因此,首先輸出One,因?yàn)樗赟cottie h 初始化之前創(chuàng)建。然后,JVM 移動(dòng)到層次的基類(lèi),運(yùn)行“祖父類(lèi)”Mammal 的構(gòu)造函數(shù)。這會(huì)輸出“Four”。然后,運(yùn)行Dog 的構(gòu)造函數(shù)。Dog 的構(gòu)造函數(shù)調(diào)用Mammal 中的ears 方法,因此輸出“Two”。最后,Dog 的構(gòu)造函數(shù)完成,輸出“Three”。 目標(biāo)三 創(chuàng)建類(lèi)實(shí)例編寫(xiě)創(chuàng)建任何具體類(lèi)實(shí)例的代碼,包括正常的高層次類(lèi),內(nèi)部類(lèi),靜態(tài)內(nèi)部類(lèi)和匿名內(nèi)部類(lèi)。 目標(biāo)的注釋這份材料的一些內(nèi)容在別的地方提到過(guò),特別是目標(biāo)4.1 中。 實(shí)例化類(lèi)具體類(lèi)是指能夠被實(shí)例化為對(duì)象引用(也簡(jiǎn)稱為對(duì)象)的類(lèi)。因此,抽象類(lèi)是不能被實(shí)例化的,所以不能創(chuàng)建對(duì)象引用。記住,包含任何抽象方法的類(lèi)本身也是抽象的,并且不能被實(shí)例化。實(shí)例化類(lèi)的關(guān)鍵是使用關(guān)鍵字new。典型地,如下所示 Button b = new Button (); 這個(gè)語(yǔ)法意為變量b 是Button 類(lèi)型的,并且包含指向Button 實(shí)例的引用。但是,盡管引用的類(lèi)型經(jīng)常與被實(shí)例化的類(lèi)的類(lèi)型是一樣的,但這不是必要的。因此,如下代碼也是合法的 Object b = new Button (); 這個(gè)語(yǔ)法指出b 引用的類(lèi)型是Object 而不是Button。 聲明和實(shí)例化不是必須出現(xiàn)在同一行上。可以這樣創(chuàng)建一個(gè)類(lèi)的實(shí)例。 Button b; b = new Button (); 內(nèi)部類(lèi)是隨著JDK1.1 的發(fā)布而加入的。它們?cè)试S一個(gè)類(lèi)在另一個(gè)類(lèi)中定義。 內(nèi)部類(lèi)內(nèi)部類(lèi)是隨著JDK1.1 的發(fā)布而引入的。它們?cè)试S類(lèi)被定義在其他類(lèi)中,有時(shí)候被稱作嵌套類(lèi)。它們被廣闊的使用在新的1.1 事件處理模型中。你肯定會(huì)在考試中遇到嵌套類(lèi)范圍的問(wèn)題。 這是一個(gè)簡(jiǎn)單的例子 class Nest {class NetIn {} } 這段代碼編譯后的輸出是兩個(gè)class 文件。如你所想的,第一個(gè)是Nest.class,另一個(gè)是Nest$NestIn.class。這說(shuō)明了嵌套類(lèi)通常只是個(gè)命名規(guī)范,而不是一種新的類(lèi)文件。內(nèi)部類(lèi)允許你邏輯性地組織類(lèi)。它們?cè)谀阆ML問(wèn)變量時(shí)也有廣泛的好處。 嵌套高層類(lèi)嵌套高層類(lèi)是一個(gè)包容高層類(lèi)的靜態(tài)成員。 這樣,修改之前的簡(jiǎn)單例子 class Nest {static class NestIn {} } 這種類(lèi)型的嵌套經(jīng)常用來(lái)簡(jiǎn)單的組合相關(guān)的類(lèi)。因?yàn)轭?lèi)是靜態(tài)的,它不需要在外部類(lèi)實(shí)例存在的情況下才能實(shí)例化內(nèi)部類(lèi)。 成員類(lèi)我認(rèn)為成員類(lèi)是“普通內(nèi)部類(lèi)”。成員類(lèi)類(lèi)似于類(lèi)的其他成員,你必須在創(chuàng)建內(nèi)部類(lèi)的實(shí)例之前首先實(shí)例化外部類(lèi)。由于需要結(jié)合外部類(lèi)的實(shí)例,Sun 引入了新的語(yǔ)法允許在創(chuàng)建內(nèi)部類(lèi)的同時(shí)創(chuàng)建外部類(lèi)的實(shí)例。這形成如下形式 Outer.Inner i = new Outer ().new Inner (); 為了弄清楚為此提供的新語(yǔ)法的意思,設(shè)法認(rèn)為在上面例子中使用的關(guān)鍵字new 屬于 this 當(dāng)前存在的實(shí)例中,因此,你可以將創(chuàng)建實(shí)例的代碼修改為 Inner i = this.new Inner (); 因?yàn)槌蓡T類(lèi)無(wú)法脫離外部類(lèi)的實(shí)例存在,它可以訪問(wèn)外部類(lèi)中的變量。 創(chuàng)建在方法中的類(lèi)這種類(lèi)更正確的叫法應(yīng)該是局部類(lèi),但是把它們當(dāng)作創(chuàng)建在方法中,有助于讓你知道最有可能在什么地方遇到它們。局部類(lèi)只在它的代碼塊或方法中可見(jiàn)。在局部類(lèi)定義中的代碼只能使用包容塊中的final局部變量或方法的參數(shù)。你很有可能在考試中遇到這樣的題目。 匿名類(lèi)你對(duì)于匿名內(nèi)部類(lèi)的第一反應(yīng)可能是“你為什么要這么做,而且如果它沒(méi)有名字,你怎么能引用它呢?”要回答這些問(wèn)題,請(qǐng)考慮下面的情形。你可能會(huì)遇到不停的為類(lèi)實(shí)例捏造自我描述的名字的情況。這樣,對(duì)于事件處理,兩件需要了解的重要事情是等待處理的事件和處理器附屬的模塊的名字。為事件處理器實(shí)例取名字不會(huì)有多大價(jià)值。至于如果類(lèi)沒(méi)有名字,如何引用該類(lèi)的問(wèn)題,你是做不到,而如果你需要通過(guò)名字來(lái)引用它,就不應(yīng)該創(chuàng)建匿名類(lèi)。缺乏名字有另一個(gè)副作用,就是你不能為它設(shè)定任何構(gòu)造函數(shù)。這是一個(gè)創(chuàng)建匿名內(nèi)部類(lèi)的例子 class Nest {public static void main (String argv []) {Nest n = new Nest ();n.mymethod (new anon () {});}public void mymethod (anon i) {} } class anon {} 請(qǐng)注意匿名內(nèi)部類(lèi)是如何在mymethod 的調(diào)用的圓括號(hào)中同時(shí)聲明和定義的。 課后測(cè)試題 問(wèn)題 1)下面哪些論述是正確的? 1)A class defined within a method can only access static methods of the enclosing method 2)A class defined within a method can only access final variables of the enclosing method 3)A class defined with a method cannot access any of the fields within the enclosing method 4)A class defined within a method can access any fields accessible by the enclosing method 問(wèn)題 2)下面哪些論述是正確的? 1) An anonymous class cannot have any constructors 2) An anonymous class can only be created within the body of a method 3) An anonymous class can only access static fields of the enclosing class 4) The class type of an anonymous class can be retrieved using the getName method 問(wèn)題 3)下面哪些論述是正確的? 1) Inner classes cannot be marked private 2) An instance of a top level nested class can be created without an instance of its enclosing class 3) A file containing an outer and an inner class will only produce one .class output file 4) To create an instance of an member class an instance of its enclosing class is required. 答案 答案 1) 2)A class defined within a method can only access final variables of the enclosing method 這種類(lèi)可以訪問(wèn)傳遞給包容方法的參數(shù) 答案 2) 1) An anonymous class cannot have any constructors 答案 3) 2) An instance of a top level nested class can be created without an instance of its enclosing class 4)To create an instance of a member class an instance of its enclosing class is required. 內(nèi)部類(lèi)會(huì)被放在它自己的.class 輸出文件中,使用格式Outer$Inner.class. 高層次嵌套類(lèi)是一個(gè)靜態(tài)類(lèi),因而不需要包容類(lèi)的實(shí)例。成員類(lèi)是普通的非靜態(tài)類(lèi),因而需要有一個(gè)包容類(lèi)的實(shí)例。 第7 章 線程 目標(biāo)一 實(shí)例化和啟動(dòng)線程通過(guò)使用java.lang.Thread 和 java.lang.Runnable 在代碼中定義,實(shí)例化,和啟動(dòng)新線程。 什么是線程?線程是表面上看似和主程序并行運(yùn)行的輕量級(jí)進(jìn)程。與進(jìn)程不同的是它與程序的其他部分共享存儲(chǔ)空間和數(shù)據(jù)。在這里線程的英文單詞thread 實(shí)際上是“thread of execution” 的縮寫(xiě),you might like to imagine a rope from which you have frayed the end and taken one thread.它依然是主線程的一部分,但它可以獨(dú)立出來(lái),自己完成操作。這里請(qǐng)注意,啟動(dòng)一個(gè)多線程的程序和僅僅啟動(dòng)一個(gè)程序的多個(gè)同一程序是有區(qū)別的,因?yàn)橐粋€(gè)多線程的程序?qū)?huì)對(duì)統(tǒng)一程序內(nèi)的數(shù)據(jù)進(jìn)行讀取和存儲(chǔ)。一個(gè)可以顯示多線程用處的例子就是打印,當(dāng)你按下打印按鈕的時(shí)候,你肯定不希望主程序直到打印完成才開(kāi)始響應(yīng)。最棒的就是你可以讓打印進(jìn)程“在后臺(tái)”悄悄的運(yùn)行,同時(shí)你可以使用主程序的其他部分。而且當(dāng)打印線程出現(xiàn)故障的時(shí)候主程序可以對(duì)此做出響應(yīng),一個(gè)講解多線程最佳的通用例子就是創(chuàng)建一個(gè)每當(dāng)你按下按鈕的時(shí)候彈出一個(gè)彈球的圖形用戶界面程序。因?yàn)楝F(xiàn)在處理器速度快的原因,導(dǎo)致表面上看每個(gè)線程似乎是獨(dú)享CPU,這是由于處理器在各個(gè)線程之間的切換速度很快,控制彈球跳動(dòng)的代碼更像是在處理器上運(yùn)行唯一一個(gè)程序。不像大部分程序那樣,線程并不是嵌入到Java 語(yǔ)言的最核心部分,它的大部分,依然是繼承自最原始的類(lèi)——Object,舊一點(diǎn)的語(yǔ)言如C/C++并沒(méi)有為線程設(shè)定標(biāo)準(zhǔn)。當(dāng)你在為Java 程序員認(rèn)證考試學(xué)習(xí)的時(shí)候,你必須要對(duì)當(dāng)一個(gè)程序啟動(dòng)一個(gè)新線程有一定認(rèn)識(shí),這個(gè)時(shí)候程序不再是在單一的路徑上執(zhí)行。因?yàn)楫?dāng)一個(gè)線程A 先于線程B 執(zhí)行,并不意味著線程A 一定會(huì)比線程B 先結(jié)束,當(dāng)然線程B 也不一定是在線程A 結(jié)束后才開(kāi)始運(yùn)行。因此你有可能會(huì)遇到這樣的問(wèn)題,如“最有可能輸出以下哪段代碼?”,最準(zhǔn)確的輸出結(jié)果決定于底層的操作系統(tǒng)和同一時(shí)間正在一起運(yùn)行的其它程序。正是因?yàn)橐粋€(gè)多線程程序在你的機(jī)器操作系統(tǒng)的組合上產(chǎn)生一個(gè)特定的輸出,因此它不能保證在其他不同的系統(tǒng)上也能有同樣的輸出結(jié)果。出考題的人會(huì)憑空假設(shè)程序在一個(gè)更加通用的平臺(tái)上運(yùn)行的(如Windows),考題可以考察底層操作系統(tǒng)對(duì)Java 線程的影響。不要只把注意力放在考試的關(guān)于線程的考點(diǎn)上,因?yàn)樗鼉H僅是考察你對(duì)一小部分線程知識(shí)是否掌握的很牢固。有很多線程相關(guān)的概念考試并沒(méi)有覆蓋到,如果你僅僅是為考試做打算,那么你可以對(duì)線程組,線程池,線程優(yōu)先級(jí)等概念不做了解。當(dāng)然,對(duì)這些概念的了解對(duì)更深層次領(lǐng)會(huì)Java 語(yǔ)言編程是有好處的,如果你只想把精力集中在應(yīng)付考試的考點(diǎn)上,那么你只需要對(duì)該指南上列出考點(diǎn)進(jìn)行學(xué)習(xí)就行了。 兩種創(chuàng)建線程的方式在這兩種方法中,使用Runnable 似乎更常見(jiàn)一些,但是出于考試的原因,你必須對(duì)這兩種方法都了解。下面這種方法就是讓對(duì)象在創(chuàng)建的時(shí)候,用實(shí)現(xiàn)Runnable 接口的方法來(lái)實(shí)現(xiàn)創(chuàng)建線程。 class MyClass implements Runnable{public void run(){//Blank Body} } 創(chuàng)建兩個(gè)線程并運(yùn)行它們。 MyClass mc = new MyClass(); MyClass mc2 = new MyClass(); Thread t = new Thread(mc); Thread t2 = new Thread(mc2); t.start(); t2.start();這里需要注意的是,線程 t 并不肯定是比線程 t2 先結(jié)束運(yùn)行,當(dāng)然由于run()方法里面并沒(méi)有任何代碼,因此線程 t 很有可能比線程 t2 先結(jié)束運(yùn)行。即使你讓該段代碼在你的機(jī)器上運(yùn)行上千次,無(wú)法改變它最終的運(yùn)行結(jié)果,當(dāng)然無(wú)法保證在其他的系統(tǒng)上運(yùn)行結(jié)果也是一樣的,或者當(dāng)你更改系統(tǒng)的環(huán)境配置時(shí),也有可能發(fā)生變化。注意到在用實(shí)現(xiàn)Runnble 接口的方法來(lái)創(chuàng)建線程時(shí),必須要求創(chuàng)建一個(gè)Thread 對(duì)象的實(shí)例,并且必須要在創(chuàng)建的時(shí)候,把實(shí)現(xiàn)該Runnable 接口的對(duì)象作為構(gòu)造方法的參數(shù)傳遞進(jìn)去。任何一個(gè)類(lèi)在它實(shí)現(xiàn)一個(gè)接口的時(shí)候,它必須同時(shí)要?jiǎng)?chuàng)建該接口中已經(jīng)定義的方法。該方法不一定非要有任何意義,比如,方法里面的內(nèi)容可以為空,但是它必須在這個(gè)類(lèi)的內(nèi)部出現(xiàn)。因此在上面那個(gè)例子里出現(xiàn)了空的run()方法,不包含run()將會(huì)導(dǎo)致程序在編譯期報(bào)錯(cuò)。當(dāng)你需要在一個(gè)類(lèi)里面創(chuàng)建一個(gè)有某種用途的線程的時(shí)候,你需要在我上面的 //Blank Body 部分寫(xiě)一些東西進(jìn)去。另外一種創(chuàng)建線程的方法就是直接使類(lèi)繼承自Thread。這樣做非常簡(jiǎn)單,但同時(shí)你也無(wú)法再繼承其他的對(duì)象,因?yàn)镴ava 只支持單繼承。因此當(dāng)你創(chuàng)建一個(gè)Button 對(duì)象的時(shí)候你無(wú)法使用這種方法來(lái)添加線程的功能,因?yàn)樗呀?jīng)是繼承了AWT Button 這個(gè)類(lèi)的,這使你不得不在繼承方面動(dòng)一點(diǎn)腦筋。不過(guò)一些反對(duì)的聲音認(rèn)為這種創(chuàng)建線程的方法更符合面向?qū)ο蟮乃枷搿5还茉趺凑f(shuō),你必須為了Java 考試對(duì)這種方法有一定了解。 實(shí)例化和啟動(dòng)一個(gè)線程盡管在線程中運(yùn)行的方法是run(),你并不需要調(diào)用這個(gè)方法來(lái)啟動(dòng)一個(gè)線程,而是調(diào)用start() 方法來(lái)啟動(dòng)一個(gè)線程。這點(diǎn)很關(guān)鍵,因?yàn)樗鼧O有可能在考試中出現(xiàn)。這點(diǎn)很有可能讓你栽跟頭,因?yàn)檫@和大多數(shù)往常你所遇見(jiàn)的Java 編程的慣例不一樣。通常如果你會(huì)把一段代碼放到一個(gè)方法里面去,當(dāng)你需要執(zhí)行它的時(shí)候,你只需要調(diào)用該方法。如果你直接調(diào)用run()方法,當(dāng)然這也不會(huì)造成什么錯(cuò)誤,只是它會(huì)象一個(gè)普通的方法那樣運(yùn)行,而不是作為線程的一部分來(lái)執(zhí)行。Runnable 接口并不包括start()方法,同樣也不包含其它一些線程中有用的方法(如sleep(),suspend()等等),你只需將你已經(jīng)實(shí)現(xiàn)了Runnable 接口的對(duì)象作為一個(gè)構(gòu)造方法參數(shù)傳遞給一個(gè)已經(jīng)實(shí)例化的Thread 對(duì)象。當(dāng)你需要一個(gè)已經(jīng)實(shí)現(xiàn)了Runnable 接口的對(duì)象執(zhí)行多線程任務(wù)的時(shí)候,你需要使用以下的代碼。 MyClass mc = new MyClass(); Thread t = new Thread(mc); t.start(); 盡管是run 方法在運(yùn)行, 但一個(gè)線程的啟動(dòng)是調(diào)用start方法。再一次強(qiáng)調(diào)你不是調(diào)用run()方法,而是start()方法來(lái)啟動(dòng)一個(gè)線程,盡管在run()方法里面的代碼才是線程執(zhí)行的時(shí)候所運(yùn)行的。如果你的對(duì)象是繼承Thread,你可以簡(jiǎn)單的調(diào)用start()方法來(lái)啟動(dòng)它。缺點(diǎn)就是Thread的子類(lèi)因?yàn)閱卫^承的原因再無(wú)法繼承其它功能的類(lèi)。 練習(xí)題 習(xí)題1)當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class Runt implements Runnable{public static void main(String argv[]){Runt r = new Runt();Thread t = new Thread(r);t.start();}public void start(){for(int i=0;i<100;i++)System.out.println(i);} } 1) Compilation and output of count from 0 to 99 2) Compilation and no output 3) Compile time error: class Runt is an abstract class. It can't 習(xí)題2)下列哪一項(xiàng)表述是正確的? Which of the following statements are true? 1) Directly sub classing Thread gives you access to more functionality of the Java threading capability than using the Runnable interface 2) Using the Runnable interface means you do not have to create an instance of the Thread class and can call run directly 3) Both using the Runnable interface and subclassing of Thread require calling start to begin execution of a Thread 4) The Runnable interface requires only one method to be implemented, this is called run 習(xí)題3)當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class Runt extends Thread{public static void main(String argv[]){Runt r = new Runt();r.run();}public void run(){for(int i=0;i<100;i++)System.out.println(i);} } 1) Compilation and output of count from 0 to 99 2) Compilation and no output 3) Compile time error: class Runt is an abstract class. It can't be instantiated. 4) Compile time error, method start has not been defined 習(xí)題4)下列哪一項(xiàng)表述是正確的? 1)To implement threading in a program you must import the class java.io.Thread 2) The code that actually runs when you start a thread is placed in the run method 3) Threads may share data between one another 4) To start a Thread executing you call the start method and not the run method 習(xí)題5) 下列哪一項(xiàng)是讓線程開(kāi)始運(yùn)行的正確代碼? 1) public class TStart extends Thread{public static void main(String argv[]){TStart ts = new TStart();ts.start();}public void run(){System.out.println("Thread starting");} } 2) public class TStart extends Runnable{public static void main(String argv[]){TStart ts = new TStart();ts.start();}public void run(){System.out.println("Thread starting");} } 3) public class TStart extends Thread{public static void main(String argv[]){TStart ts = new TStart();ts.start();}public void start(){System.out.println("Thread starting");} } 4) public class TStart extends Thread{public static void main(String argv[]){TStart ts = new TStart();ts.run();}public void run(){System.out.println("Thread starting");} } 答案 答案 1) 3) Compile time error: class Runt is an abstract class.它不能被實(shí)例化. 這個(gè)類(lèi)實(shí)現(xiàn)了Runnable 接口,但是沒(méi)有定義run()方法。 答案 2) 3)不管是繼承Thread 對(duì)象還是實(shí)現(xiàn)Runnable 接口,都要使用start()方法來(lái)讓該線程開(kāi)始運(yùn)行。 4) 實(shí)現(xiàn)Runnable 接口只需要定義一個(gè)run()的方法。 答案 3) 1) 編譯輸出從0-99。 盡管如此,注意到這段代碼并沒(méi)有讓線程運(yùn)行,run()方法不應(yīng)該這樣被調(diào)用。 答案 4) 2)當(dāng)你讓線程跑起來(lái)的時(shí)候運(yùn)行的實(shí)際上是run()方法里面的代碼。 3) 線程之間可以彼此共享數(shù)據(jù)信息。 4) 當(dāng)你需要一個(gè)線程開(kāi)始運(yùn)行的時(shí)候調(diào)用的是start()方法而不是run()方法。你不需要導(dǎo)入額外的類(lèi),因?yàn)榫€程是Java 語(yǔ)言的一部分。 答案 5) 1) 僅選項(xiàng)1 是一個(gè)有效的方式開(kāi)始一個(gè)新的線程執(zhí)行。 選項(xiàng)2 的代碼繼承Runnable 接口但沒(méi)意義,因?yàn)镽unnable 是接口不是類(lèi),接口使用implements 關(guān)鍵字。 選項(xiàng)3 的代碼直接地調(diào)用起動(dòng)方法。 如果您運(yùn)行這個(gè)代碼您將發(fā)現(xiàn)文本輸出,但由于直接調(diào)用方法,并不是因?yàn)橐粋€(gè)新的線程在運(yùn)行。 選項(xiàng)4 也一樣,直接地調(diào)用運(yùn)行線程僅是另一個(gè)方法,并且象其他的一樣執(zhí)行。 目標(biāo)二 何時(shí)線程會(huì)被阻止運(yùn)行 對(duì)什么情況下線程會(huì)被阻止運(yùn)行有一定認(rèn)識(shí)。 關(guān)于該目標(biāo)的解釋“線程會(huì)被阻止運(yùn)行”的表述看上去很籠統(tǒng),它的意思是該線程已經(jīng)被人為的暫停?還是這個(gè)線程已經(jīng)被徹底銷(xiāo)毀?其實(shí)“線程會(huì)被阻止運(yùn)行”的意思是線程被阻塞了。 可能造成線程阻塞的原因 線程阻塞的原因可能是 1) 線程已經(jīng)被設(shè)置了一定長(zhǎng)度的睡眠時(shí)間。 2) 調(diào)用了suspend()方法,它將一直保持阻塞直到resume()方法被調(diào)用。 3) 該線程因?yàn)楸徽{(diào)用了wait()方法被暫停了,當(dāng)收到notify 或者notifyAll 消息的時(shí)候該線程會(huì)重新被激活。 出于對(duì)付考試的原因,sleep(),notify 和notifyAll()是這些造成線程組塞的原因非常需要掌握的。sleep()方法是一個(gè)靜態(tài)的可以暫停線程一定毫秒時(shí)間長(zhǎng)度的方法。還有一個(gè)版本可以支持設(shè)定睡眠的時(shí)間單位為十億分之一秒的版本。我認(rèn)為沒(méi)有多少人會(huì)在有如此精確的機(jī)器或者實(shí)現(xiàn)Java 的平臺(tái)上進(jìn)行工作。下面是一個(gè)展示線程如何進(jìn)入睡眠狀態(tài)的例子,注意這個(gè)sleep()方法是如何拋出InterruptedException 異常的。 public class TSleep extends Thread{public static void main(String argv[]){TSleep t = new TSleep();t.start();}public void run(){try{while(true){this.sleep(1000);System.out.println("looping while");}}catch(InterruptedException ie){}} }當(dāng)Java2 版本發(fā)布的時(shí)候,Thread 類(lèi)里面的stop(),suspend()和resume()方法已經(jīng)被認(rèn)為是過(guò)時(shí)的了(不提倡繼續(xù)再使用,并且在編譯期會(huì)報(bào)出警告提示)。同時(shí)JDK 文檔認(rèn)為//Quote這種方式因?yàn)樗逃械脑斐伤梨i可能的原因也不再提倡使用了。當(dāng)目標(biāo)線程正在所鎖定保護(hù)系統(tǒng)的臨界資源的監(jiān)視器時(shí)候因?yàn)楸粫和63肿枞麪顟B(tài),其他線程將不能再讀寫(xiě)該臨界資源直到該目標(biāo)線程解除死鎖狀態(tài)。如果一個(gè)線程解除該目標(biāo)線程的組塞,而同時(shí)又試圖在調(diào)用resume()之前保持該監(jiān)視器的鎖定狀態(tài),那么將會(huì)造成一個(gè)死鎖。這樣的死鎖具有代表性,就像”frozen”進(jìn)程一樣。需要更多的信息請(qǐng)參考為什么Thread.stop, Thread.suspend 和Thread.resume 不提倡再被繼續(xù)使用的原因。//End Quote線程的通過(guò)wait/notify 的協(xié)議來(lái)進(jìn)行阻塞操作將在下一個(gè)目標(biāo)中進(jìn)行表述。 使用Thread 包中的yield 方法由于Java 線程對(duì)平臺(tái)依賴的本質(zhì),你不能保證一個(gè)線程會(huì)把對(duì)CPU 資源的使用權(quán)移交給另一個(gè)線程。某些操作系統(tǒng)的線程調(diào)度規(guī)則會(huì)自動(dòng)給不同的線程分配CPU 的占有時(shí)間。而另一些操作系統(tǒng)則僅僅是讓線程獨(dú)享處理器資源。因?yàn)樯鲜鲈?#xff0c;Java 的Thread 包里面構(gòu)造了一個(gè)靜態(tài)的名叫yield()的方法可以讓當(dāng)前正在運(yùn)行狀態(tài)的線程讓出正在占用的CPU周期。該進(jìn)程則返回“準(zhǔn)備運(yùn)行”狀態(tài),這樣線程規(guī)劃系統(tǒng)可以有機(jī)會(huì)讓其他線程進(jìn)來(lái)調(diào)用CPU 資源運(yùn)行。如果沒(méi)有其他的線程在“準(zhǔn)備運(yùn)行”運(yùn)行狀態(tài),則剛剛讓出CPU 資源的線程馬上重新恢復(fù)到運(yùn)行狀態(tài)。 限制/搶占每一個(gè)線程都有一個(gè)設(shè)定好的CPU 占用周期來(lái)運(yùn)行。一旦它用完了設(shè)定好的一個(gè)CPU占用周期時(shí)間,那么它將停止占用CPU 資源以讓其他正在等待中的線程獲得機(jī)會(huì)運(yùn)行。當(dāng)一個(gè)線程進(jìn)入之前設(shè)定好的CPU 占用時(shí)間那么它的一個(gè)新的運(yùn)行周期就又開(kāi)始了。這種機(jī)制的好處就在于你可以讓所有的線程都跑起來(lái)而花費(fèi)最少的時(shí)間。 沒(méi)有時(shí)間 限制/共享優(yōu)先級(jí)系統(tǒng)將會(huì)決定哪個(gè)線程將會(huì)運(yùn)行。一個(gè)相對(duì)來(lái)說(shuō)最高優(yōu)先級(jí)的線程將會(huì)獲得時(shí)間來(lái)運(yùn)行。一段運(yùn)行在該系統(tǒng)中的程序必須使自己能夠自動(dòng)地讓每個(gè)線程讓出CPU 資源的占用,讓所有線程共享CPU 資源。 Java 線程的優(yōu)先級(jí)Java 考試并不認(rèn)為你需要對(duì)系統(tǒng)如何設(shè)置線程的優(yōu)先級(jí)。盡管知道這些機(jī)制是非常有的。同時(shí)這樣的局限性讓你意識(shí)到Thread 包中的yield()方法的重要性是非常有用的。你可以通過(guò)Thread 包中Thread.setPriority 來(lái)設(shè)置線程的優(yōu)先級(jí),你可以通過(guò)getPriority 來(lái)獲得線程的優(yōu)先級(jí),一個(gè)新建線程的默認(rèn)優(yōu)先級(jí)是Thread.NORM_PRIORITY。 練習(xí)題 習(xí)題1)當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class TGo implements Runnable{public static void main(String argv[]){TGo tg = new TGo();Thread t = new Thread(tg);t.start();}public void run(){while(true){Thread.currentThread().sleep(1000);System.out.println("looping while");}} } 1) Compilation and no output 2) Compilation and repeated output of "looping while" 3) Compilation and single output of "looping while" 4) Compile time error 習(xí)題2)下面哪種方式是推薦的讓線程阻塞的方式? 1) sleep() 2) wait/notify 3) suspend 4) pause 習(xí)題3)下列哪一項(xiàng)表述是正確的? 1) The sleep method takes parameters of the Thread and the number of seconds it should sleep 2) The sleep method takes a single parameter that indicates the number of seconds it should sleep 3) The sleep method takes a single parameter that indicates the number of milliseconds it should sleep 4) The sleep method is a static member of the Thread class 習(xí)題4)下列哪一項(xiàng)表述是正確的? Which of the following statements are true? 1) A higher priority Thread will prevent a lower priorty Thread from getting any access to the CPU. 2) The yield method only allows any higher priority priority thread to execute. 3) The Thread class has a static method called yield 4) Calling yield with an integer parameter causes it to yield for a specific time 練習(xí)題答案 答案1) Compile time error sleep()方法將會(huì)拋出InterruptedException 異常。除非讓這個(gè)代碼段放到try/catch 塊里面去,否則這段代碼將無(wú)法編譯。 答案2) 1) sleep() 2) wait/notify Java2 版本里面suspend()方法已不推薦再繼續(xù)使用。 答案3) 3) sleep()方法只需要一個(gè)表示線程睡眠時(shí)間長(zhǎng)度的參數(shù)。 4) sleep()方法是Thread 類(lèi)里面的一個(gè)靜態(tài)方法。 答案4) 線程類(lèi)有一個(gè)靜態(tài)方法yield,調(diào)用它可以允許任何等待的線程按照底層操作系統(tǒng)的計(jì)劃安排執(zhí)行.沒(méi)有帶一個(gè)整數(shù)型參數(shù)的yield 方法.是否高優(yōu)先級(jí)的線程比低優(yōu)先級(jí)能獲得更多的CPU 時(shí)間與平臺(tái)有關(guān),并不確定。 目標(biāo)三 何時(shí)線程會(huì)被阻止運(yùn)行編寫(xiě)代碼的時(shí)候使用同步的wait ,notify 和 notifyAll 方法,以防止并行讀取問(wèn)題的發(fā)生,同時(shí)保證各個(gè)線程之間的正常通信。當(dāng)執(zhí)行同步的wait,notify 和notifyAll 方法的時(shí)候?qū)€程和線程之間以及線程和對(duì)象鎖之間的內(nèi)部交互進(jìn)行定義。 為什么你需要wait/notify 法則?一個(gè)更容易理解的方式,比如你想象一下數(shù)據(jù)庫(kù)中的一條整型的變量數(shù)據(jù),如果你沒(méi)有一些鎖定數(shù)據(jù)的措施的話,你將會(huì)面臨數(shù)據(jù)污染的危險(xiǎn)。這樣一個(gè)用戶可以將這條數(shù)據(jù)取出來(lái),經(jīng)過(guò)一定運(yùn)算后再放回去。期間如果其他的用戶也將該數(shù)據(jù)取出來(lái)進(jìn)行運(yùn)算后返回,那么第一個(gè)用戶運(yùn)算后返回的數(shù)據(jù)將會(huì)失效。就像數(shù)據(jù)庫(kù)在任何事先不可知的情況下要處理更新一樣,所以一個(gè)多線程程序必須要有應(yīng)付這種可能性的機(jī)制。為了考試,你十分有必要對(duì)本目標(biāo)的內(nèi)容進(jìn)行研究,一些十分有經(jīng)驗(yàn)的Java 程序員wait/notify 法則也并不是十分了解,這是一個(gè)普遍現(xiàn)象,強(qiáng)烈建議讀者寫(xiě)一些簡(jiǎn)單的程序來(lái)熟悉這個(gè)法則,并對(duì)后面的模仿考試的練習(xí)題進(jìn)行針對(duì)性的練習(xí)。 一個(gè)銀行/帳戶 的例子下面的代碼講解了同步的線程之間對(duì)同一個(gè)數(shù)據(jù)進(jìn)行操作。它一個(gè)名叫bank 的類(lèi),它主要用來(lái)驅(qū)動(dòng)多個(gè)運(yùn)行著B(niǎo)usiness 類(lèi)中的數(shù)據(jù)處理方法的線程。Bussiness 線程實(shí)際上就是對(duì) Account 里面的金額進(jìn)行加減操作。下面代碼的思想展示了多線程是如何“踩到對(duì)方的腳”并造成數(shù)據(jù)污染的,但是是有代碼可以阻止這類(lèi)事情發(fā)生的。為了“修復(fù)”已經(jīng)存在的這個(gè)數(shù)據(jù)污染我調(diào)用了sleep()方法,你可以認(rèn)為是等同于暫停,當(dāng)bank 中有代碼寫(xiě)入操作數(shù)據(jù)庫(kù)的時(shí)候。如果沒(méi)有調(diào)用這個(gè)sleep()方法,數(shù)據(jù)污染發(fā)生的可能性就依然存在。你不得不運(yùn)行很多次程序,這樣才能讓這個(gè)毛病顯現(xiàn)出來(lái)。 public class Account{private int iBalance;public void add(int i){iBalance = iBalance + i;System.out.println("adding " +i +" Balance = "+ iBalance);}public void withdraw(int i){if((iBalance - i) >0 ){try{Thread.sleep(60);}catch(InterruptedException ie){}iBalance = iBalance - i;}else{System.out.println("Cannot withdraw, funds would be < 0");}if(iBalance < 0){System.out.println("Woops, funds below 0");System.exit(0);}System.out.println("withdrawing " + i+ " Balance = " +iBalance);}public int getBalance(){return iBalance;} } 關(guān)鍵字synchronized關(guān)鍵字synchronized 可以用在標(biāo)記一段聲明或者鎖定一段代碼,保證在同一時(shí)間只有一個(gè)線程能夠運(yùn)行它的一個(gè)實(shí)例。進(jìn)入這段代碼將會(huì)受到負(fù)責(zé)監(jiān)視它的監(jiān)視器的保護(hù)。這個(gè)過(guò)程是由一個(gè)鎖定系統(tǒng)實(shí)現(xiàn)的。你也可以看到用監(jiān)控,或者使用互斥來(lái)形容(互不相關(guān))。一個(gè)鎖分配給一個(gè)對(duì)象以保證同一時(shí)間只能有一個(gè)線程的進(jìn)入,因此當(dāng)一個(gè)線程試圖進(jìn)入的時(shí)候必須試圖獲得這個(gè)鎖的許可。其它的線程將無(wú)法進(jìn)入這段代碼,知道第一個(gè)進(jìn)入的線程完成然后釋放這個(gè)鎖。請(qǐng)注意的是這里的鎖是基于對(duì)象而不是基于方法的。 關(guān)鍵字synchronized 放在方法的名字之前,如: synchronized void amethod() { /* method body */} 關(guān)鍵字synchronized 也可放在代碼段的括號(hào)之前,如: synchronized (ObjectReference) { /* Block body */ } 注釋的部分是指對(duì)象或者類(lèi)的里面某段需要監(jiān)視器需要鎖定的部分。大部分情況下我們使用的是前者,而不是后者。 當(dāng)一個(gè)被關(guān)鍵字synchronized 標(biāo)記的代碼開(kāi)始執(zhí)行以后,擁有它的對(duì)象將保持鎖定狀 態(tài),它將不能被調(diào)用直到鎖定狀態(tài)被解除。 synchronized void first(); synchronized void second(); 有更好的辦法比在一個(gè)代碼塊前加上關(guān)鍵字synchronized能獲得串行化的好處,它必須用于聯(lián)接管理可串行化代碼鎖的代碼 。 wait/notify除了鎖可以獲得和釋放以外,每個(gè)對(duì)象都會(huì)暫停或者進(jìn)入等待當(dāng)其它的線程獲得這個(gè)鎖的時(shí)候。這使得線程之間溝通情況隨時(shí)運(yùn)行。由于Java 語(yǔ)言的單繼承性,每一個(gè)子類(lèi)都是繼承自最原始的Object 對(duì)象,從它那獲得這個(gè)線程級(jí)的通信能力。 wait 和notify 應(yīng)該放在Synchronized 關(guān)鍵字標(biāo)記的代碼中以保證當(dāng)前的代碼在監(jiān)視器的監(jiān)控之中。在一個(gè)標(biāo)記為synchronized 的代碼中調(diào)用wait()方法,會(huì)造成運(yùn)行這段代碼的這個(gè)線程交出鎖的權(quán)限并進(jìn)入睡眠狀態(tài)。這種情況通常是為了其他的線程來(lái)接管這個(gè)鎖以進(jìn)行下一步操作。如果沒(méi)有讓線程喚醒并重新進(jìn)入運(yùn)行狀態(tài)的notify()或者notifyAll()方法的話,那么 wait()方法也變得毫無(wú)意義。一個(gè)典型的使用wait()/notify()法則來(lái)讓線程之間進(jìn)行通信的例子,看上去它似乎陷入了死循環(huán)。 //producing code while(true){try{wait();}catch (InterruptedException e) {} } //some producing action goes here notifyAll(); 如果真的是這樣,那這段代碼真的是垃圾。當(dāng)你第一眼看到這段代碼的時(shí)候你會(huì)感覺(jué)它會(huì)一直這樣運(yùn)行下去。其實(shí)wait()會(huì)告訴它交出鎖讓其它線程運(yùn)行,直到你調(diào)用了notify 或者notifyAll 方法。線程調(diào)度不是獨(dú)立的,不能依靠虛擬機(jī)讓它用同一種方式 運(yùn)行。不象Java 的大部分特性,線程在不同的平臺(tái)上會(huì)有不同的表現(xiàn)。這兩點(diǎn)分別是線程的優(yōu)先級(jí)和線程的調(diào)度。線程調(diào)度的兩種途徑是:搶占和時(shí)間片在一個(gè)可以進(jìn)行搶占的系統(tǒng)上,程序可以通過(guò)搶占來(lái)獲得CPU 獨(dú)享周期。在一個(gè)實(shí)行時(shí)間片分配的系統(tǒng)上,每個(gè)線程都會(huì)獲得一個(gè)CPU 獨(dú)享周期,然后進(jìn)入準(zhǔn)備運(yùn)行狀態(tài)。這樣可以確保不會(huì)讓一個(gè)線程一直獨(dú)享CPU。缺點(diǎn)在于你無(wú)法預(yù)測(cè)這個(gè)線程會(huì)運(yùn)行多長(zhǎng)時(shí)間才會(huì)結(jié)束,也無(wú)法預(yù)測(cè)什么時(shí)候這個(gè)線程會(huì)再運(yùn)行,因此通常建議你使用notify 或者notifyAll方法。盡管Java 把線程的優(yōu)先級(jí)按1-10 從低到高來(lái)分配。一些平臺(tái)能夠識(shí)別這個(gè)優(yōu)先級(jí)的屬性,但其它的卻不能。notify 方法會(huì)喚醒一個(gè)線程讓它進(jìn)入重新要求獲得某對(duì)象的監(jiān)控權(quán)限。你不能確定哪個(gè)線程被喚醒了。如果你只是有一個(gè)線程被喚醒了當(dāng)然不會(huì)存在這種問(wèn)題。如果你有很多線程等待喚醒,那么等待最長(zhǎng)時(shí)間的那個(gè)將被喚醒。盡管如此,你依然不能確定,線程的優(yōu)先級(jí)對(duì)最后結(jié)果也有影響。因此一般推薦你使用notifyAll 而不是notify,不要對(duì)線程的優(yōu)先級(jí)和調(diào)度進(jìn)行任何假設(shè)。你可能要讓你的代碼在盡量多的平臺(tái)上運(yùn)行來(lái)測(cè)試一下,當(dāng)然,并不總是這樣的。 練習(xí)題 問(wèn)題 1)下列哪一個(gè)關(guān)鍵字表示該線程放棄了該對(duì)象的鎖? 1) release 2) wait 3) continue 4) notifyAll 問(wèn)題 2)下列哪一是關(guān)于關(guān)鍵字synchronized 的表述是最合適的? 1) Allows more than one Thread to access a method simultaneously 2) Allows more than one Thread to obtain the Object lock on a reference 3) Gives the notify/notifyAll keywords exclusive access to the monitor 4) Means only one thread at a time can access a method or block of code 問(wèn)題 3)當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class WaNot{int i=0;public static void main(String argv[]){WaNot w = new WaNot();w.amethod();}public void amethod(){while(true){try{wait();}catch (InterruptedException e) {}i++;}//End of while}//End of amethod }//End of class 1)Compile time error, no matching notify within the method 2)Compile and run but an infinite looping of the while method 3)Compilation and run 4)Runtime Exception "IllegalMonitorStatException" 問(wèn)題 4)你如何使用wait/notify 法則指定某個(gè)線程被喚醒? 1) Pass the object reference as a parameter to the notify method 2) Pass the method name as a parameter to the notify method 3) Use the notifyAll method and pass the object reference as a parameter 4) None of the above 問(wèn)題 5)下列哪項(xiàng)表述是正確的? 1) Java uses a time-slicing scheduling system for determining which Thread will execute 2) Java uses a pre-emptive, co-operative system for determining which Thread will execute 3) Java scheduling is platform dependent and may vary from one implementation to another 4) You can set the priority of a Thread in code 答案 答案 1) Wait 答案 2) 以為著在同一時(shí)間內(nèi)只有一個(gè)線程對(duì)該代碼段或方法進(jìn)行操作。 答案 3) Runtime Exception “IllegalMonitorStateException” wait/notify 法則只能在被標(biāo)記為synchronized 的代碼段里面使用,在該題這種情況下調(diào)用代碼會(huì)拋出異常。 答案 4) 4) None of the above. wait/notify 法則沒(méi)有為哪個(gè)線程將被激活提供方法。 答案 5) 3) Java 的調(diào)度平臺(tái)不是獨(dú)立的,它最終的實(shí)現(xiàn)結(jié)果是不確定的。 4) 你可以在代碼中為代碼設(shè)定優(yōu)先級(jí)。 第8 章 Java 的lang 包 目標(biāo)一 Math 類(lèi)中的方法在開(kāi)發(fā)過(guò)程中運(yùn)用java.lang.Math 中的如下方法:abs ,ceil ,floor ,max ,min ,random ,round ,sin, cos, tan, sqrt。 本節(jié)需要注意的問(wèn)題Math 類(lèi)是不可被繼承的,它里面的方法都是靜態(tài)的。這或許是好事,因?yàn)樗档土肆嘶靵y情況發(fā)生的可能性。在這一塊你幾乎肯定會(huì)遇到問(wèn)題,如果僅僅是因?yàn)槟愫鲆曀鼈兌斐慑e(cuò)誤的發(fā)生,那將非常遺憾。 abs因?yàn)槲冶∪醯臄?shù)學(xué)基礎(chǔ),一開(kāi)始我?guī)缀鯇?duì)如何使用abs()這個(gè)方法的用法一無(wú)所知,直到為了通過(guò)Java 程序員考試,我才開(kāi)始認(rèn)真學(xué)習(xí)弄懂它。它的作用是對(duì)一個(gè)數(shù)值進(jìn)行取絕對(duì)值操作。因此下面的那段代碼打印出來(lái)的數(shù)字是99。如果進(jìn)行操作的數(shù)是一個(gè)非負(fù)數(shù),它會(huì)原樣返回。 System.out.println(Math.abs(-99)); ceil這個(gè)方法返回的是比被操作數(shù)大的最小double 值。比如下面這個(gè)例子: ceil(1.1) 它將返回2.0 如果你換成 ceil(-1.1) 它將返回 -1.0; floor參考一下JDK 的說(shuō)明文檔,該方法返回的是:返回最大的(最接近正無(wú)窮大)double 值,該值小于或等于參數(shù),并且等于某個(gè)整數(shù)。如果覺(jué)得這表達(dá)的不夠清楚,那么我們可以看一下,下面那一小段代碼和它的輸出情況: public class MyMat{public static void main(String[] argv){System.out.println(Math.floor(-99.1));System.out.println(Math.floor(-99));System.out.println(Math.floor(99));System.out.println(Math.floor(-.01));System.out.println(Math.floor(0.1));} } 它的輸出是: -100.0 -99.0 99.0 -1.0 0.0 max 和 min注意一下這兩個(gè)方法需要兩個(gè)參數(shù)。你可能會(huì)有疑問(wèn),如果僅僅傳遞給它們一個(gè)參數(shù)會(huì)發(fā)生錯(cuò)誤。你可以把這兩個(gè)方法看成是:“這兩個(gè)數(shù)里面哪一個(gè)是最大的?”以下的代碼演示了這兩個(gè)方法是如何工作的: class MaxMin{public static void main(String argv[]){System.out.println(Math.max(-1,-10));System.out.println(Math.max(1,2));System.out.println(Math.min(1,1));System.out.println(Math.min(-1,-10));System.out.println(Math.min(1,2));} } 下面是輸出的結(jié)果: -1 2 1 -10 1 random該方法返回的是一個(gè)0.0 到1.0 之間的隨機(jī)數(shù)。不像一些隨機(jī)數(shù)系統(tǒng),Java 似乎并不支持提供種子數(shù)來(lái)增加隨機(jī)性。這個(gè)方法可以用下面的方法來(lái)生成0 到100 之間的隨機(jī)數(shù)。從考試的角度來(lái)說(shuō),這個(gè)“返回一個(gè)0.0 到1.0 之間的隨機(jī)數(shù)”的知識(shí)點(diǎn)是很重要的。因此下面的幾個(gè)數(shù)字是可能輸出結(jié)果: 0.9151633320773057 0.25135231957619386 0.10070205341831895經(jīng)常會(huì)遇到需要程序生成0 到10 之間或者0 到100 之間的隨機(jī)數(shù)的情況。下面這行代碼就是演示如何生成0 到100 之間的隨機(jī)數(shù): System.out.println(Math.round(Math.random()*100)); round返回最接近參數(shù)的一個(gè)整型數(shù)。如果小數(shù)部分大于0.5 則返回下一個(gè)相對(duì)最小整數(shù),如果小數(shù)部分小于等于0.5 則返回上一個(gè)相對(duì)最大整數(shù)。如下例所示: 2.0 <=x < 2.5. then Math.round(x)==2.0 2.5 <=x < 3.0 the Math.round(x)==3.0 以下是一些例子和它們的輸出: System.out.println(Math.round(1.01)); System.out.println(Math.round(-2.1)); System.out.println(Math.round(20)); 1 -2 20 sin cos tan這三個(gè)方便快捷的方法都只需要一個(gè) double 型的參數(shù),它們的功能和其他語(yǔ)言里面的方法功能是一樣的。在我12 年的編程工作中,我還從未使用過(guò)它們。可能需要記憶的僅僅是參數(shù)類(lèi)型是double 型的。 sqrt返回該參數(shù)的double 型平方根。 總結(jié) max 和min 方法需要兩個(gè)參數(shù)。 random 方法返回的數(shù)值在0 到1 之間。 abs 返回的是絕對(duì)值。 round 返回最接近參數(shù)的整型數(shù),但保留符號(hào)位。 練習(xí)題 習(xí)題 1) 下列哪個(gè)選項(xiàng)將會(huì)編譯正確? 1) System.out.println(Math.max(x)); 2) System.out.println(Math.random(10,3)); 3) System.out.println(Math.round(20)); 4) System.out.println(Math.sqrt(10)); 習(xí)題 2)下列哪個(gè)選項(xiàng)將會(huì)輸出1 到10 之間的隨機(jī)數(shù)? 1) System.out.println(Math.round(Math.random()* 10)); 2) System.out.println(Math.round(Math.random() % 10)); 3) System.out.println(Math.random() *10); 4) None of the above 習(xí)題 3)寫(xiě)面一行代碼將會(huì)輸出什么? System.out.println(Math.floor(-2.1)); 1) -2 2) 2.0 3) -3 4) -3.0 習(xí)題 4)寫(xiě)面一行代碼將會(huì)輸出什么? System.out.println(Math.abs(-2.1)); 1) -2.0 2) -2.1 3) 2.1 4) 1.0 習(xí)題 5)寫(xiě)面一行代碼將會(huì)輸出什么? System.out.println(Math.ceil(-2.1)); 1) -2.0 2) -2.1 3) 2.1 3) 1.0 習(xí)題 6)當(dāng)你試圖編譯下列代碼時(shí)將會(huì)發(fā)生什么? class MyCalc extends Math{public int random(){double iTemp;iTemp=super();return super.round(iTemp);} } public class MyRand{public static void main(String argv[]){MyCalc m = new MyCals();System.out.println(m.random());} } 1) Compile time error 2) Run time error 3) Output of a random number between 0 and 1 4) Output of a random number between 1 and 10 答案 答案1) 3) System.out.println(Math.round(20)); 4) System.out.println(Math.sqrt(10)); 選項(xiàng)1 錯(cuò)誤是因?yàn)閙ax 方法只需要一個(gè)參數(shù),而選項(xiàng)2 錯(cuò)誤是因?yàn)閞andom 方法只不需要參數(shù)。 答案2) 4) None of the above 最接近正確答案的是選項(xiàng)1,但是請(qǐng)別忘記一個(gè)細(xì)節(jié)就是,random 方法返回的數(shù)據(jù)中包括0,而題目問(wèn)的是1 到10 答案3) 4) -3.0 答案4) 3) 2.1 答案5) 1) -2.0 答案6) 1) Compile time error Math 類(lèi)是不可被繼承的。這段代碼有一些低級(jí)的錯(cuò)誤。你只能在構(gòu)造方法里面使用super,而它卻是在random 方法里面使用。 目標(biāo)二 Strings 的不變性描述string 對(duì)象不變性的重要性。String 類(lèi)的不變性理論說(shuō)明, string 對(duì)象一旦被創(chuàng)建,它就決不能被改變。Java 編程的一些經(jīng)歷意味著似乎并不如此。 如下面的代碼所示: public class ImString{public static void main(String argv[]){String s1 = new String("Hello");String s2 = new String("There");System.out.println(s1);s1=s2;System.out.println(s1);} } 如果Strings 不能被改變,那么s1 應(yīng)該仍然打印出Hello,但是你如果運(yùn)行這個(gè)程序段,你會(huì)發(fā)現(xiàn)第二次輸出的字符串是“There”,這是為什么呢?不變性實(shí)際上指的是字符串指針?biāo)赶虻膬?nèi)容。在例子中,將s2 賦給s1,字符串池中“Hello”字符串不再被指向,s1 現(xiàn)在和s2 指向同一個(gè)字符串。事實(shí)上“Hello”字符串沒(méi)有被修改,理論上,你不能再獲取到它了。這個(gè)目標(biāo)要求你認(rèn)清strings 的不變性,如果你想要改變字符串的內(nèi)容的話,主要的方法就是采用StringBuffer 類(lèi)。因?yàn)樵诤笈_(tái)實(shí)例化時(shí),字符串連接會(huì)產(chǎn)生一個(gè)新的字符串,所以當(dāng)你的操作大量的字符串時(shí),比如從讀取一個(gè)大的文本文件時(shí),性能就很重要了。通常字符串不變性并不影響每天的編程,但是在考試中它經(jīng)常被考到。記住不論怎么考,字符串一旦被創(chuàng)建,它本身就不會(huì)改變,即使指向它的指針指到別的字符串了。如果允許同一字符串再生,這就涉及到字符串在字符串池中的創(chuàng)建方式了。5.2 節(jié)在講解在使用strings 時(shí)=與equal 的作用時(shí),將這個(gè)內(nèi)容作為一個(gè)部分涉及到了。雖然Java2 和Java1.1 都沒(méi)有特別到這個(gè)內(nèi)容,但是我認(rèn)為一些問(wèn)題的回答需要StrngBuffer 的內(nèi)容。 練習(xí)題 習(xí)題1) 已經(jīng)創(chuàng)建了兩個(gè)包含姓名的字符串,即: String fname="John"; String lname="String" 你如果在同一個(gè)代碼塊中,改變這些字符串的值? 1) fname="Fred"; lname="Jones"; 2) String fname=new String("Fred"); String lname=new String("Jones"); 3) StringBuffer fname=new StringBuffer(fname); StringBuffer lname=new StringBuffer(lname); 4) 以上都不正確 習(xí)題2) 假如你寫(xiě)了一個(gè)程序用于讀取8MB 的文本文件。一行一行的讀到一個(gè)String 對(duì)象中,但是你發(fā)現(xiàn)執(zhí)行性能不好。最可能的解釋是? 1) Java I/O 是圍繞最慢的設(shè)備而設(shè)計(jì)的,它本身就很慢 2) String 類(lèi)不適合I/O 操作,字符數(shù)組將更合適 3) 因?yàn)镾tring 的不變性,每一次讀要?jiǎng)?chuàng)建一個(gè)新的String 對(duì)象,改為StringBuffer 可能會(huì)提高性能 4) 以上都不正確 答案 答案1) 4)以上都不正確 一旦創(chuàng)建了一個(gè)String 對(duì)象,它就只能讀不能改變 答案2) 3)因?yàn)镾tring 的不變性,每一次讀要?jiǎng)?chuàng)建一個(gè)新的String 對(duì)象,改為StringBuffer 可能會(huì)提高性能。我希望你們都不會(huì)像C 程序員那樣采用一個(gè)字符數(shù)據(jù)? 目標(biāo)三 包裝類(lèi) 本目標(biāo)主要討論包裝類(lèi)的重要性,包括因?yàn)樘囟ǖ男枨筮x擇最合適的包裝類(lèi)。講述當(dāng)一個(gè)包裝類(lèi)的的實(shí)例代碼片段運(yùn)行回產(chǎn)生什么結(jié)果。DoubleValue, floatValue,longValue,parseXxx,getXxx,toString,toHexString 等等。 本節(jié)需要注意的問(wèn)題該目標(biāo)的知識(shí)點(diǎn)明確在JDK1.4 版本的考試中有明確規(guī)定,如果你看過(guò)以前的舊模擬題,你肯定不會(huì)看見(jiàn)里面包含本目標(biāo)中的知識(shí)。因?yàn)樵趯?shí)際開(kāi)發(fā)過(guò)程中你經(jīng)常會(huì)用到本目標(biāo)中的內(nèi)容,所以學(xué)習(xí)起來(lái)會(huì)很容易。要特別仔細(xì)的學(xué)習(xí)這些知識(shí)點(diǎn),你將會(huì)在真題庫(kù)中看到它們的影子。 什么是包裝類(lèi)Java 中的基本類(lèi)型的包裝類(lèi)提供了大量非常有用的公用方法。比如你需要往一個(gè)vetor里面存儲(chǔ)一列整型數(shù)據(jù),而Vetor 里面存儲(chǔ)的對(duì)象類(lèi)型必須是Object 而不是基本類(lèi)型數(shù)據(jù),當(dāng)你從Vetor 中將這些對(duì)象再取出來(lái)的時(shí)候,你得要用相應(yīng)基本類(lèi)型的包裝類(lèi)中的toxxValue公用方法來(lái)將對(duì)象強(qiáng)制轉(zhuǎn)換成相應(yīng)的基本類(lèi)型的數(shù)據(jù)。下面的代碼講述了這種技巧: import java.util.*; public class VecNum{public static void main(String argv[]){Vector v = new Vector();v.add(new Integer(1));v.add(new Integer(2));for(int i=0; i < v.size();i ++){Integer iw =(Integer) v.get(i);System.out.println(iw.intValue());}} }包裝類(lèi)提供了該對(duì)象與整形數(shù)據(jù)之間相互轉(zhuǎn)換的公用方法,因此當(dāng)你有一個(gè)String 型的數(shù)據(jù)當(dāng)你需要將它轉(zhuǎn)換成它所代表的整型數(shù)據(jù)的時(shí)候,你可以使用包裝類(lèi)來(lái)完成這一系列操作。包裝類(lèi)中提供的公用方法是靜態(tài)的,所以你不需要實(shí)例化一個(gè)包裝類(lèi)的對(duì)象再對(duì)它里面的方法進(jìn)行調(diào)用。當(dāng)你對(duì)一個(gè)包裝類(lèi)賦值以后,你將不能再改邊它。如果你在考試中遇到諸如Integer.setInt(int i)的表述,不用多想,這種方法是不存在的,它是錯(cuò)誤的。 公用方法一個(gè)最有用的包裝類(lèi)的公用方法是一些諸如parseXX 的方法,它的作用是把一個(gè)String型的數(shù)據(jù)轉(zhuǎn)換成一個(gè)它所對(duì)應(yīng)的基本類(lèi)型數(shù)據(jù)。XX 代表包裝類(lèi)所能包括的數(shù)據(jù)類(lèi)型。它包括parseInt, parseLong, parseShort, parseCharacter, parseBoolean。如果你在一個(gè)WEB 頁(yè)面里面有一個(gè)字段代表一個(gè)數(shù)據(jù)類(lèi)型,如果你有一個(gè)里面包含一個(gè)表格字段的WEB頁(yè)面,返回的一個(gè)String型數(shù)據(jù)可以轉(zhuǎn)化成一個(gè)數(shù)值。因此該字段可能包含"101"或者"elephant"。你可以試著用包裝類(lèi)將這些String型的數(shù)據(jù)轉(zhuǎn)化成基本數(shù)據(jù)類(lèi)型.如果它不能被適當(dāng)?shù)霓D(zhuǎn)化(比如其中包含"elephant"),一個(gè)NumberFormatException將會(huì)被拋出。這里有一個(gè)例子,講解了如何將一個(gè)可能可以轉(zhuǎn)化成整型的String數(shù)據(jù)轉(zhuǎn)化成整型數(shù)據(jù),當(dāng)轉(zhuǎn)化不能進(jìn)行的時(shí)候打印出錯(cuò)誤信息。包裝類(lèi)可以構(gòu)造該包裝類(lèi)所包裝的數(shù)據(jù)類(lèi)型,以及可以轉(zhuǎn)化為該類(lèi)型的String型。所以Integer類(lèi)型的包裝類(lèi)可以保存任何的整形數(shù)據(jù),但是如果你試圖將一個(gè)浮點(diǎn)數(shù)傳遞給它的時(shí)候一個(gè)錯(cuò)誤將會(huì)發(fā)生。記住,包裝類(lèi)不是基本數(shù)據(jù)類(lèi)型,它的實(shí)例的操作方式和其他對(duì)象是一樣的。你可能對(duì)考試中出現(xiàn)的一些代碼很疑惑,它們使用數(shù)學(xué)操作符對(duì)包裝類(lèi)的實(shí)例進(jìn)行操作。你顯然可以使用“+”來(lái)對(duì)包裝類(lèi)的實(shí)例進(jìn)行操作,它會(huì)在后臺(tái)調(diào)用toString方法。不過(guò)小心當(dāng)你看到“-”, “%”和 “*”的時(shí)候。 public class String2Int{public static void main(String argv[]){try{int i= Integer.parseInt(argv[0]);System.out.println("Coverted to int val = " + i);}catch(NumberFormatException nfe){ System.out.println("Could not covert to int");}} } toHexStringtoHexString方法以十六進(jìn)制的無(wú)符號(hào)整數(shù)形式返回一個(gè)整數(shù)參數(shù)的字符串表示形式。它有一個(gè)孿生的兄弟方法,它能夠以二進(jìn)制(基數(shù) 2)無(wú)符號(hào)整數(shù)形式返回一個(gè)整數(shù)參數(shù)的字符串表示形式。對(duì)這兩個(gè)方法的用法理解需要對(duì)二進(jìn)制數(shù)和十六進(jìn)制數(shù)的概念有一定了解。你需要知道和對(duì)象相關(guān)的比特偏移的概念。下面的代碼將會(huì)輸出10接著100的串。 public class NumberFormats{public static void main(String argv[]){System.out.println(Integer.toBinaryString(4));System.out.println(Integer.toHexString(16));} } 練習(xí)題 習(xí)題1) 下列哪項(xiàng)表述是正確的? 1) The Integer class has a String and an int constructor 2) The Integer has a floatValue() method 3) The wrapper classes are contained in the java.lang.Math package 4) The Double class has constructors for type double and float 習(xí)題2) 當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class WrapMat{public static void main(String argv[]){Integer iw = new Integer(2);Integer iw2 = new Integer(2);System.out.println(iw * iw2);System.out.println(iw.floatValue());} } 1 )Compile time error 2) Compilation and output of 4 followed by 2.0 3) Compilation and output of 4 followed by 2 4) Compile time error, the Integer class has no floatValue method 習(xí)題3) 當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class TwoEms {public static void main(String argv[]){Object[] oa = new Object[3];oa[0] = new Integer(1);int i = oa[0];System.out.print(i);} } 1) Compile time error an array cannot contain object references 2) Compile time error elements in an array cannot be anonymous 3) Compilation and output of 1 4) Compile time error Integer cannot be assigned to int 5) Compilation and output of the memory address of the Integer instance 習(xí)題4) 當(dāng)你試圖編譯運(yùn)行下列代碼的時(shí)候會(huì)發(fā)生什么? public class TwoPack {public static void main(String argv[]){Integer iw = new Integer(“2”);Integer iw2 = new Integer(“2”);String sOut = iw + iw2;System.out.println(sOut);} } 1) Compile time error, the + operator cannot be applied to Integer 2) Compilation and output of 22 3) Compilation and output of 4 4) Compile time error, Integer has no String constructor 習(xí)題5) 下列哪段代碼是正確的? 1) System.out.println(Integer.toBinaryString(4)); 2) System.out.println(Integer.toOctalString(4)); 3) System.out.println(Integer.add(2,2)); 4) Float[] ar = new Float[] { new Float(1.0), new Float(2.1)}; 答案 答案 1) 1) Integer類(lèi)有一個(gè)整型和String型的構(gòu)造方法 2) Integer類(lèi)有一個(gè)floatValue()方法 4) Double類(lèi)有一個(gè)float型和double型的構(gòu)造方法 答案 2) 1 )Compile time error 包裝類(lèi)的實(shí)例不能像基本數(shù)據(jù)類(lèi)型那樣進(jìn)行操作,注意Integer確實(shí)有一個(gè) floatValue方法 答案 3) 4) Compile time error Integer cannot be assigned to int 這段代碼可以通過(guò) Integer類(lèi)的intValue 方法正常進(jìn)行。 它是一個(gè)包裝類(lèi)對(duì)象不能賦給基本數(shù)據(jù)類(lèi)型變量。 答案 4) 1) Compile time error, the + operator cannot be applied to Integer 包裝類(lèi)的實(shí)例不能像基本數(shù)據(jù)類(lèi)型那樣進(jìn)行操作,它們是對(duì)象的實(shí)例,你必須將起進(jìn)行轉(zhuǎn)換成基本數(shù)據(jù)類(lèi)型來(lái)進(jìn)行數(shù)學(xué)操作。 答案 5) 1) System.out.println(Integer.toBinaryString(4)); 2) System.out.println(Integer.toOctalString(4)); 4) Float[] ar = new Float[] { new Float(1.0), new Float(2.1)}; 包裝類(lèi)的實(shí)例不能像基本數(shù)據(jù)類(lèi)型那樣進(jìn)行操作,因此選項(xiàng)3中的add方法不存在。如果年紀(jì)需要那樣操作,你需要將其轉(zhuǎn)化成基本數(shù)據(jù)類(lèi)型。 第9 章 Java 的Util 包 目標(biāo)一 Collection 類(lèi)/接口為了滿足特定的開(kāi)發(fā)需求,選擇合適的Collection 類(lèi)/接口。 本節(jié)需要注意的問(wèn)題雖然沒(méi)有特別的提到,但是對(duì)本小節(jié)的集合類(lèi)的知識(shí)點(diǎn)是Java2 版本考試的新考點(diǎn)之一,考試中關(guān)于新出現(xiàn)的集合類(lèi)的題目非常簡(jiǎn)單,只需要應(yīng)試者知道在哪如何使用這些類(lèi),而不需要應(yīng)試者完全清楚底層細(xì)節(jié)的方法和字段。 舊的Collection 類(lèi)/接口Java2 通過(guò)一些新增加的類(lèi)/接口加強(qiáng)了集合類(lèi)的用途,早一些的Java 版本的Collection類(lèi)包括: vector hashtable array BitSet 在這些類(lèi)當(dāng)中,只有array 包含在1.1 版本的認(rèn)證考試的考點(diǎn),從Java1.1 開(kāi)始,就是對(duì)所有開(kāi)發(fā)情況中經(jīng)常需要用到的排序功能提供了支持是導(dǎo)致Java 越來(lái)越臃腫的一個(gè)原因。 新的Collection 類(lèi)/接口集合類(lèi)的最底層是Collection 接口,它提供了一系列所有集合類(lèi)開(kāi)發(fā)中常用到的方法。在開(kāi)發(fā)中,或許你從未在你創(chuàng)建的類(lèi)中實(shí)現(xiàn)Collection 接口,那是因?yàn)镴ava 提供了一系列Collection 接口的子類(lèi)/接口。Java2 的API 包含了以下幾個(gè)新的Collection 接口 Sets Maps 所有實(shí)現(xiàn)Collection 接口的類(lèi)存儲(chǔ)對(duì)象為元素而不是原始數(shù)據(jù)類(lèi)型,這種機(jī)制有個(gè)缺點(diǎn)就是創(chuàng)建對(duì)象對(duì)性能的影響,而且元素在使用之前必須從Object 類(lèi)型強(qiáng)制轉(zhuǎn)換成合適的類(lèi)型,這也同時(shí)意味著集合類(lèi)不要求元素是同一類(lèi)型的,因?yàn)橐粋€(gè)Object 對(duì)象可以是任何東西。 Set Set 是一個(gè)不可包含重復(fù)元素的集合類(lèi)接口,這恰好和關(guān)系數(shù)據(jù)庫(kù)中返回某一條記錄的set 概念相符合。Set 接口的奧妙就在于它的add 方法。 add(Object o) 任何一個(gè)傳遞給add 方法的對(duì)象必須實(shí)現(xiàn)equals 方法,這樣保證與已存數(shù)據(jù)進(jìn)行對(duì)比。如果已經(jīng)存在該數(shù)據(jù),那么調(diào)用add 方法不會(huì)對(duì)該set 起任何影響并且返回false。這種試圖添加一個(gè)元素返回false 的思想更像是C/C++中使用的機(jī)制而不是Java,在這種情況下,大多數(shù)Java 的其他類(lèi)似添加方法選擇的是拋出異常。 List list 是一個(gè)有序的可以有重復(fù)元素的集合類(lèi)接口,該接口中一些重要的方法如下: add remove clear JDK 的幫助文檔給出了使用List 處理一個(gè)實(shí)際GUI list 進(jìn)行控制一列包含名為Planets的列表的例子。 Map Map 是一個(gè)接口,實(shí)現(xiàn)它的類(lèi)不能包含重復(fù)的key,這一點(diǎn)和hashtable 很相似。為什么我們使用集合類(lèi)而不使用數(shù)組?相比較而言,集合類(lèi)相對(duì)于數(shù)組一個(gè)最大的優(yōu)點(diǎn)就是它可以自增長(zhǎng),你不需要在創(chuàng)建它的時(shí)候?yàn)樗峙浯笮】臻g,缺點(diǎn)就是集合類(lèi)只能存儲(chǔ)Object 對(duì)象,而不能存儲(chǔ)原始數(shù)據(jù)類(lèi)型,因此不可避免的影響了一定的性能。數(shù)組不能直接支持排序,但是這點(diǎn)可以通過(guò)使用靜態(tài)的集合類(lèi)方法來(lái)克服。以下是一個(gè)例子。 import java.util.*; public class Sort{public static void main(String argv[]){Sort s = new Sort();}Sort(){String s[] = new String[4];s[0]="z";s[1]="b";s[2]="c";s[3]="a";Arrays.sort(s);for(int i=0;i< s.length;i++)System.out.println(s[i]);} } 使用 Vector下面的例子解釋了怎樣將不同類(lèi)的對(duì)象添加到一個(gè)Vector里面.這與數(shù)組不同,不要求每個(gè)元素必須同類(lèi)型.代碼會(huì)將每個(gè)對(duì)象輸出到標(biāo)準(zhǔn)輸出設(shè)備,它隱性調(diào)用了每個(gè)對(duì)象的toString()方法到了Java2 Vector類(lèi)成為創(chuàng)建一個(gè)可變大小數(shù)據(jù)結(jié)構(gòu)的主要方法.可以用remove()方法從Vector類(lèi)移出元素 import java.awt.*; import java.util.*; public class Vec{public static void main(String argv[]){Vec v = new Vec();v.amethod();}//End of mainpublic void amethod(){Vector mv = new Vector();//Note how a vector can store objects//of different typesmv.addElement("Hello");mv.addElement(Color.red);mv.addElement(new Integer(99));//This would cause an error//As a vector will not store primitives//mv.addElement(99)//Walk through each element of the vectorfor(int i=0; i< mv.size(); i++){System.out.println(mv.elementAt(i));}}//End of amethod } 在Java2 之前Vector 類(lèi)是創(chuàng)建可重新分配大小的數(shù)據(jù)結(jié)構(gòu)的主要手段。可以使用remove()方法將元素從Vector 中剔除掉。 使用 HashtablesHashtables 有點(diǎn)像 Visual Basic 中使用鍵來(lái)索引相應(yīng)的鍵值的概念。除了用數(shù)值來(lái)對(duì)應(yīng)元素以外,它的效果很像Vector。哈希表的名字部分通過(guò)引用數(shù)學(xué)概念中的數(shù)字索引概念來(lái)進(jìn)行解決。一個(gè)hashtable 比 Vector 優(yōu)越的地方就在于快速的查找。 BitSet正如BitSet 所暗示的,它存儲(chǔ)的是一個(gè)序列比特。不要被它名字部分的set 部分誤導(dǎo),它不同于數(shù)學(xué)中或者數(shù)據(jù)庫(kù)領(lǐng)域的set,并且它和Java2 中提供Sets 沒(méi)有任何關(guān)聯(lián)。它更適合于被看作一個(gè)存儲(chǔ)比特的容器。一個(gè)BitSet 適合更有效率的存儲(chǔ)一序列代表正/否值的比特值。其他可供選擇的某些集合類(lèi)在存儲(chǔ)布爾值方面不及它有效率。參考一下Bruce Eckel 的《Thinking in Java》:如果僅僅是從存儲(chǔ)的角度來(lái)看它是很有效率;如果你期待更有效率的訪問(wèn),它比一些原生類(lèi)型的數(shù)組要稍微慢一些。BitSet 是一個(gè)在開(kāi)發(fā)中從未需要使用的比較生僻的類(lèi),我認(rèn)為它在密碼領(lǐng)域或者圖片處理的開(kāi)發(fā)過(guò)重使用起來(lái)比較方便。下面讓我看看你是否能夠應(yīng)付來(lái)自Java2 考試的習(xí)題。 練習(xí)題 習(xí)題1) 下面哪些是集合類(lèi)? 1) Collection 2) Iterator 3) HashSet 4) Vector 習(xí)題2) 關(guān)于Collection interface 下面哪些是正確的? 1) The Vector class has been modified to implement Collection 2) The Collection interface offers individual methods and Bulk methods such as addAll 3) The Collection interface is backwardly compatible and all methods are available within the JDK 1.1 classes 4) The collection classes make it unnecessary to use arrays 習(xí)題3) 下面哪些是正確的? 1) The Set interface is designed to ensure that implementing classes have unique members 2) Classes that implement the List interface may not contain duplicate elements 3) The Set interface is designed for storing records returned from a database query 4) The Map Interface is not part of the Collection Framework 習(xí)題4) 下面哪些是正確的? 1) The elements of a Collection class can be ordered by using the sort method of the Collection interface 2) You can create an ordered Collection by instantiating a class that implements the List interface 3) The Collection interface sort method takes parameters of A or D to change the sort order, Ascending/Descending 4) The elements of a Collection class can be ordered by using the order method of the Collection interface 習(xí)題5) 你希望存儲(chǔ)少量數(shù)據(jù)并能快速訪問(wèn). 你并不需要排序這些數(shù)據(jù), uniqueness is not an issue and the data will remain fairly static 那種數(shù)據(jù)結(jié)構(gòu)最適合這種需求? 1) TreeSet 2) HashMap 3) LinkedList 4) an array 習(xí)題6) 下面哪些是Collection 類(lèi)? 1) ListBag 2) HashMap 3) Vector 4) SetList 習(xí)題7) 怎樣從Vector 中移出元素? 1) delete method 2) cancel method 3) clear method 4) remove method 答案 答案1) 3) HashSet 4) Vector 另外兩個(gè)是接口不是類(lèi) 答案2) 1) Vector 類(lèi)已經(jīng)被修改用類(lèi)實(shí)現(xiàn)Collection 2) 集合類(lèi)方法提供了單個(gè)的方法和addAll 等的批量方法。集合類(lèi)是隨著JDK1.2 新推出的. 除了舊的集合類(lèi)如Vetor,Bitset, 如果你在舊的平臺(tái)上運(yùn)行包含了新集合類(lèi)的代碼,將會(huì)拋出異常。 答案3) 1) Set 接口是為了確保正在執(zhí)行的類(lèi)有特定的成員。實(shí)現(xiàn)List 接口的對(duì)象中可以包含重復(fù)的元素.盡管一個(gè)實(shí)現(xiàn)Set 接口的類(lèi)的元素存儲(chǔ)的可能是用來(lái)數(shù)據(jù)庫(kù)查詢結(jié)果, 但它不是為了那個(gè)目的專(zhuān)門(mén)設(shè)計(jì)的。 答案4) 2) 你可以通過(guò)實(shí)力化一個(gè)實(shí)現(xiàn)List 接口來(lái)創(chuàng)建一個(gè)有序的集合類(lèi)。 答案5) 4) 一個(gè)數(shù)組 像這些簡(jiǎn)單的需求用數(shù)組是最合適的了。 答案6) 2) HashMap 3) Vector JDK1.2(Java2)中Vector 這個(gè)類(lèi)被“加裝到”集合框架中來(lái)了。 習(xí)題7) 4) remove 方法 目標(biāo)二 實(shí)現(xiàn)hashCode正確與錯(cuò)誤hashCode 實(shí)現(xiàn)方法的區(qū)別 本節(jié)需要注意的問(wèn)題此宗旨是與JDK1.4 的發(fā)布新推出的. Sun 的網(wǎng)站上顯示這個(gè)宗旨時(shí),用小寫(xiě)字母c 拼寫(xiě)hashcode,但從Object 對(duì)象繼承的方法卻用了大寫(xiě)的C 拼作hashCode.這是一個(gè)已經(jīng)引入到此宗旨的奇怪的話題,處理大量的非常嚴(yán)肅的java 編程,卻不必麻煩你實(shí)現(xiàn)hashcode.真正的數(shù)據(jù)庫(kù)例子不會(huì)讓你困惑于此問(wèn)題,但是這是你應(yīng)該理解的宗旨. 它來(lái)自O(shè)bject 對(duì)象這個(gè)hashcode 方法繼承自所有類(lèi)對(duì)象的父類(lèi),所以任何對(duì)象的實(shí)例都可以調(diào)用hashcode方法,此hashcode 方法的簽名是: public int hashCode() 所以,你可能會(huì)遇到hashcode 的一些偽簽名,比如碩返回非int 值或者帶有非空參數(shù).盡管如此,我懷疑問(wèn)題會(huì)比這個(gè)稍微理論化一些.返回int 值是基于hash 的集合類(lèi)的特殊應(yīng)用,例如 HashTable, HashSet, HashSet基于hash 的集合的本質(zhì)是存儲(chǔ)鍵、值.你用鍵來(lái)查找值。所以,舉個(gè)例子,你可以用一個(gè)HashMap 來(lái)存儲(chǔ)職工的id 作為鍵,職工名字作為值。通常,一個(gè)hashcode 值會(huì)是對(duì)象的內(nèi)存地址。你可以很容易地用一些瑣碎的代碼來(lái)示范這個(gè): public class ShowHash{public static void main(String argv[]){ShowHash sh = new ShowHash();System.out.println(sh.hashCode());} }當(dāng)我編譯、運(yùn)行這段代碼,就會(huì)輸出7474923,這個(gè)就是運(yùn)行程序時(shí)這個(gè)類(lèi)內(nèi)存地址的表示。這就說(shuō)明了一個(gè)hashcode 的一個(gè)特性:在運(yùn)行不同程序時(shí)它會(huì)得到不同的值。如果你考慮一個(gè)對(duì)象的內(nèi)存地址,就不能確定一個(gè)程序的不同運(yùn)行所得到的值。這里有一段來(lái)自JDK1.4 的引用,它包含了一個(gè)hashcode 值的要求“不管什么時(shí)候,當(dāng)一個(gè)java 應(yīng)用程序執(zhí)行期間,多次援引同一個(gè)對(duì)象,hashCode 方法必須一致地返回同樣的integer 類(lèi)型,對(duì)象上的無(wú)信息應(yīng)用的equals 比較就被修改了。這個(gè)整數(shù)在一個(gè)應(yīng)用程序的不同執(zhí)行可以不必保持一致性。”既然它說(shuō),在同樣的程序運(yùn)行中hashCode 的返回值必須一致,改變了對(duì)象上的無(wú)信息應(yīng)用的equals 比較,這個(gè)就告訴我們equals 和hashCode 方法之間的關(guān)系了。 equals 和 hashCode由于每個(gè)對(duì)象都繼承自一個(gè)叫Object 的最終父對(duì)象,所以它們都可以訪問(wèn)equals 方法。但是,當(dāng)默認(rèn)情況下,它只是簡(jiǎn)單地比較對(duì)象的內(nèi)存地址。在用String 類(lèi)的時(shí)候,它的弊端就戲劇性地暴露出來(lái)了。如果String 類(lèi)不實(shí)現(xiàn)equals 方法自己的版本,在比較兩個(gè)字符串的時(shí)候就會(huì)比較它們的內(nèi)存地址,而不是字符串序列。這顯然不是你想要的,基于此,String類(lèi)實(shí)現(xiàn)了自己的equals 方法,可以比較兩個(gè)字符串。這里有API 文檔的另一個(gè)重點(diǎn)。如果兩個(gè)對(duì)象用對(duì)象的equals 方法比較是相等的,那么它們調(diào)用hashCode 方法必須生成同樣的整數(shù)值。此原則可以用下面的代碼來(lái)解釋 public class CompStrings{public static void main(String argv[]){String s1 = new String("Hello");String s2 = new String("Hello");System.out.println(s1.hashCode());System.out.println(s2.hashCode());Integer i1 = new Integer(10);Integer i2 = new Integer(10);System.out.println(i1.hashCode());System.out.println(i2.hashCode());} } 每次程序的運(yùn)行,這段代碼都可以輸出s1 和s2,i1 和i2 的同樣hashCode 值。理論上,在不同情況下會(huì)輸出不同值。 當(dāng)兩個(gè)對(duì)象不等時(shí)就象上面所寫(xiě),用equals 方法判斷兩個(gè)不同的對(duì)象一定會(huì)返回不同hashCode 值,這是一個(gè)看似合理的推斷。實(shí)際上不是,就像API 文檔所說(shuō)的那樣。如果兩個(gè)依據(jù)java.lang.Object 的equals 方法不相等的兩個(gè)對(duì)象,分別調(diào)用hashCode 方法一定會(huì)生成不同的整數(shù)值,這顯然是不確定的。但是程序員應(yīng)該了解這些。同時(shí),你也可以查詢?cè)嫉腁PI 文檔來(lái)理解hashCode 方法的要求。 練習(xí)題 習(xí)題1 )下面所述哪些是正確的? 1)一個(gè)對(duì)象的hashCode 方法會(huì)返回任何原始的整數(shù)類(lèi)型 2)依據(jù)equals 方法,兩個(gè)相等的對(duì)象調(diào)用hashCode 方法會(huì)生成同樣的結(jié)果。 3)一個(gè)對(duì)象的hashcode 方法在一個(gè)應(yīng)用程序的不同執(zhí)行,一定會(huì)返回同樣的值。 4) Object 類(lèi)的hashcode 方法簽名是public int hashCode() 習(xí)題 2) 定義: public class ValuePair implements Comparable{private int iLookUp;public ValuePair(int iLookUp, String sValue){this.iLookUp=iLookUp;}public void setLookUp(int iLookUp){this.iLookUp = iLookUp;}public int getLookUp(){return iLookUp;}public boolean equals(Object o){if( o instanceof ValuePair){ValuePair vp = (ValuePair) o;if(iLookUp == vp.getLookup()){return true;}return false;}public int compareTo(Object o) {ValuePair vp = (ValuePair) o;Integer iwLookUp= new Integer(vp.getLookUp());if(iwLookUp.intValue() < iLookUp){return -1;}if(iwLookUp.intValue() > iLookUp){return +1;}return 0;}} } 下面那個(gè)是有效的hashCode 方法 1) public int hashCode() {return (int) System.currentTimeMillis(); } 2) public char hashCode(){reutrn (char) iLookUp; } 3) public int hashCode(){return iLookUp; } 4) public int hashCode(){return iLookup * 100; } 習(xí)題 3) 給出下面的代碼 public class Boxes{String sValue;Boxes(String sValue){this.sValue=sValue;}public String getValue(){return sValue;}public boolean equals(Object o){String s = (String) o;if (sValue.equals(s) ){return true;}else{return false;}}public int hashCode(){return sValue.hashCode();} } 哪些是正確的 1) 正確地執(zhí)行hashCode 方法 2)此類(lèi)不會(huì)編譯,因?yàn)镾tring 沒(méi)有hashCode 方法 3)不正確地執(zhí)行hashCode 方法 4)類(lèi)不會(huì)編譯,因?yàn)閏ompareTo 方法不會(huì)執(zhí)行 習(xí)題 4) 判斷對(duì)錯(cuò) 如果正確地創(chuàng)建了一個(gè)對(duì)象,那么調(diào)用它的hashCode 方法將返回同樣的值 1) True 2) False 答案 答案 1) 2)依據(jù)equals 方法,兩個(gè)相等的對(duì)象調(diào)用hashCode 方法會(huì)生成同樣的結(jié)果。 4) Object 類(lèi)的hashcode 方法簽名是public int hashCode() 答案 2) 3) public int hashCode(){return iLookUp; } 4) public int hashCode(){return iLookup * 100; } hashCode 方法必須返回整數(shù)值就排除了返回一個(gè)char 值的選項(xiàng)2,選項(xiàng)1 返回了毫秒形式的time 類(lèi)型,由于程序的單次運(yùn)行一定會(huì)得到不同的值,所以就破壞了hashCode 的一個(gè)特殊要求。正確選項(xiàng)3 和4 可能不是hashCode 方法的好版本,但是它們一致地得到相等值并返回正確的數(shù)據(jù)類(lèi)型 答案 3) 1)正確地執(zhí)行hashCode 方法String 類(lèi)有hashCode 方法的自己實(shí)現(xiàn)。如果沒(méi)有,它會(huì)繼承Object 對(duì)象的hashCode 方法,此方法簡(jiǎn)單地返回對(duì)象實(shí)例的內(nèi)存地址。 答案 4) 2) False 小心任何帶always 詞的問(wèn)題。對(duì)象類(lèi)的hashCode 方法默認(rèn)返回對(duì)象的內(nèi)存地址。Java 工作的一些知識(shí)告訴我們,不同執(zhí)行不一定會(huì)得到同樣的內(nèi)存地址。一個(gè)hashCode 方法在一個(gè)程序的同樣運(yùn)行下一定會(huì)返回同樣的值,但在不同運(yùn)行下不一定會(huì)。如果你測(cè)試一個(gè)對(duì)象實(shí)例的hashCode,你可能發(fā)現(xiàn)在多程序運(yùn)行期間,好像返回同樣的內(nèi)存地址,但是這并不是確定的。 附 SCJP認(rèn)證考試授權(quán)考試中心 天津 天津智知堂培訓(xùn)中心 022 23657188 23657188 天津市南開(kāi)區(qū)復(fù)康路25號(hào) 天津市教育科學(xué)研究院7樓 300191 湖北省武漢 武漢愛(ài)科信息技術(shù)有限公司 027 87522501 87522530-803 59713102 87522501 珞瑜路272號(hào)關(guān)山高新大廈502房 430074

?

轉(zhuǎn)自:http://xiangxm.iteye.com/blog/1744131

?

總結(jié)

以上是生活随笔為你收集整理的SCJP 认证考试指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

黑人大群体交免费视频 | 日本高清一区免费中文视频 | 亚洲国产欧美国产综合一区 | 成人精品一区二区三区中文字幕 | 日产精品99久久久久久 | 无码国模国产在线观看 | 日本熟妇大屁股人妻 | 久久综合给久久狠狠97色 | 久久97精品久久久久久久不卡 | 成人性做爰aaa片免费看不忠 | 少妇无码一区二区二三区 | 亚洲日韩精品欧美一区二区 | 色婷婷综合中文久久一本 | 在线亚洲高清揄拍自拍一品区 | 中文无码伦av中文字幕 | 亚洲一区二区三区无码久久 | 97色伦图片97综合影院 | 麻豆国产人妻欲求不满谁演的 | 国产婷婷色一区二区三区在线 | 少妇久久久久久人妻无码 | 亚洲综合另类小说色区 | 香蕉久久久久久av成人 | 亚洲七七久久桃花影院 | 欧美激情一区二区三区成人 | а√天堂www在线天堂小说 | 国产亚洲美女精品久久久2020 | 国产激情精品一区二区三区 | 成人免费视频一区二区 | 中文精品久久久久人妻不卡 | 国产精品香蕉在线观看 | 久久精品国产精品国产精品污 | 中文字幕乱码中文乱码51精品 | 精品国产青草久久久久福利 | 久久久久亚洲精品中文字幕 | 精品人妻av区 | 免费视频欧美无人区码 | 日本乱偷人妻中文字幕 | 亚洲国产精品久久人人爱 | 国产乱子伦视频在线播放 | 又色又爽又黄的美女裸体网站 | 欧美国产日产一区二区 | 欧美午夜特黄aaaaaa片 | 亚洲乱亚洲乱妇50p | 国产后入清纯学生妹 | 亚洲国产一区二区三区在线观看 | 亚拍精品一区二区三区探花 | 亚洲国产精品成人久久蜜臀 | 天堂在线观看www | 初尝人妻少妇中文字幕 | 国产莉萝无码av在线播放 | 久久亚洲中文字幕精品一区 | 激情内射亚州一区二区三区爱妻 | 日韩精品一区二区av在线 | 国产又爽又黄又刺激的视频 | 波多野结衣av一区二区全免费观看 | 精品水蜜桃久久久久久久 | 欧美人妻一区二区三区 | 久久久久久国产精品无码下载 | 亚洲日韩av一区二区三区四区 | 岛国片人妻三上悠亚 | 自拍偷自拍亚洲精品被多人伦好爽 | 少妇被黑人到高潮喷出白浆 | 97久久精品无码一区二区 | 黑人玩弄人妻中文在线 | 学生妹亚洲一区二区 | 精品aⅴ一区二区三区 | 国产麻豆精品一区二区三区v视界 | 国产99久久精品一区二区 | 亚洲精品综合五月久久小说 | 国产成人综合美国十次 | 亚洲日本一区二区三区在线 | 捆绑白丝粉色jk震动捧喷白浆 | 成 人影片 免费观看 | 一区二区传媒有限公司 | 亚洲 日韩 欧美 成人 在线观看 | 国产莉萝无码av在线播放 | 人妻少妇精品无码专区二区 | 丁香花在线影院观看在线播放 | 久久精品女人的天堂av | 97无码免费人妻超级碰碰夜夜 | 久久久中文字幕日本无吗 | 国产精品成人av在线观看 | 在线а√天堂中文官网 | 偷窥日本少妇撒尿chinese | 日韩亚洲欧美中文高清在线 | 日韩精品无码一区二区中文字幕 | 领导边摸边吃奶边做爽在线观看 | 国产精品无码成人午夜电影 | 99er热精品视频 | 国产亚洲精品精品国产亚洲综合 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲码国产精品高潮在线 | 人妻少妇被猛烈进入中文字幕 | 中文字幕无码av激情不卡 | 国产女主播喷水视频在线观看 | 樱花草在线社区www | 美女扒开屁股让男人桶 | 成人精品一区二区三区中文字幕 | 亚洲精品一区国产 | www国产亚洲精品久久久日本 | 国产亚洲欧美在线专区 | 亚洲va中文字幕无码久久不卡 | 成人试看120秒体验区 | 人人妻人人澡人人爽欧美精品 | 无码吃奶揉捏奶头高潮视频 | 日韩精品a片一区二区三区妖精 | 成熟人妻av无码专区 | 无遮挡国产高潮视频免费观看 | 日韩欧美群交p片內射中文 | 丝袜人妻一区二区三区 | 一本久久a久久精品亚洲 | 无码午夜成人1000部免费视频 | 亚洲精品国产a久久久久久 | 精品无人国产偷自产在线 | 乱中年女人伦av三区 | 四虎4hu永久免费 | 国产国语老龄妇女a片 | 亚洲aⅴ无码成人网站国产app | 国产亚av手机在线观看 | 欧美亚洲日韩国产人成在线播放 | 精品厕所偷拍各类美女tp嘘嘘 | 精品久久8x国产免费观看 | 99久久精品无码一区二区毛片 | 午夜精品一区二区三区的区别 | 婷婷五月综合缴情在线视频 | 熟女俱乐部五十路六十路av | 亚洲精品国产精品乱码视色 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲成色www久久网站 | 麻豆成人精品国产免费 | 又粗又大又硬毛片免费看 | 国产做国产爱免费视频 | 一二三四在线观看免费视频 | 老司机亚洲精品影院 | 人人超人人超碰超国产 | 日本熟妇人妻xxxxx人hd | 99精品久久毛片a片 | 内射欧美老妇wbb | 亲嘴扒胸摸屁股激烈网站 | 人人超人人超碰超国产 | 成人无码视频免费播放 | 国产亚洲欧美日韩亚洲中文色 | 国产精品亚洲а∨无码播放麻豆 | 一本精品99久久精品77 | 又湿又紧又大又爽a视频国产 | 久久久婷婷五月亚洲97号色 | 最新国产乱人伦偷精品免费网站 | 欧美人与物videos另类 | 日韩视频 中文字幕 视频一区 | 国产绳艺sm调教室论坛 | 日本一区二区更新不卡 | 天天拍夜夜添久久精品 | 丰满妇女强制高潮18xxxx | 男女下面进入的视频免费午夜 | 无码国产乱人伦偷精品视频 | 天堂在线观看www | 亚洲国产精品久久久久久 | 波多野结衣av一区二区全免费观看 | 夜夜夜高潮夜夜爽夜夜爰爰 | 丁香花在线影院观看在线播放 | 亚洲国产精品无码久久久久高潮 | 精品久久久久久亚洲精品 | 天堂亚洲免费视频 | 亚洲人成影院在线无码按摩店 | 又色又爽又黄的美女裸体网站 | 无码国产色欲xxxxx视频 | 久久久成人毛片无码 | 天海翼激烈高潮到腰振不止 | 真人与拘做受免费视频一 | 中文无码成人免费视频在线观看 | 中文字幕乱码人妻二区三区 | 国产凸凹视频一区二区 | 啦啦啦www在线观看免费视频 | 国内少妇偷人精品视频免费 | 99视频精品全部免费免费观看 | 性色欲网站人妻丰满中文久久不卡 | 亚洲日本一区二区三区在线 | 国产亚洲精品久久久久久久 | 久久国产自偷自偷免费一区调 | 图片小说视频一区二区 | 久久久精品456亚洲影院 | 国产香蕉97碰碰久久人人 | 精品国产青草久久久久福利 | 丰满妇女强制高潮18xxxx | 国产av人人夜夜澡人人爽麻豆 | 亚洲欧洲中文日韩av乱码 | 国产午夜精品一区二区三区嫩草 | 久久成人a毛片免费观看网站 | 精品国产一区二区三区四区 | 国产精品无套呻吟在线 | 亚洲の无码国产の无码步美 | 精品久久久无码中文字幕 | 欧美日韩色另类综合 | 亚洲色在线无码国产精品不卡 | 精品亚洲韩国一区二区三区 | 无码午夜成人1000部免费视频 | 伊人色综合久久天天小片 | 大地资源中文第3页 | 日产精品高潮呻吟av久久 | 日韩成人一区二区三区在线观看 | 欧美亚洲日韩国产人成在线播放 | 夜夜影院未满十八勿进 | 久久99久久99精品中文字幕 | 亚洲欧美色中文字幕在线 | 成人av无码一区二区三区 | 精品偷拍一区二区三区在线看 | 久久无码中文字幕免费影院蜜桃 | 精品国偷自产在线视频 | 色欲人妻aaaaaaa无码 | 在线观看国产午夜福利片 | 午夜精品一区二区三区在线观看 | 欧美zoozzooz性欧美 | 国内精品人妻无码久久久影院蜜桃 | 国内精品一区二区三区不卡 | 成 人 免费观看网站 | 宝宝好涨水快流出来免费视频 | 99久久99久久免费精品蜜桃 | 少妇性l交大片欧洲热妇乱xxx | 亚洲国产午夜精品理论片 | 中文毛片无遮挡高清免费 | 少妇被粗大的猛进出69影院 | 国产成人综合在线女婷五月99播放 | 啦啦啦www在线观看免费视频 | 人人妻人人澡人人爽精品欧美 | 美女扒开屁股让男人桶 | 乱人伦人妻中文字幕无码久久网 | 国产成人午夜福利在线播放 | 国产精品a成v人在线播放 | 精品国产乱码久久久久乱码 | 激情五月综合色婷婷一区二区 | 国产香蕉尹人综合在线观看 | 无码av岛国片在线播放 | 狠狠色丁香久久婷婷综合五月 | 无码人妻精品一区二区三区下载 | 宝宝好涨水快流出来免费视频 | аⅴ资源天堂资源库在线 | 丰满妇女强制高潮18xxxx | 天天燥日日燥 | 久久精品国产精品国产精品污 | 国产精品18久久久久久麻辣 | 精品一区二区三区无码免费视频 | 国产精品美女久久久网av | 扒开双腿疯狂进出爽爽爽视频 | 夜夜高潮次次欢爽av女 | 国产美女精品一区二区三区 | 国产精品手机免费 | 黑人大群体交免费视频 | 亚洲热妇无码av在线播放 | 久精品国产欧美亚洲色aⅴ大片 | 草草网站影院白丝内射 | 少女韩国电视剧在线观看完整 | 波多野结衣 黑人 | 欧美日韩一区二区三区自拍 | 一区二区三区乱码在线 | 欧洲 | 天干天干啦夜天干天2017 | 欧美 日韩 人妻 高清 中文 | 亚洲国产精品一区二区第一页 | 99精品国产综合久久久久五月天 | 精品无码一区二区三区的天堂 | 日韩精品无码免费一区二区三区 | 色欲综合久久中文字幕网 | 成人免费无码大片a毛片 | 久久精品成人欧美大片 | 中文字幕无码免费久久99 | 国产精品沙发午睡系列 | 大肉大捧一进一出好爽视频 | 丰满岳乱妇在线观看中字无码 | 无码毛片视频一区二区本码 | 欧美丰满老熟妇xxxxx性 | 人妻少妇精品视频专区 | 国产舌乚八伦偷品w中 | 天下第一社区视频www日本 | 国产精品爱久久久久久久 | 久久99国产综合精品 | 丰满人妻一区二区三区免费视频 | 乌克兰少妇xxxx做受 | 亚洲呦女专区 | 一本久道久久综合狠狠爱 | 黑人粗大猛烈进出高潮视频 | 图片区 小说区 区 亚洲五月 | 亚洲精品无码国产 | 国产精品多人p群无码 | 大肉大捧一进一出好爽视频 | 国产香蕉97碰碰久久人人 | 中文字幕无码人妻少妇免费 | 亚洲国产精品久久人人爱 | 四虎国产精品一区二区 | 乌克兰少妇xxxx做受 | 在线观看国产一区二区三区 | 亚洲热妇无码av在线播放 | 玩弄少妇高潮ⅹxxxyw | 国产无遮挡又黄又爽免费视频 | 亚洲色偷偷男人的天堂 | 中文字幕无码乱人伦 | 久久zyz资源站无码中文动漫 | 久久国产精品萌白酱免费 | 激情国产av做激情国产爱 | 99久久人妻精品免费一区 | 少妇被黑人到高潮喷出白浆 | 久久成人a毛片免费观看网站 | 东京无码熟妇人妻av在线网址 | 性欧美牲交xxxxx视频 | 少妇人妻大乳在线视频 | 亚洲精品国产精品乱码视色 | a片免费视频在线观看 | 久久综合网欧美色妞网 | 色欲av亚洲一区无码少妇 | 久久久久久久人妻无码中文字幕爆 | 久久久久久久久蜜桃 | 成年美女黄网站色大免费视频 | 1000部夫妻午夜免费 | 亚洲人成网站在线播放942 | 国产在线aaa片一区二区99 | 免费人成在线观看网站 | 少妇性俱乐部纵欲狂欢电影 | 成熟女人特级毛片www免费 | 国产精品久免费的黄网站 | 久久99精品国产.久久久久 | 任你躁在线精品免费 | 久久综合色之久久综合 | 精品国产麻豆免费人成网站 | 日韩欧美成人免费观看 | 亚洲色大成网站www | 少妇无套内谢久久久久 | 精品aⅴ一区二区三区 | 中文字幕乱码亚洲无线三区 | 欧美性生交xxxxx久久久 | 岛国片人妻三上悠亚 | 成人女人看片免费视频放人 | 欧美熟妇另类久久久久久多毛 | 丁香花在线影院观看在线播放 | aa片在线观看视频在线播放 | 少妇人妻偷人精品无码视频 | 无码av免费一区二区三区试看 | 乱人伦中文视频在线观看 | 欧美黑人巨大xxxxx | 老司机亚洲精品影院无码 | 99在线 | 亚洲 | 亚洲国产精品无码久久久久高潮 | 超碰97人人射妻 | 国产肉丝袜在线观看 | 亚洲自偷自偷在线制服 | 精品国产一区二区三区四区在线看 | 熟女少妇在线视频播放 | 人人妻人人澡人人爽欧美一区九九 | 熟妇人妻无乱码中文字幕 | 大地资源网第二页免费观看 | 亚洲日韩一区二区 | 久热国产vs视频在线观看 | 澳门永久av免费网站 | 大地资源网第二页免费观看 | 欧美人与动性行为视频 | 性色av无码免费一区二区三区 | 俺去俺来也在线www色官网 | 精品 日韩 国产 欧美 视频 | 国产色在线 | 国产 | 久久久久国色av免费观看性色 | 色欲综合久久中文字幕网 | 动漫av一区二区在线观看 | 波多野42部无码喷潮在线 | 国产精品永久免费视频 | 少妇邻居内射在线 | 久久无码专区国产精品s | 日本肉体xxxx裸交 | 大地资源网第二页免费观看 | 九九在线中文字幕无码 | 黄网在线观看免费网站 | 亚洲va欧美va天堂v国产综合 | 日本成熟视频免费视频 | 中文字幕乱妇无码av在线 | 88国产精品欧美一区二区三区 | 狂野欧美性猛xxxx乱大交 | 强辱丰满人妻hd中文字幕 | 青青草原综合久久大伊人精品 | 欧美自拍另类欧美综合图片区 | 精品偷拍一区二区三区在线看 | 国产精品第一区揄拍无码 | 亚洲一区二区三区四区 | 动漫av一区二区在线观看 | 日产精品99久久久久久 | 久久久中文字幕日本无吗 | 丰满少妇女裸体bbw | 男人的天堂av网站 | 成人精品视频一区二区三区尤物 | 亚洲成a人片在线观看无码3d | 日本xxxx色视频在线观看免费 | 女人被男人躁得好爽免费视频 | 亚洲中文字幕va福利 | 国产精品二区一区二区aⅴ污介绍 | 国产日产欧产精品精品app | 亚洲欧美日韩成人高清在线一区 | 综合激情五月综合激情五月激情1 | 国产成人无码av一区二区 | 精品人妻人人做人人爽 | 纯爱无遮挡h肉动漫在线播放 | 国产欧美亚洲精品a | 国产国产精品人在线视 | 久久成人a毛片免费观看网站 | 人人爽人人爽人人片av亚洲 | av香港经典三级级 在线 | 亚洲国产欧美在线成人 | a在线观看免费网站大全 | 天天爽夜夜爽夜夜爽 | 久久精品视频在线看15 | 熟妇人妻激情偷爽文 | 5858s亚洲色大成网站www | 国产在线aaa片一区二区99 | 久久综合久久自在自线精品自 | 国产农村乱对白刺激视频 | 大肉大捧一进一出视频出来呀 | 麻豆av传媒蜜桃天美传媒 | 亚洲国产精品无码一区二区三区 | av小次郎收藏 | 5858s亚洲色大成网站www | 无码成人精品区在线观看 | 老头边吃奶边弄进去呻吟 | 在线亚洲高清揄拍自拍一品区 | 未满小14洗澡无码视频网站 | 亚洲熟妇色xxxxx欧美老妇y | 婷婷六月久久综合丁香 | 粉嫩少妇内射浓精videos | 日本大乳高潮视频在线观看 | 97久久精品无码一区二区 | 国产高清不卡无码视频 | 欧美精品在线观看 | 黑人粗大猛烈进出高潮视频 | 国产亚洲人成a在线v网站 | 中文毛片无遮挡高清免费 | 国产成人综合在线女婷五月99播放 | 欧美日本精品一区二区三区 | 正在播放老肥熟妇露脸 | 欧洲熟妇色 欧美 | 国产亚洲精品久久久ai换 | 欧美日本精品一区二区三区 | 中文字幕av日韩精品一区二区 | 国产人成高清在线视频99最全资源 | 欧洲vodafone精品性 | 在线亚洲高清揄拍自拍一品区 | 国产成人无码av在线影院 | 欧美性猛交xxxx富婆 | 撕开奶罩揉吮奶头视频 | 久久久久免费精品国产 | 日本丰满护士爆乳xxxx | 高清国产亚洲精品自在久久 | 久久精品女人天堂av免费观看 | 扒开双腿吃奶呻吟做受视频 | 97精品人妻一区二区三区香蕉 | 久久久久久久久888 | 18精品久久久无码午夜福利 | 亚洲日韩中文字幕在线播放 | 国产成人精品无码播放 | 日韩人妻系列无码专区 | 熟妇女人妻丰满少妇中文字幕 | 欧美性猛交xxxx富婆 | 永久免费观看国产裸体美女 | 免费国产成人高清在线观看网站 | 国产乡下妇女做爰 | 三级4级全黄60分钟 | 桃花色综合影院 | 欧美肥老太牲交大战 | 图片区 小说区 区 亚洲五月 | 伊人久久婷婷五月综合97色 | 日本一区二区三区免费高清 | 欧美丰满熟妇xxxx性ppx人交 | 亚洲va欧美va天堂v国产综合 | 在教室伦流澡到高潮hnp视频 | 真人与拘做受免费视频 | 中文字幕乱码人妻二区三区 | 亚洲国产日韩a在线播放 | 久在线观看福利视频 | 日欧一片内射va在线影院 | 少妇厨房愉情理9仑片视频 | 久久精品国产一区二区三区肥胖 | 在线欧美精品一区二区三区 | 午夜性刺激在线视频免费 | 国产精品美女久久久网av | 又色又爽又黄的美女裸体网站 | 九月婷婷人人澡人人添人人爽 | 黄网在线观看免费网站 | 日韩视频 中文字幕 视频一区 | 99久久精品无码一区二区毛片 | 亚洲午夜久久久影院 | 欧美人与牲动交xxxx | 无码av免费一区二区三区试看 | 久久精品中文字幕一区 | 亚洲区小说区激情区图片区 | 日本www一道久久久免费榴莲 | 国产成人av免费观看 | 成人无码视频在线观看网站 | 少妇一晚三次一区二区三区 | 老司机亚洲精品影院无码 | 午夜男女很黄的视频 | 无遮挡国产高潮视频免费观看 | 妺妺窝人体色www婷婷 | 一本色道婷婷久久欧美 | 精品国偷自产在线视频 | 高潮毛片无遮挡高清免费视频 | 亚洲精品久久久久avwww潮水 | 欧美35页视频在线观看 | 国产麻豆精品一区二区三区v视界 | 综合激情五月综合激情五月激情1 | 亚洲国产精品无码久久久久高潮 | 日本精品久久久久中文字幕 | 又色又爽又黄的美女裸体网站 | 午夜福利不卡在线视频 | 国产suv精品一区二区五 | 婷婷五月综合激情中文字幕 | 性做久久久久久久免费看 | 亚洲精品一区二区三区四区五区 | av无码不卡在线观看免费 | 无套内谢老熟女 | 免费网站看v片在线18禁无码 | 精品成人av一区二区三区 | 欧美老妇与禽交 | 国产区女主播在线观看 | 日日摸夜夜摸狠狠摸婷婷 | 精品久久久久久人妻无码中文字幕 | 成人性做爰aaa片免费看不忠 | 久久精品国产99久久6动漫 | 欧美日韩亚洲国产精品 | 2020久久超碰国产精品最新 | 国产精品久久久一区二区三区 | 熟妇人妻激情偷爽文 | 2020久久香蕉国产线看观看 | 国语精品一区二区三区 | 久久久www成人免费毛片 | 亚洲综合久久一区二区 | 131美女爱做视频 | 熟女少妇在线视频播放 | 波多野结衣乳巨码无在线观看 | 国产国产精品人在线视 | 色诱久久久久综合网ywww | 国产成人精品三级麻豆 | 精品国产一区二区三区四区在线看 | 99久久精品无码一区二区毛片 | 粗大的内捧猛烈进出视频 | 国产免费无码一区二区视频 | 亚洲中文字幕va福利 | 中文字幕av伊人av无码av | 真人与拘做受免费视频一 | 国产电影无码午夜在线播放 | 国产精品va在线观看无码 | 日本熟妇乱子伦xxxx | 国产成人精品必看 | 亚洲 a v无 码免 费 成 人 a v | 中文字幕无码日韩欧毛 | 国产sm调教视频在线观看 | 天天躁日日躁狠狠躁免费麻豆 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国产成人无码a区在线观看视频app | 国产无套粉嫩白浆在线 | 人妻aⅴ无码一区二区三区 | 国产婷婷色一区二区三区在线 | 中文字幕+乱码+中文字幕一区 | 99精品视频在线观看免费 | 18精品久久久无码午夜福利 | 久久精品女人的天堂av | 免费网站看v片在线18禁无码 | 亚洲中文字幕无码中字 | 无套内谢的新婚少妇国语播放 | 少妇被粗大的猛进出69影院 | 亚洲无人区午夜福利码高清完整版 | 国产精品鲁鲁鲁 | 中文字幕无码av激情不卡 | 少妇厨房愉情理9仑片视频 | 亚洲国产一区二区三区在线观看 | 无码一区二区三区在线观看 | 国产欧美熟妇另类久久久 | 亚洲色欲久久久综合网东京热 | 亚洲综合精品香蕉久久网 | 精品国产福利一区二区 | 麻豆国产人妻欲求不满 | 又粗又大又硬又长又爽 | 国产亚洲精品久久久闺蜜 | 国产人妻精品一区二区三区 | 久久熟妇人妻午夜寂寞影院 | 国产乱人无码伦av在线a | 亚洲人成影院在线无码按摩店 | 搡女人真爽免费视频大全 | 久久伊人色av天堂九九小黄鸭 | 成人免费无码大片a毛片 | 99国产精品白浆在线观看免费 | 男人的天堂2018无码 | 国产精品欧美成人 | 永久免费精品精品永久-夜色 | 天堂久久天堂av色综合 | 内射欧美老妇wbb | 99久久精品无码一区二区毛片 | 婷婷丁香五月天综合东京热 | 色一情一乱一伦 | 亚洲精品国产精品乱码不卡 | 中文精品久久久久人妻不卡 | 欧美freesex黑人又粗又大 | 久久精品国产99精品亚洲 | 精品国产麻豆免费人成网站 | 99国产欧美久久久精品 | 中文字幕乱码人妻无码久久 | 伊在人天堂亚洲香蕉精品区 | 日韩精品无码一区二区中文字幕 | 在线精品国产一区二区三区 | 欧美一区二区三区 | 欧美精品一区二区精品久久 | 久久精品一区二区三区四区 | 国产精品资源一区二区 | √8天堂资源地址中文在线 | 波多野结衣av一区二区全免费观看 | 最近中文2019字幕第二页 | 日日天干夜夜狠狠爱 | 日韩欧美成人免费观看 | 国产精品对白交换视频 | 亚洲国精产品一二二线 | 欧美精品无码一区二区三区 | 在线观看国产一区二区三区 | 美女扒开屁股让男人桶 | 欧美午夜特黄aaaaaa片 | 草草网站影院白丝内射 | 成人欧美一区二区三区黑人 | 99久久精品日本一区二区免费 | 亚洲精品中文字幕久久久久 | 日欧一片内射va在线影院 | 久久综合九色综合欧美狠狠 | 无人区乱码一区二区三区 | 在线播放亚洲第一字幕 | 成人欧美一区二区三区黑人免费 | 日韩亚洲欧美精品综合 | 亚洲精品国产第一综合99久久 | 成人影院yy111111在线观看 | 纯爱无遮挡h肉动漫在线播放 | 丁香花在线影院观看在线播放 | 中文无码精品a∨在线观看不卡 | 在线天堂新版最新版在线8 | 人人爽人人爽人人片av亚洲 | 久久99热只有频精品8 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产极品视觉盛宴 | 国产尤物精品视频 | 无码国产乱人伦偷精品视频 | 国产精品久久久久无码av色戒 | 日本乱偷人妻中文字幕 | 色综合视频一区二区三区 | 麻豆国产人妻欲求不满谁演的 | 国产精品久久精品三级 | 欧美亚洲日韩国产人成在线播放 | 欧美三级不卡在线观看 | 精品熟女少妇av免费观看 | 国产精品va在线播放 | 色五月五月丁香亚洲综合网 | 国产人成高清在线视频99最全资源 | 日本xxxx色视频在线观看免费 | 999久久久国产精品消防器材 | 国产激情无码一区二区 | 麻豆国产丝袜白领秘书在线观看 | 人人妻人人澡人人爽精品欧美 | 中文字幕亚洲情99在线 | 国产人妻精品一区二区三区 | 天堂亚洲免费视频 | 欧美三级a做爰在线观看 | 人人妻人人澡人人爽欧美精品 | 自拍偷自拍亚洲精品10p | 久久亚洲精品中文字幕无男同 | 搡女人真爽免费视频大全 | 青草青草久热国产精品 | 精品欧洲av无码一区二区三区 | 在线a亚洲视频播放在线观看 | 人妻中文无码久热丝袜 | 亚洲精品美女久久久久久久 | 亚洲人成影院在线无码按摩店 | 国产精品久久精品三级 | 人人妻人人澡人人爽欧美一区九九 | 国产av无码专区亚洲awww | 欧美 亚洲 国产 另类 | 欧美精品无码一区二区三区 | 亚洲春色在线视频 | 久久久久久久人妻无码中文字幕爆 | 国产乱人偷精品人妻a片 | 东京热男人av天堂 | 免费人成网站视频在线观看 | 久久久久免费精品国产 | 99久久久无码国产精品免费 | 清纯唯美经典一区二区 | 成人免费视频视频在线观看 免费 | 性色av无码免费一区二区三区 | 亚洲综合色区中文字幕 | 全黄性性激高免费视频 | 少妇人妻av毛片在线看 | 国产精品99爱免费视频 | 特大黑人娇小亚洲女 | 亚洲色在线无码国产精品不卡 | 天堂亚洲2017在线观看 | 亚洲成在人网站无码天堂 | 99精品视频在线观看免费 | 亚洲综合精品香蕉久久网 | 国内精品久久毛片一区二区 | 亚洲国产一区二区三区在线观看 | 欧美午夜特黄aaaaaa片 | 亚洲色大成网站www国产 | 国产亚洲精品久久久闺蜜 | 亚洲精品一区二区三区四区五区 | 色偷偷人人澡人人爽人人模 | 东北女人啪啪对白 | 精品一区二区三区波多野结衣 | 噜噜噜亚洲色成人网站 | 一个人看的视频www在线 | 国产午夜精品一区二区三区嫩草 | 麻豆国产人妻欲求不满谁演的 | 日本乱人伦片中文三区 | 亚洲色欲色欲天天天www | 午夜免费福利小电影 | 少妇人妻偷人精品无码视频 | 97精品国产97久久久久久免费 | 日日摸夜夜摸狠狠摸婷婷 | 欧美日韩亚洲国产精品 | 精品一区二区三区无码免费视频 | 国产黑色丝袜在线播放 | 无码av最新清无码专区吞精 | 国产精品毛多多水多 | 97资源共享在线视频 | 人人妻人人澡人人爽欧美精品 | 天天躁日日躁狠狠躁免费麻豆 | 爽爽影院免费观看 | 亚洲自偷自拍另类第1页 | 中文字幕无码av激情不卡 | 精品一区二区不卡无码av | 性欧美熟妇videofreesex | 国产午夜视频在线观看 | 97久久国产亚洲精品超碰热 | 国产亚洲欧美日韩亚洲中文色 | 动漫av一区二区在线观看 | 老司机亚洲精品影院无码 | 久久综合给合久久狠狠狠97色 | 日本成熟视频免费视频 | 国产午夜福利亚洲第一 | 5858s亚洲色大成网站www | 国产精品久久久久久亚洲影视内衣 | 无码av岛国片在线播放 | www国产精品内射老师 | av香港经典三级级 在线 | 亚洲伊人久久精品影院 | 激情亚洲一区国产精品 | 丰满肥臀大屁股熟妇激情视频 | 亚洲无人区一区二区三区 | 鲁鲁鲁爽爽爽在线视频观看 | 牛和人交xxxx欧美 | 18无码粉嫩小泬无套在线观看 | 国产电影无码午夜在线播放 | 夜精品a片一区二区三区无码白浆 | 天堂а√在线地址中文在线 | 夜夜高潮次次欢爽av女 | 欧美性生交xxxxx久久久 | 人妻插b视频一区二区三区 | 无码av中文字幕免费放 | 精品无码av一区二区三区 | 激情亚洲一区国产精品 | 免费观看的无遮挡av | 欧美熟妇另类久久久久久多毛 | 亚洲另类伦春色综合小说 | 国内精品人妻无码久久久影院蜜桃 | 久久综合激激的五月天 | 天下第一社区视频www日本 | 丰满少妇熟乱xxxxx视频 | 在线观看免费人成视频 | 日日碰狠狠躁久久躁蜜桃 | 纯爱无遮挡h肉动漫在线播放 | 最近免费中文字幕中文高清百度 | 久久人人爽人人人人片 | 内射爽无广熟女亚洲 | 国产成人人人97超碰超爽8 | 欧美老人巨大xxxx做受 | 国产猛烈高潮尖叫视频免费 | 少妇无码一区二区二三区 | 日韩欧美群交p片內射中文 | 国产麻豆精品精东影业av网站 | 亚洲人成网站色7799 | 日日麻批免费40分钟无码 | 精品日本一区二区三区在线观看 | 色综合久久88色综合天天 | 99精品久久毛片a片 | 日本大香伊一区二区三区 | 日韩欧美中文字幕公布 | 精品久久久无码人妻字幂 | 国产亲子乱弄免费视频 | 又黄又爽又色的视频 | 一本久道久久综合婷婷五月 | 国产后入清纯学生妹 | 无码人妻黑人中文字幕 | 中文字幕人妻无码一夲道 | 国产莉萝无码av在线播放 | 中文字幕无码乱人伦 | 成年美女黄网站色大免费全看 | 丁香花在线影院观看在线播放 | 色婷婷综合中文久久一本 | 国产亚洲精品久久久久久大师 | 久久久久久国产精品无码下载 | 一本色道婷婷久久欧美 | 久久久婷婷五月亚洲97号色 | 3d动漫精品啪啪一区二区中 | 精品偷拍一区二区三区在线看 | 亚洲爆乳精品无码一区二区三区 | 国产9 9在线 | 中文 | 丰满少妇女裸体bbw | 影音先锋中文字幕无码 | 黑森林福利视频导航 | 久热国产vs视频在线观看 | 麻豆国产人妻欲求不满谁演的 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲 a v无 码免 费 成 人 a v | 久久久国产一区二区三区 | 人妻体内射精一区二区三四 | 精品欧美一区二区三区久久久 | 美女黄网站人色视频免费国产 | 一本久道高清无码视频 | 无码乱肉视频免费大全合集 | 在线a亚洲视频播放在线观看 | 久久99精品久久久久婷婷 | 秋霞成人午夜鲁丝一区二区三区 | 无码av岛国片在线播放 | 午夜嘿嘿嘿影院 | 色婷婷av一区二区三区之红樱桃 | 永久免费观看美女裸体的网站 | 伊人久久大香线蕉亚洲 | 久久久久免费看成人影片 | 亚洲精品久久久久avwww潮水 | 亚洲国产精品美女久久久久 | 少妇激情av一区二区 | 一个人看的视频www在线 | 日韩成人一区二区三区在线观看 | 影音先锋中文字幕无码 | 亚欧洲精品在线视频免费观看 | 四虎4hu永久免费 | 国产9 9在线 | 中文 | 领导边摸边吃奶边做爽在线观看 | 色婷婷香蕉在线一区二区 | 在线a亚洲视频播放在线观看 | 国产成人无码av在线影院 | 国产成人一区二区三区别 | 久久99精品久久久久久动态图 | 人人澡人人透人人爽 | 日日摸日日碰夜夜爽av | 亚洲精品国产第一综合99久久 | 亚洲熟妇色xxxxx亚洲 | 一本加勒比波多野结衣 | а天堂中文在线官网 | 美女极度色诱视频国产 | 亚洲s色大片在线观看 | 久久综合九色综合97网 | 性欧美熟妇videofreesex | 亚洲а∨天堂久久精品2021 | 国产亚洲精品久久久ai换 | 波多野结衣高清一区二区三区 | 国产免费久久久久久无码 | 亚洲精品国偷拍自产在线麻豆 | 人妻尝试又大又粗久久 | 久久精品国产精品国产精品污 | 久久亚洲国产成人精品性色 | 精品一区二区三区无码免费视频 | 国产精品18久久久久久麻辣 | 国产国产精品人在线视 | 日本乱偷人妻中文字幕 | 欧美 丝袜 自拍 制服 另类 | 国产一精品一av一免费 | 亚无码乱人伦一区二区 | 日韩欧美中文字幕公布 | 女高中生第一次破苞av | 亚洲国产精品美女久久久久 | 天天拍夜夜添久久精品大 | 无码人妻久久一区二区三区不卡 | 国产亚洲精品久久久久久久 | 最近中文2019字幕第二页 | 女人被男人爽到呻吟的视频 | 久久久久av无码免费网 | 天天爽夜夜爽夜夜爽 | 久久久久久亚洲精品a片成人 | 亚洲综合色区中文字幕 | 玩弄少妇高潮ⅹxxxyw | 无码人中文字幕 | 在线播放亚洲第一字幕 | 男女猛烈xx00免费视频试看 | 亚洲欧美日韩成人高清在线一区 | 国产成人一区二区三区别 | 无码av最新清无码专区吞精 | 色情久久久av熟女人妻网站 | 人人澡人摸人人添 | 国产绳艺sm调教室论坛 | 丰满少妇熟乱xxxxx视频 | 性开放的女人aaa片 | 中文字幕人妻无码一夲道 | 国产国产精品人在线视 | 亚欧洲精品在线视频免费观看 | 色婷婷综合中文久久一本 | 女人高潮内射99精品 | 夜夜影院未满十八勿进 | 国产成人精品久久亚洲高清不卡 | 日韩成人一区二区三区在线观看 | 亚洲天堂2017无码中文 | 日本高清一区免费中文视频 | 狠狠综合久久久久综合网 | 亚洲日韩精品欧美一区二区 | 成人无码视频在线观看网站 | 亚洲国产精品无码久久久久高潮 | 国产在线精品一区二区三区直播 | 精品国产精品久久一区免费式 | 久久国产精品二国产精品 | 女人被爽到呻吟gif动态图视看 | 亚洲の无码国产の无码影院 | 漂亮人妻洗澡被公强 日日躁 | 国产人妻人伦精品1国产丝袜 | 最新国产乱人伦偷精品免费网站 | 国产亚洲精品久久久久久大师 | 国产精品第一国产精品 | 日日干夜夜干 | 亚洲日本在线电影 | 在教室伦流澡到高潮hnp视频 | 免费播放一区二区三区 | 国产香蕉尹人视频在线 | 中文字幕精品av一区二区五区 | 国内精品人妻无码久久久影院蜜桃 | 一本一道久久综合久久 | 精品乱码久久久久久久 | 欧美性色19p | 两性色午夜免费视频 | 国产又粗又硬又大爽黄老大爷视 | 人人妻人人藻人人爽欧美一区 | 欧美熟妇另类久久久久久不卡 | 亚洲第一无码av无码专区 | 欧美日韩视频无码一区二区三 | 大肉大捧一进一出好爽视频 | 国产精品第一国产精品 | 中文字幕日韩精品一区二区三区 | 人人澡人人透人人爽 | 日韩精品无码一本二本三本色 | 午夜福利一区二区三区在线观看 | 日本一卡二卡不卡视频查询 | 青春草在线视频免费观看 | 日本精品人妻无码免费大全 | 四十如虎的丰满熟妇啪啪 | 午夜成人1000部免费视频 | 中文无码伦av中文字幕 | 久久人人爽人人爽人人片ⅴ | 成人性做爰aaa片免费看 | 秋霞成人午夜鲁丝一区二区三区 | 午夜肉伦伦影院 | 99久久久国产精品无码免费 | 国产性生交xxxxx无码 | 国产精品高潮呻吟av久久4虎 | 国产凸凹视频一区二区 | 欧美午夜特黄aaaaaa片 | 无码人妻丰满熟妇区五十路百度 | 国产情侣作爱视频免费观看 | 国内丰满熟女出轨videos | 亚洲精品鲁一鲁一区二区三区 | 无码国产激情在线观看 | 亚洲人成无码网www | 无码av最新清无码专区吞精 | 中文字幕av无码一区二区三区电影 | 亚洲国精产品一二二线 | 亚洲 日韩 欧美 成人 在线观看 | 国产成人精品三级麻豆 | 亚洲 a v无 码免 费 成 人 a v | 欧美日韩久久久精品a片 | 国产内射老熟女aaaa | 亚洲熟妇自偷自拍另类 | 亚洲va中文字幕无码久久不卡 | 亚洲伊人久久精品影院 | 中文字幕无码人妻少妇免费 | 国产成人精品三级麻豆 | 2020久久香蕉国产线看观看 | 国内精品久久久久久中文字幕 | 亚洲乱码日产精品bd | 成人免费无码大片a毛片 | 亚洲成a人片在线观看日本 | 亚洲理论电影在线观看 | 国产sm调教视频在线观看 | 久久久av男人的天堂 | 人妻互换免费中文字幕 | 亚洲色欲色欲欲www在线 | 中文字幕 亚洲精品 第1页 | 色狠狠av一区二区三区 | 男女超爽视频免费播放 | 精品久久久久久人妻无码中文字幕 | 性做久久久久久久免费看 | 国内精品久久毛片一区二区 | 久久精品人人做人人综合试看 | 欧美黑人性暴力猛交喷水 | 中文字幕中文有码在线 | 最近中文2019字幕第二页 | 亚洲熟妇色xxxxx欧美老妇y | 亚洲熟熟妇xxxx | 国产精品资源一区二区 | 丁香花在线影院观看在线播放 | 无码任你躁久久久久久久 | 乱码av麻豆丝袜熟女系列 | 野狼第一精品社区 | 水蜜桃av无码 | 精品无码一区二区三区的天堂 | 免费中文字幕日韩欧美 | 好男人www社区 | 成人试看120秒体验区 | 人妻体内射精一区二区三四 | 东京无码熟妇人妻av在线网址 | 日日摸日日碰夜夜爽av | 无码人妻精品一区二区三区不卡 | 欧美日韩久久久精品a片 | 老熟妇乱子伦牲交视频 | 午夜精品一区二区三区的区别 | 久久精品无码一区二区三区 | 人妻夜夜爽天天爽三区 | 国产一区二区三区影院 | 97夜夜澡人人双人人人喊 | 蜜臀aⅴ国产精品久久久国产老师 | 中文久久乱码一区二区 | 精品偷拍一区二区三区在线看 | 亚洲色在线无码国产精品不卡 | 欧美老妇交乱视频在线观看 | 国产免费久久久久久无码 | 麻豆人妻少妇精品无码专区 | 国产成人人人97超碰超爽8 | 人人妻人人澡人人爽欧美一区九九 | 99久久99久久免费精品蜜桃 | 久久综合激激的五月天 | 日日碰狠狠丁香久燥 | 午夜福利电影 | 亚洲精品一区二区三区大桥未久 | 国产莉萝无码av在线播放 | 国产精品.xx视频.xxtv | 精品久久8x国产免费观看 | а√天堂www在线天堂小说 | 亚洲欧美综合区丁香五月小说 | 日产精品高潮呻吟av久久 | 亚洲精品国偷拍自产在线观看蜜桃 | 丰满人妻翻云覆雨呻吟视频 | 中文字幕 人妻熟女 | 国产一区二区三区日韩精品 | 樱花草在线社区www | 人妻中文无码久热丝袜 | 久久精品人人做人人综合 | 中文字幕精品av一区二区五区 | 亚洲精品成a人在线观看 | 久久久久99精品成人片 | 狠狠色欧美亚洲狠狠色www | 色一情一乱一伦一区二区三欧美 | a国产一区二区免费入口 | 天海翼激烈高潮到腰振不止 | 欧美黑人性暴力猛交喷水 | 亚洲狠狠婷婷综合久久 | 无套内射视频囯产 | 亚洲日韩精品欧美一区二区 | 久久久久久a亚洲欧洲av冫 | 国产av一区二区精品久久凹凸 | 亚洲综合精品香蕉久久网 | 欧美xxxxx精品 | 亚洲一区二区三区偷拍女厕 | 欧美喷潮久久久xxxxx | 麻花豆传媒剧国产免费mv在线 | 最新版天堂资源中文官网 | 日韩av无码一区二区三区不卡 | 又大又硬又爽免费视频 | 中文字幕av伊人av无码av | 中文字幕无线码 | 玩弄少妇高潮ⅹxxxyw | 国产黄在线观看免费观看不卡 | 国产又爽又猛又粗的视频a片 | 国产午夜无码精品免费看 | 国产午夜亚洲精品不卡下载 | 最近中文2019字幕第二页 | 嫩b人妻精品一区二区三区 | 在线观看国产午夜福利片 | 国精产品一品二品国精品69xx | 亚洲自偷精品视频自拍 | 东京一本一道一二三区 | 欧美亚洲国产一区二区三区 | 男女下面进入的视频免费午夜 | 无遮挡国产高潮视频免费观看 | 日本熟妇乱子伦xxxx | 日本大香伊一区二区三区 | 亚洲一区av无码专区在线观看 | 国产性生大片免费观看性 | 亚洲自偷自拍另类第1页 | 国产激情无码一区二区app | 国产成人无码区免费内射一片色欲 | 亚洲人成影院在线无码按摩店 | 麻豆人妻少妇精品无码专区 | 黑人粗大猛烈进出高潮视频 | 精品人妻人人做人人爽夜夜爽 | 嫩b人妻精品一区二区三区 | 亚洲欧美国产精品专区久久 | 在线视频网站www色 | 国产69精品久久久久app下载 | 任你躁国产自任一区二区三区 | 丰满少妇弄高潮了www | 精品一二三区久久aaa片 | 999久久久国产精品消防器材 | 亚洲欧美中文字幕5发布 | 国产香蕉尹人综合在线观看 | 乱人伦人妻中文字幕无码久久网 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 永久免费观看美女裸体的网站 | 正在播放东北夫妻内射 | 国产亚洲精品久久久ai换 | 草草网站影院白丝内射 | 久久无码中文字幕免费影院蜜桃 | 少妇被粗大的猛进出69影院 | 久久亚洲中文字幕无码 | 在教室伦流澡到高潮hnp视频 | 双乳奶水饱满少妇呻吟 | 青草青草久热国产精品 | 亚洲a无码综合a国产av中文 | 亚洲乱码中文字幕在线 | 国产精品美女久久久久av爽李琼 | 亚洲大尺度无码无码专区 | 国产xxx69麻豆国语对白 | 日韩人妻无码一区二区三区久久99 | 人妻无码久久精品人妻 | 无码av中文字幕免费放 | 国产舌乚八伦偷品w中 | 亚洲成色www久久网站 | 国产精品久久久久7777 | 中文字幕精品av一区二区五区 | 国产成人无码av片在线观看不卡 | 日本xxxx色视频在线观看免费 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产欧美亚洲精品a | 国产高清不卡无码视频 | 麻豆av传媒蜜桃天美传媒 | 国产三级久久久精品麻豆三级 | 青草青草久热国产精品 | 亚洲乱亚洲乱妇50p | 国产无av码在线观看 | 久久99精品久久久久婷婷 | 日日摸日日碰夜夜爽av | 国产亚洲精品久久久久久大师 | 波多野结衣高清一区二区三区 | 国产成人无码av片在线观看不卡 | 麻豆国产人妻欲求不满谁演的 | 高潮毛片无遮挡高清免费 | 国产超碰人人爽人人做人人添 | 亚洲 另类 在线 欧美 制服 | 亚洲爆乳大丰满无码专区 | 野狼第一精品社区 | 日韩在线不卡免费视频一区 | 久久久www成人免费毛片 | 美女毛片一区二区三区四区 | 综合人妻久久一区二区精品 | 国产精品-区区久久久狼 | 国产情侣作爱视频免费观看 | 国产成人精品必看 | 自拍偷自拍亚洲精品10p | 亚洲成a人片在线观看无码3d | 国产精品久久久久久久9999 | 日本精品人妻无码77777 天堂一区人妻无码 | 99麻豆久久久国产精品免费 | 国产午夜无码视频在线观看 | 2020最新国产自产精品 | 天天爽夜夜爽夜夜爽 | 亚洲精品无码人妻无码 | 色欲av亚洲一区无码少妇 | 中文字幕无码av波多野吉衣 | 宝宝好涨水快流出来免费视频 | 亚洲成av人片在线观看无码不卡 | 亚洲色在线无码国产精品不卡 | 国产精品美女久久久 | 免费人成在线观看网站 | 久久久久久久久蜜桃 | 白嫩日本少妇做爰 | 日本一区二区三区免费播放 | 亚洲男女内射在线播放 | 在教室伦流澡到高潮hnp视频 | 亚洲国产精品久久久天堂 | 成人影院yy111111在线观看 | 黑人玩弄人妻中文在线 | 欧美成人家庭影院 | 4hu四虎永久在线观看 | 亚洲精品一区二区三区在线观看 | 久久99精品久久久久婷婷 | 亚洲精品一区二区三区在线观看 | 欧美国产日韩久久mv | 国内综合精品午夜久久资源 | а√天堂www在线天堂小说 | 少女韩国电视剧在线观看完整 | 国产精品无套呻吟在线 | 任你躁在线精品免费 | 性做久久久久久久久 | 国产精品高潮呻吟av久久4虎 | 国产成人综合美国十次 | 亚洲精品国产a久久久久久 | v一区无码内射国产 | 国产激情一区二区三区 | 一区二区三区高清视频一 | 久久 国产 尿 小便 嘘嘘 | 人妻互换免费中文字幕 | 国产精品久久久久久无码 | 精品偷拍一区二区三区在线看 | 青青久在线视频免费观看 | 久久国产劲爆∧v内射 | 无遮挡国产高潮视频免费观看 | 强伦人妻一区二区三区视频18 | 日韩人妻无码中文字幕视频 | 综合网日日天干夜夜久久 | 精品夜夜澡人妻无码av蜜桃 | 精品久久久中文字幕人妻 | 国产av一区二区精品久久凹凸 | 精品人妻中文字幕有码在线 | 思思久久99热只有频精品66 | 精品久久综合1区2区3区激情 | 欧美一区二区三区 | 东京热男人av天堂 | 亚洲 另类 在线 欧美 制服 | 97精品人妻一区二区三区香蕉 | 人人妻人人澡人人爽人人精品浪潮 | 东北女人啪啪对白 | 亚洲一区av无码专区在线观看 | 亚洲精品一区三区三区在线观看 | 亚洲乱亚洲乱妇50p | 国产精品久久久久久亚洲影视内衣 | 欧美日韩亚洲国产精品 | 久久aⅴ免费观看 | 久久www免费人成人片 | 国产精品无码永久免费888 | 大地资源中文第3页 | 国产超碰人人爽人人做人人添 | 在线天堂新版最新版在线8 | 成人片黄网站色大片免费观看 | 大乳丰满人妻中文字幕日本 | 色 综合 欧美 亚洲 国产 | 男女下面进入的视频免费午夜 | 久久 国产 尿 小便 嘘嘘 | 亚洲人成无码网www | 亚洲精品成a人在线观看 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲中文字幕乱码av波多ji | 日本丰满熟妇videos | 性开放的女人aaa片 | 国产麻豆精品精东影业av网站 | 免费观看又污又黄的网站 | 久久国产自偷自偷免费一区调 | 少妇高潮喷潮久久久影院 | 粗大的内捧猛烈进出视频 | 丝袜足控一区二区三区 | 99久久无码一区人妻 | 久久亚洲中文字幕无码 | 久久国内精品自在自线 | 久久精品国产大片免费观看 | 欧美 亚洲 国产 另类 | 亚洲中文无码av永久不收费 | 野外少妇愉情中文字幕 | 中文字幕无码av波多野吉衣 | 欧美兽交xxxx×视频 | 狠狠色欧美亚洲狠狠色www | 鲁鲁鲁爽爽爽在线视频观看 | 国产真实乱对白精彩久久 | 双乳奶水饱满少妇呻吟 | 人妻少妇精品无码专区二区 | 夜夜高潮次次欢爽av女 | 欧美日韩一区二区综合 | 亚洲精品国偷拍自产在线观看蜜桃 | 色综合视频一区二区三区 | 精品久久久久香蕉网 | a在线观看免费网站大全 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 亚洲日韩av片在线观看 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲狠狠婷婷综合久久 | 久久人人爽人人人人片 | 国产亚洲视频中文字幕97精品 | 亚洲国产精品成人久久蜜臀 | 人妻尝试又大又粗久久 | 亚洲七七久久桃花影院 | 少妇性l交大片欧洲热妇乱xxx | 久久精品中文闷骚内射 | 黑人粗大猛烈进出高潮视频 | 图片区 小说区 区 亚洲五月 | a在线观看免费网站大全 | 麻豆人妻少妇精品无码专区 | 国产猛烈高潮尖叫视频免费 | 丝袜足控一区二区三区 | 亚洲精品欧美二区三区中文字幕 | 丰满少妇女裸体bbw | 亚洲欧美精品aaaaaa片 | 一本无码人妻在中文字幕免费 | 久久久精品国产sm最大网站 | 成人无码精品1区2区3区免费看 | 欧美性色19p | 国产精品久久久 | 国产精品二区一区二区aⅴ污介绍 | 秋霞特色aa大片 | 青草青草久热国产精品 | 国产人妻精品一区二区三区不卡 | 国产人妻精品一区二区三区 | 夜精品a片一区二区三区无码白浆 | 少妇性l交大片欧洲热妇乱xxx | 欧美喷潮久久久xxxxx | 欧美日韩视频无码一区二区三 | 自拍偷自拍亚洲精品10p | 欧美放荡的少妇 | 久久综合网欧美色妞网 | 黑人玩弄人妻中文在线 | 永久免费观看美女裸体的网站 | 人人妻人人澡人人爽精品欧美 | 曰本女人与公拘交酡免费视频 | 免费看男女做好爽好硬视频 | 无遮挡国产高潮视频免费观看 | 午夜精品久久久久久久久 | 免费人成在线视频无码 | 精品国产一区二区三区av 性色 | 日本护士毛茸茸高潮 | 久久久久久久久888 | 亚洲国产精品一区二区第一页 | 5858s亚洲色大成网站www | 噜噜噜亚洲色成人网站 | 5858s亚洲色大成网站www | 高清无码午夜福利视频 | 国产精品久久久久久亚洲毛片 | 欧美午夜特黄aaaaaa片 | 日产精品99久久久久久 | 中文字幕无码免费久久99 | 麻豆国产丝袜白领秘书在线观看 | 精品熟女少妇av免费观看 | 亚洲一区二区三区无码久久 | 免费人成在线观看网站 | 亚洲色www成人永久网址 | 少妇久久久久久人妻无码 | 丰满护士巨好爽好大乳 | 欧美一区二区三区视频在线观看 | 久久国产精品_国产精品 | 国产成人无码av在线影院 | 成人免费无码大片a毛片 | 国产精品亚洲一区二区三区喷水 | 亚洲精品午夜国产va久久成人 | 风流少妇按摩来高潮 | 精品偷自拍另类在线观看 | 国语自产偷拍精品视频偷 | 亚洲综合色区中文字幕 | 日本一区二区三区免费高清 | 欧美日本精品一区二区三区 | 九九综合va免费看 | 精品一区二区三区无码免费视频 | 精品欧美一区二区三区久久久 | 精品乱子伦一区二区三区 | 99久久亚洲精品无码毛片 | 日日碰狠狠丁香久燥 | 国产内射爽爽大片视频社区在线 | 日韩精品久久久肉伦网站 | 亚洲国产成人a精品不卡在线 | 天天摸天天透天天添 | 特黄特色大片免费播放器图片 | 亚洲国产精品久久久久久 | 夜先锋av资源网站 | 乱人伦中文视频在线观看 | 图片区 小说区 区 亚洲五月 | 亚洲国产欧美日韩精品一区二区三区 | 亚洲人成人无码网www国产 | 色诱久久久久综合网ywww | 欧洲熟妇精品视频 | 亚洲爆乳大丰满无码专区 | 女人被男人躁得好爽免费视频 | 日韩视频 中文字幕 视频一区 | 欧美日本日韩 | 无码国产乱人伦偷精品视频 | 国产偷国产偷精品高清尤物 | 久久久久se色偷偷亚洲精品av | 日本www一道久久久免费榴莲 | 欧洲精品码一区二区三区免费看 | 亚洲中文字幕成人无码 | 无码纯肉视频在线观看 | 久久国产精品精品国产色婷婷 | 亚洲国产精品成人久久蜜臀 | 人人澡人人妻人人爽人人蜜桃 | 久久zyz资源站无码中文动漫 | 玩弄少妇高潮ⅹxxxyw | aa片在线观看视频在线播放 | 熟妇人妻无码xxx视频 | 丰满少妇人妻久久久久久 | 亚洲乱码日产精品bd | 99久久亚洲精品无码毛片 | 荫蒂添的好舒服视频囗交 | 日韩成人一区二区三区在线观看 | 亚洲精品欧美二区三区中文字幕 | 日本精品人妻无码77777 天堂一区人妻无码 | 亚洲aⅴ无码成人网站国产app | 亚洲啪av永久无码精品放毛片 | 免费观看又污又黄的网站 | 久久久久人妻一区精品色欧美 | 中文无码精品a∨在线观看不卡 | 人人澡人人妻人人爽人人蜜桃 | 中文字幕乱码人妻无码久久 | 亚洲中文字幕无码中文字在线 | www国产精品内射老师 | 天天拍夜夜添久久精品大 | 国产亚洲精品久久久久久久久动漫 | 51国偷自产一区二区三区 | 领导边摸边吃奶边做爽在线观看 | 亚洲欧洲日本无在线码 | 国产成人无码av片在线观看不卡 | 国产成人精品久久亚洲高清不卡 | 无码人妻久久一区二区三区不卡 | 性欧美牲交xxxxx视频 | 成人精品一区二区三区中文字幕 | 亚洲a无码综合a国产av中文 | 国产精品99爱免费视频 | 精品熟女少妇av免费观看 | 人妻天天爽夜夜爽一区二区 | 亚洲男人av香蕉爽爽爽爽 | 国产两女互慰高潮视频在线观看 | 亚洲欧美综合区丁香五月小说 | 无码av最新清无码专区吞精 | 亚拍精品一区二区三区探花 | 99久久久国产精品无码免费 | 国产精品亚洲五月天高清 | 亚洲欧美国产精品久久 | 国产精品高潮呻吟av久久 | 色婷婷久久一区二区三区麻豆 | 中文字幕无码av激情不卡 | 亚洲一区二区三区含羞草 | 成人欧美一区二区三区黑人免费 | 精品无码国产自产拍在线观看蜜 | 性啪啪chinese东北女人 | 精品成在人线av无码免费看 | 久久亚洲精品成人无码 | 在线成人www免费观看视频 | 日本饥渴人妻欲求不满 | 国产又粗又硬又大爽黄老大爷视 | 77777熟女视频在线观看 а天堂中文在线官网 | 亚洲成av人片在线观看无码不卡 | 国产成人无码av片在线观看不卡 | 欧洲美熟女乱又伦 | 日本精品人妻无码77777 天堂一区人妻无码 | 精品人妻中文字幕有码在线 | 国产精品va在线观看无码 | 亚洲国产精品成人久久蜜臀 | 亚洲人成网站免费播放 | 欧美自拍另类欧美综合图片区 | 99国产精品白浆在线观看免费 | 无套内谢老熟女 | 性色欲网站人妻丰满中文久久不卡 | 性欧美牲交xxxxx视频 | 久久久久久av无码免费看大片 | 天天躁夜夜躁狠狠是什么心态 | 欧美日韩视频无码一区二区三 | 日韩视频 中文字幕 视频一区 | 日韩欧美群交p片內射中文 | 亚洲码国产精品高潮在线 | 免费无码一区二区三区蜜桃大 | 亚洲中文字幕在线无码一区二区 | 精品无人区无码乱码毛片国产 | 亚洲熟妇色xxxxx欧美老妇y | 强辱丰满人妻hd中文字幕 | 精品aⅴ一区二区三区 | www国产亚洲精品久久久日本 | 又大又硬又黄的免费视频 | 亚洲aⅴ无码成人网站国产app | 久久久www成人免费毛片 | 无码帝国www无码专区色综合 | 国产女主播喷水视频在线观看 | 人妻中文无码久热丝袜 | 亚洲精品www久久久 | 亚洲精品国产精品乱码不卡 | 无遮挡啪啪摇乳动态图 | 久久综合网欧美色妞网 | yw尤物av无码国产在线观看 | 亚洲高清偷拍一区二区三区 | 日本熟妇大屁股人妻 | 内射老妇bbwx0c0ck | 人人妻人人澡人人爽欧美一区 | 麻豆人妻少妇精品无码专区 | 亚洲日韩av一区二区三区四区 | 激情人妻另类人妻伦 | 国产特级毛片aaaaaaa高清 | 免费无码av一区二区 | 鲁鲁鲁爽爽爽在线视频观看 | 国产无遮挡又黄又爽又色 | 成人欧美一区二区三区 | 国产麻豆精品一区二区三区v视界 | 野外少妇愉情中文字幕 | 一个人看的www免费视频在线观看 | 人人爽人人澡人人人妻 | 老熟女重囗味hdxx69 | 国产在线aaa片一区二区99 | 好男人www社区 | 国产人妻精品一区二区三区 | 精品国产麻豆免费人成网站 | 性色欲网站人妻丰满中文久久不卡 | 久久久久se色偷偷亚洲精品av | 精品国产一区二区三区四区在线看 | 亚洲色大成网站www国产 | 亚洲精品午夜国产va久久成人 | 亚洲精品一区二区三区在线 | 亚洲精品国产品国语在线观看 | 无人区乱码一区二区三区 | 一本一道久久综合久久 | 伊人色综合久久天天小片 | 久久精品中文闷骚内射 | 国产日产欧产精品精品app | 成人亚洲精品久久久久 | 丰满人妻翻云覆雨呻吟视频 | 欧美成人午夜精品久久久 | 久久久婷婷五月亚洲97号色 | 亚洲伊人久久精品影院 | 国精产品一区二区三区 | 成人av无码一区二区三区 | 精品少妇爆乳无码av无码专区 | 国产精品丝袜黑色高跟鞋 | 性欧美熟妇videofreesex | 成人免费视频一区二区 | 国产在热线精品视频 | 4hu四虎永久在线观看 | 中文字幕无码人妻少妇免费 | 无码av岛国片在线播放 | 免费观看黄网站 | 久激情内射婷内射蜜桃人妖 | 国产成人综合在线女婷五月99播放 | 久久熟妇人妻午夜寂寞影院 | 无码中文字幕色专区 | 国产精品久久久午夜夜伦鲁鲁 | 久久这里只有精品视频9 | 又粗又大又硬又长又爽 | 欧美 亚洲 国产 另类 | 亚洲日韩av一区二区三区四区 | 亚洲日韩av一区二区三区中文 | 奇米影视7777久久精品人人爽 | 中文字幕无码av波多野吉衣 | 亚洲精品欧美二区三区中文字幕 | 牲欲强的熟妇农村老妇女 | 久久综合九色综合97网 | 亚洲成av人影院在线观看 | 日韩亚洲欧美中文高清在线 | 成 人影片 免费观看 | 人妻少妇精品无码专区动漫 | 亚洲国产精品一区二区美利坚 | 2019nv天堂香蕉在线观看 | 无码av最新清无码专区吞精 | 亚洲精品成人福利网站 | 初尝人妻少妇中文字幕 | 一本久道久久综合狠狠爱 | 免费中文字幕日韩欧美 | 亚洲色www成人永久网址 | 精品无人区无码乱码毛片国产 | 一区二区三区乱码在线 | 欧洲 | 免费网站看v片在线18禁无码 | 中文字幕无线码免费人妻 | 亚洲人亚洲人成电影网站色 | 日本精品高清一区二区 | 妺妺窝人体色www在线小说 | 乌克兰少妇性做爰 | 亚洲色欲色欲天天天www | 午夜精品一区二区三区在线观看 | 日韩人妻少妇一区二区三区 | 丰满肥臀大屁股熟妇激情视频 | 我要看www免费看插插视频 | 精品国产一区二区三区四区 | 两性色午夜视频免费播放 | 国产精品沙发午睡系列 | 丰满少妇弄高潮了www | 色婷婷综合中文久久一本 | 亚洲日韩精品欧美一区二区 | 三上悠亚人妻中文字幕在线 | 亚洲综合无码一区二区三区 | 久久久国产精品无码免费专区 | 久久婷婷五月综合色国产香蕉 | 一区二区传媒有限公司 | 中文字幕无线码 | 3d动漫精品啪啪一区二区中 | 中文久久乱码一区二区 | 久久久亚洲欧洲日产国码αv | 日本丰满护士爆乳xxxx | 人人澡人人透人人爽 | 亚洲の无码国产の无码步美 | 久久天天躁夜夜躁狠狠 | a在线观看免费网站大全 | 成人欧美一区二区三区黑人免费 | 日韩无码专区 | 天天躁夜夜躁狠狠是什么心态 | 天干天干啦夜天干天2017 | 亚洲精品综合一区二区三区在线 | 无码国产乱人伦偷精品视频 | 国产精品高潮呻吟av久久4虎 | 国产精品亚洲综合色区韩国 | 国产精品久久久一区二区三区 | 精品人人妻人人澡人人爽人人 | 好爽又高潮了毛片免费下载 | 亚洲成a人片在线观看无码 | 乌克兰少妇性做爰 | 日韩人妻少妇一区二区三区 | 亚洲精品国产精品乱码不卡 | 少妇性l交大片欧洲热妇乱xxx | 日日鲁鲁鲁夜夜爽爽狠狠 | 日韩欧美成人免费观看 | 国产激情无码一区二区app | 国产精品久免费的黄网站 | 一本大道伊人av久久综合 | 国产精品亚洲专区无码不卡 | 在线视频网站www色 | 亚洲国产精品一区二区美利坚 | 亚洲大尺度无码无码专区 | a在线亚洲男人的天堂 | 国产黄在线观看免费观看不卡 | 久久这里只有精品视频9 | 影音先锋中文字幕无码 | 全球成人中文在线 | 精品久久综合1区2区3区激情 | 精品人人妻人人澡人人爽人人 | 国内综合精品午夜久久资源 | 中文毛片无遮挡高清免费 | 久久综合色之久久综合 | 欧美日韩一区二区综合 | 久久zyz资源站无码中文动漫 | 精品成在人线av无码免费看 | 国产精品高潮呻吟av久久4虎 | 国产va免费精品观看 | 国产无套粉嫩白浆在线 | 精品人妻人人做人人爽夜夜爽 | 狂野欧美性猛交免费视频 | 超碰97人人射妻 | 中文亚洲成a人片在线观看 | 蜜臀aⅴ国产精品久久久国产老师 | 免费中文字幕日韩欧美 | 色婷婷综合激情综在线播放 | 乱人伦人妻中文字幕无码 | 国产一区二区三区日韩精品 | 国产精品香蕉在线观看 | 国内精品久久毛片一区二区 | 国产艳妇av在线观看果冻传媒 | 亚洲 欧美 激情 小说 另类 | 一本无码人妻在中文字幕免费 | 少妇邻居内射在线 | 亚洲精品国偷拍自产在线麻豆 | 久久久av男人的天堂 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 乱中年女人伦av三区 | 国产精品久久国产精品99 | 国产成人精品一区二区在线小狼 | 亚洲欧洲中文日韩av乱码 | 日本免费一区二区三区最新 | 亚洲熟妇色xxxxx欧美老妇y | 日韩少妇内射免费播放 | 亚洲国产综合无码一区 | 一二三四在线观看免费视频 | 任你躁在线精品免费 | 国产精品久久国产精品99 | 无码人妻少妇伦在线电影 | 日韩精品成人一区二区三区 | 无码毛片视频一区二区本码 | 亚洲s码欧洲m码国产av | 国产午夜福利100集发布 | 99久久久无码国产aaa精品 | 国产人妻精品一区二区三区 | 精品无人区无码乱码毛片国产 | 国产性生大片免费观看性 | 欧美熟妇另类久久久久久多毛 | 欧美喷潮久久久xxxxx | 久久无码专区国产精品s | 六月丁香婷婷色狠狠久久 | 精品国产一区二区三区四区 | 亚洲欧洲无卡二区视頻 | 亚洲精品国产品国语在线观看 | 亚洲精品午夜无码电影网 | 免费无码一区二区三区蜜桃大 | 亚洲日韩精品欧美一区二区 | 在线观看欧美一区二区三区 | 久9re热视频这里只有精品 | 乱人伦人妻中文字幕无码久久网 | 搡女人真爽免费视频大全 | 女人被男人躁得好爽免费视频 | 国内揄拍国内精品人妻 | 十八禁视频网站在线观看 | 欧美成人午夜精品久久久 | 麻豆蜜桃av蜜臀av色欲av | 国产精品美女久久久网av | 丝袜人妻一区二区三区 | 国产精品多人p群无码 | 无码精品人妻一区二区三区av | 久久久久人妻一区精品色欧美 | 亚洲综合色区中文字幕 | 97色伦图片97综合影院 | 久久精品中文字幕一区 | 中文无码精品a∨在线观看不卡 | 国产欧美熟妇另类久久久 | 国产成人精品三级麻豆 |