【正文】
不同的語(yǔ)言,那么我們就會(huì)發(fā)覺(jué)一個(gè)有些不同的世界”。 無(wú)論 C++還是 Java 都屬于 雜合語(yǔ)言 。雜合語(yǔ)言允許采用多種編程風(fēng)格;之所以說(shuō) C++是一種雜合語(yǔ)言,是因?yàn)樗С峙c C語(yǔ)言的向后兼容能力。 Java 語(yǔ)言首先便假定了我們只希望進(jìn)行面向?qū)ο蟮某绦蛟O(shè)計(jì)。只有做好這個(gè)準(zhǔn)備工作,與其他 OOP 語(yǔ)言相比,才能體會(huì)到 Java 的易學(xué)易用。 用 句柄操縱對(duì)象 每種編程語(yǔ)言都有自己的數(shù)據(jù)處理方式。您曾利用一些特殊語(yǔ)法直接操作過(guò)對(duì)象,或處理過(guò)一些間接表示的對(duì)象嗎( C或 C++里的指針)? 所有這些在 Java 里都得到了簡(jiǎn)化,任何東西都可看作對(duì)象。但要注意,盡管一切都“看作”對(duì)象,但操縱的標(biāo)識(shí)符實(shí)際是指向一個(gè)對(duì)象的“句柄”( Handle)??蓪⑦@一情形想象成用遙控板(句柄)操縱電視機(jī)(對(duì)象)。但一旦需要“換頻道”或者“關(guān)小聲音”,我們實(shí)際操縱的是遙控板(句柄),再有遙控板自己操縱電視機(jī)(對(duì)象)。 此外,即使沒(méi)有電視機(jī),遙控板亦可獨(dú)立存在。所以如果想容納一個(gè)詞或句子,可創(chuàng)建一個(gè) String 句柄 : String s。若此時(shí)向 s 發(fā)送一條消息,就會(huì)獲得一個(gè)錯(cuò)誤(運(yùn)行期)。因此,一種更安全的做法是:創(chuàng)建一個(gè)句柄時(shí),記住無(wú)論如何都進(jìn)行初始化: String s = “ zyp” 。通常,必須為對(duì)象使用一種更通用的初始化 方法 。通常用 new 關(guān)鍵字達(dá)到這一目標(biāo)。所以在上面的例子中,可以說(shuō): String s = new String(“ asdf” )。 當(dāng)然,字串( String)并非唯一的類型。對(duì)我們來(lái)講,最重要的就是記住能自行創(chuàng)建類型。 存儲(chǔ) 到什么地方 程序運(yùn)行時(shí),我們最好對(duì)數(shù)據(jù)保存到什么地方做到心中有數(shù)。有六個(gè)地方都可以保存數(shù)據(jù): ( 1)寄存器。然而,寄存器的數(shù)量十分有限,所以寄存器是根據(jù)需要由編譯器分配。 ( 2)堆棧。堆棧指針若向下移 , 會(huì)創(chuàng)建新的內(nèi)存;若向上移,則會(huì)釋放那些內(nèi)存。創(chuàng)建程序時(shí), Java 編譯器必須準(zhǔn)確地知道堆棧內(nèi)保存的所有數(shù)據(jù)的“長(zhǎng)度”以及“存在時(shí)間”。這一限制無(wú)疑影響了程序的靈活性,所以盡管有些Java 數(shù)據(jù)要保存在堆棧里 —— 特別是對(duì)象句柄,但 Java 對(duì)象并不 存儲(chǔ)于 其中。一種 通用 的內(nèi)存池(也在 RAM 區(qū)域), 用于存放所 有的 Java 對(duì)象。因此,用堆保存數(shù)據(jù)時(shí)會(huì)得到更大的靈活性。執(zhí)行這些代碼時(shí),會(huì)在堆里自動(dòng)進(jìn)行數(shù)據(jù)的保存。 ( 4) 常量 存儲(chǔ)。這樣做是安全的,因?yàn)?他們永遠(yuǎn)都不會(huì)改變。 ( 5)非 RAM 存儲(chǔ)。其中兩個(gè)最重 要的例子便是“ 流式對(duì)象”和“持久化對(duì)象”。而對(duì)于持久化 對(duì)象,對(duì)象保存在磁盤中。對(duì)于這些類型的數(shù)據(jù)存儲(chǔ),一個(gè)特別有用的技巧就是它們能存在于其他媒體中。 Java 提供了對(duì) 輕量級(jí)持久化的支持,而諸如 JDBC 和 Hibernate 這樣的機(jī)制提供了更加復(fù)雜的對(duì)數(shù)據(jù)庫(kù)中存儲(chǔ)和讀取對(duì)象信息的支持。可以把他們想像成“基本”類型。因此,對(duì)于這些類型, Java 采取與 C 和 C++相同的方法。這個(gè)變量直接存儲(chǔ)“值”,并置于堆棧中 ,因此更加高效。他們的大小并不像其他大多數(shù)語(yǔ)言那樣隨機(jī)器硬件架構(gòu)的變化而變化。 所有數(shù)值類型都有正負(fù)號(hào),所以不要去尋找無(wú)符號(hào)的數(shù)值類型?;绢愋途哂械陌b器類,使得可以在堆中創(chuàng)建一個(gè)非基本對(duì)象,用來(lái)表示對(duì)應(yīng)的基本類型。 Character ch = new Character(c)。 包裝基本類型的原因?qū)⒃谝院蟮恼鹿?jié)中說(shuō)明。雖然它們大體上屬于“包裝器類”的范疇,但二者都沒(méi)有對(duì)應(yīng)的基本類型。也就是說(shuō),能作用于 int 或 float 的操作,也能作用于 BigInteger 或 Big Decimal。由于這么做復(fù)雜了許多,所以運(yùn)算速度會(huì)比較慢。 BigInteger 支持任意精度的整數(shù)。 BigDecimal 支持任何精度的定點(diǎn)數(shù),例如,可以用它進(jìn)行精確的貨幣計(jì)算。 Java 中的數(shù)組 幾乎所有的程序設(shè)計(jì)語(yǔ)言都支持?jǐn)?shù)組。如果一個(gè)程序要訪 問(wèn)其自身內(nèi)存塊之外的數(shù)組,或在數(shù)組初始化前使用內(nèi)存(程序中常見(jiàn)的錯(cuò)誤),都會(huì)產(chǎn)生難以預(yù)料的后果。 Java 確保數(shù)組會(huì)被初始化,而且不能在它的范圍之外被訪問(wèn)。但由此換來(lái)的是安全性和效率的提高,因此付出的代價(jià)是值得的(并且 Java 有時(shí)可以優(yōu)化這些操作)。一旦 Java 看到 null,就知道這個(gè)引用還沒(méi)有指向某個(gè)對(duì)象。因此,常犯的數(shù)組錯(cuò)誤在 Java 中就可以避免。同樣,編譯器也能確保這種數(shù)組的初始化,因?yàn)樗鼤?huì)將這種數(shù)組所占的內(nèi)存全部置零。 永遠(yuǎn)不需要銷毀對(duì)象 在大多數(shù)程序設(shè)計(jì)語(yǔ)言中,變量生命周期的概念,占據(jù)了程序設(shè)計(jì)工作中非常重要的 部分 。 作用域 大多數(shù) 過(guò)程型語(yǔ)言都有作用域( scope)的概念。在 C、 C++和 Java 中,作用域由花括號(hào)的位置決定。 //Only x available { int q = 96。 q available } //only x available //q is “ out of scope” } 在作用域里定義的變量只可用于作用域結(jié)束之前。 縮排格式使 Java 代碼更易于閱讀。 盡管 以下 代碼在 C和 C++中是合法的,但是在 Java 中卻不能這樣寫: { int x = 12。 //illegal } } 編譯器將會(huì)報(bào)告變量 x已經(jīng)定義過(guò)。因?yàn)?Java 設(shè)計(jì)者認(rèn)為這樣做會(huì)導(dǎo)致程序混亂。當(dāng)用 new 創(chuàng)建一個(gè) Java 對(duì)象時(shí),它可以 存活于作用域之外。 // End of scope } 引用 s在作用域終點(diǎn)就消失了。在這一 小段代碼中,我們無(wú)法在這個(gè)作用域之后 訪問(wèn)這個(gè)對(duì)象,因?yàn)?對(duì)它唯一的引用已超出了作用域的范圍。 事實(shí) 證明,由 new 創(chuàng)建的對(duì)象,只要你需要,就會(huì)一直保留下去。在 C++中,你不僅必須要確保對(duì)象的保留時(shí)間與你需要這些對(duì)象的時(shí)間一樣長(zhǎng),而且還必須在你使用完它們之后,將其銷毀。如果 Java 讓對(duì)象繼續(xù)存在,那么靠什么才能防止這些對(duì)象填滿內(nèi)存空間,進(jìn)而阻塞你的程序呢?這正是 C++里可能會(huì)發(fā)生的問(wèn)題。 Java 有一個(gè)垃圾回收器,用來(lái)監(jiān)視用 new 創(chuàng)建的所有對(duì)象,并辨別那些不會(huì)再被引用的對(duì)象。也就是說(shuō),你根本不必?fù)?dān)心內(nèi)存回收的問(wèn)題。這樣做就消除了這類編程問(wèn)題(即“內(nèi)存泄漏”), 這是由于程序員忘記釋放內(nèi)存而產(chǎn)生的問(wèn)題。然而,從歷史發(fā)展角度來(lái)看,大多數(shù)面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言習(xí)慣用關(guān)鍵字 class來(lái)表示 “我準(zhǔn)備告訴你一種新類型的對(duì)象看起來(lái)像什么樣子”。例如: class ATypeName {/*Class body goes here*/} 這就引入了一種新的類型,盡管類主體僅包含一條注釋語(yǔ)句(星號(hào)和斜杠以及其中的內(nèi)容就是注釋,本章后面再討論)。然而,你已經(jīng)可以用 new來(lái)創(chuàng)建這種類型的對(duì)象 。 但是,在定義它的所有方法之前,還沒(méi)有辦法能讓它去做更多的事情(也就是說(shuō),不能向它發(fā)送任何有意義的消息)。 字段可以是任何類型的對(duì)象,可以通過(guò)其引用與其進(jìn)行通信;也可以是基本類型中的一種。 每個(gè)對(duì)象都有用來(lái)存儲(chǔ)其字段的空間;普通字段不能在對(duì)象間共享。 double d。 } 盡管這個(gè)類除了存儲(chǔ)數(shù)據(jù)之外什么也不能做,但是仍舊可以像下面這樣創(chuàng)建它的一個(gè)對(duì)象; DataOnly data = new DataOnly()。具體的實(shí)現(xiàn)為 :在對(duì)象引用的名稱之后緊接著一個(gè)句點(diǎn),然 后再接著是對(duì)象內(nèi)部的成員名稱: 例如: = 47。 = false。在這種情況下,只需要再使用連接句點(diǎn)即可。 DataOnly 類除了保存數(shù)據(jù)外沒(méi)別的用處,因?yàn)樗鼪](méi)有任何成員方法。 基本成員默認(rèn)值 若累的某個(gè) 成員是基本數(shù)據(jù)類型,即使沒(méi)有進(jìn)行初始化, Java 也會(huì)確保它獲得一個(gè)默認(rèn)值, 當(dāng)變量作為類的成員使用時(shí), Java 才確保給定其默認(rèn)值,以確保那些是基本類型的成員變量得到初始化 ( C++沒(méi)有 此功能 ) ,防止產(chǎn)生程序錯(cuò)誤。所以最好明確地對(duì)變量進(jìn)行初始化。因此如果在某個(gè)方法定義中有 int x。所以在使用 x前,應(yīng) 先對(duì)其賦一個(gè)適當(dāng)?shù)闹?。告訴你此變量沒(méi)有初始化,這正是 Java 優(yōu)于 C++的地方。 方法、參數(shù)和返回值 許多程序設(shè)計(jì)語(yǔ)言(像 C 和 C++)用函數(shù)這個(gè)術(shù)語(yǔ)來(lái)描述命名子程序;而在 Java 里卻常用方法這個(gè)術(shù)語(yǔ)來(lái)表示“做某些事情的方式”。盡管這只是用詞上的差別,但本書將沿用 Java 的慣用法,即用術(shù)語(yǔ)“方法”而不是“函數(shù)”來(lái)描述。方法的基本組成部分包括:名稱、參數(shù)、返回值和方法體。參數(shù)列表給出了要傳遞方法的信息的類型和名稱。 Java 中的方法只能作為類的一部分來(lái)創(chuàng)建。如果試圖在某個(gè)對(duì)象上調(diào)用 它并不具備的方法,那么在編譯時(shí)就會(huì)得到一條錯(cuò)誤消息。如: (arg1,arg2,arg3)。如果有個(gè)名為 a 的對(duì)象,可以通過(guò)它調(diào)用 f(),那么就可以這樣寫: int x = ()。 這種調(diào)用方法的行為通常被稱為發(fā)送消息給對(duì)象。面向?qū)ο蟮某绦蛟O(shè)計(jì)通常簡(jiǎn)單 地歸納為“向?qū)ο蟀l(fā)送消息”。正如你可能料想的那樣,這些信息像 Java 中的其他信息一樣,采用的都是對(duì)象形式。像 Java 中任何傳遞對(duì)象的場(chǎng)合一樣,這里傳遞的實(shí)際上也是引用,并且引用類型必須正確。 假設(shè)某個(gè)方法接受 String 為其參數(shù),下面是其具體定義,它必須置于某個(gè)類的定義內(nèi)才能被正確編譯。 } 此方法告訴你,需要多少個(gè)字節(jié) 才能容納一個(gè)特定的 String 對(duì)象中的信息( 字符串的每個(gè)字符的尺寸都是 16 位或 2 個(gè)字節(jié),以此來(lái)提供對(duì) Unicode 字符集的支持 ) 。一旦將 s 傳遞給此方法,就可以把他當(dāng)作其他對(duì)象一樣進(jìn)行處理(可以給它傳遞消息)。 通過(guò)上面的例子,還可以了解到 return 關(guān)鍵 字的用法,它包括兩方面: 首先,它代表“已經(jīng)做完,離開(kāi)此方法”。在這個(gè)例子中,返回值是通過(guò)計(jì)算 ()*2 這個(gè)表達(dá)式得到的。下面是一些例子: boolean flag(){return true。} void nothing(){return。因此,沒(méi)有必要到方法結(jié)束時(shí)才離開(kāi),可在任何地方返回。 到此為止,讀者或許覺(jué)得:程序視乎只是一系列帶有方法的對(duì)象組合,這些方法以其他對(duì)象為參數(shù),并發(fā)送消息給其他對(duì)象。至于本章,讀者只需要理解消息發(fā)送就足夠了。 the reason C++ is hybrid is to support backward patibility with the C language. Because C++ is a superset of the C language, it includes many of that language?s undesirable features, which can make some aspects of C++ overly plicated. The Java language assumes that you want to do only objectoriented programming. This means that before you can begin you must shift your mindset into an objectoriented world (unless it?s already there). The benefit of this initial effort is the ability to program in a language that is simpler to learn and to use than many other OOP languages. In this chapter you?ll see the basic ponents of a Java program and learn that (almost) everything in Java is an object. You manipulate objects with references Each programming language has its own means of manipulating elements in memory. Sometimes the programmer must be constantly aware of what type of manipulation is going on. Are you manipulating the element directly, or are you dealing with some kind of indirect representation (a pointer in C or C++) that must be treated with a special syntax? All this is simplified in Java. You treat everything as an object, using a single consistent syntax. Although you treat everything as an object, the identifier you manipulate is actually a “reference” to an object. You might imagine a televisio