Thinking in Java 第二章
[第二章 萬(wàn)物皆對(duì)象] Java 由C 而來(lái),但Java 是比C 更純粹的OOP 。在Java 中,萬(wàn)物皆對(duì)象!2.1 用引用操縱對(duì)象不同語(yǔ)言有不同操縱內(nèi)存中元素的方式??梢灾苯?,也可以間接(
[第二章 萬(wàn)物皆對(duì)象] Java 由C 而來(lái),但Java 是比C 更純粹的OOP 。在Java 中,萬(wàn)物皆對(duì)象!
2.1 用引用操縱對(duì)象
不同語(yǔ)言有不同操縱內(nèi)存中元素的方式??梢灾苯?,也可以間接(如C 與C 中的指針)。
在Java 中,對(duì)象標(biāo)示符其實(shí)是內(nèi)存中真實(shí)對(duì)象的reference (引用,有些書稱其為Handle 句柄)。
可以僅創(chuàng)建一個(gè)引用,而不一定必須有一個(gè)對(duì)象與它關(guān)聯(lián)。比如:String s ; 僅這句代碼并沒有對(duì)象被創(chuàng)建,創(chuàng)建的只是一個(gè)引用。如果這時(shí)向s 發(fā)送一個(gè)消息,就會(huì)返回一個(gè)運(yùn)行時(shí)錯(cuò)誤,因?yàn)檫@時(shí)s 并沒有和任何對(duì)象相關(guān)聯(lián)。
2.2 必須由來(lái)你創(chuàng)建所有對(duì)象
1. 存儲(chǔ)數(shù)據(jù)的5種方式:
1)寄存器(Registers ):CPU 里,最快,控件最小。不能直接控制,也不能在程序中感覺到寄存器存在的任何跡象。(c 和c 允許編程者向編譯器建議寄存器的分配方式??)
2)堆棧(The stack):在RAM (內(nèi)存)中。但通過(guò)棧指針(stack pointer)可以從處理器獲得直接支持。棧指針向下移動(dòng),則分配新內(nèi)存;若向上移動(dòng),則釋放內(nèi)存。這種方式快速有效,僅次于使用寄存器。但,創(chuàng)建程序時(shí),Java 系統(tǒng)必須知道存儲(chǔ)在棧內(nèi)的所有項(xiàng)的確切生命周期,以便上下移動(dòng)棧指針。這一約束限制了程序的靈活性。所以,雖然某些Java 數(shù)據(jù)存儲(chǔ)在棧內(nèi)——特別是對(duì)象引用(object references),但Java 對(duì)象本身并不存儲(chǔ)在棧中。
3)堆(The heap):一種存放所有Java 對(duì)象的通用內(nèi)存池(a general-purpose pool of memory ),也位于RAM 中。
Heap 不同于stack 的好處是,編譯器不需要知道存儲(chǔ)的數(shù)據(jù)在heap 中存活多長(zhǎng)時(shí)間(不用手動(dòng)編寫代碼去清空)。所以,使用heap 靈活性大。用關(guān)鍵字new ,執(zhí)行時(shí),就會(huì)自動(dòng)在heap 中分配內(nèi)存空間。缺點(diǎn):比stack 處理速度慢。
4)常量存儲(chǔ)(constant storage):常量值通常直接存放在程序代碼內(nèi)部,這樣是安全的,因?yàn)樗麄冇肋h(yuǎn)不會(huì)被改變。有時(shí),在嵌入式系統(tǒng)中(embedded systems),常量會(huì)獨(dú)立于其他部分,這時(shí),可選擇將其放在ROM (只讀存儲(chǔ)器)中。
5)非RAM 存儲(chǔ):如果數(shù)據(jù)完全獨(dú)立于程序之外,它不受程序的任何控制,程序不運(yùn)行時(shí)它也存在。兩個(gè)基本例子:流對(duì)象(streamed objects)和持久化對(duì)象(persistent object)。 流對(duì)象:對(duì)象轉(zhuǎn)化為字節(jié)流(streams of bytes),通常被發(fā)送到另一臺(tái)機(jī)器。
持久化對(duì)象:對(duì)象被存放于磁盤上,程序終止時(shí),它們?nèi)钥梢员3肿约涸鹊臓顟B(tài)。
,非RAM 存儲(chǔ),可以 把對(duì)象轉(zhuǎn)化成可以存放在其他媒介上的事物,在需要時(shí),可恢復(fù)成常規(guī)的、基于RAM 的對(duì)象。Java 提供了對(duì)輕量級(jí)持久化(lightweight persistence)的支持,而JDBC 和Hibernate 這樣的機(jī)制提供了更加復(fù)雜的對(duì)在數(shù)據(jù)庫(kù)中存儲(chǔ)和讀取對(duì)象信息的支持。
2. 特例:基本類型(primitive types)
New 關(guān)鍵字將對(duì)象放入heap 中,而小的,簡(jiǎn)單的變量——Java 中的基本類型,會(huì)像C 與C 的處理方式相同,直接將其放入stack 中?;绢愋偷淖兞坎皇褂胣ew 關(guān)鍵字,創(chuàng)建時(shí),直接生成一個(gè)非引用的“自動(dòng)”變量(automatic variable),直接來(lái)存儲(chǔ)“值”,由于在stack 中,所以更加高效。
Java 基本類型占空間大小,不會(huì)隨機(jī)器硬件架構(gòu)的變化而改動(dòng),這也增強(qiáng)了Java 程序的可移植性。

注意:Java 中沒有無(wú)符號(hào)類型。
基本類型具有包裝器類(Wrapper type),可以在堆中創(chuàng)建一個(gè)非基本對(duì)象(non-primitive object ),用來(lái)表示對(duì)應(yīng)的基本類型。
例如:char c = ‘x ’ ;
Character ch = new Character(c) ;
或者:Character ch = new Character( ‘x ’ ) ;
Java SE5的自動(dòng)包裝功能可以自動(dòng)將基本類型轉(zhuǎn)換為包裝器類型:
Character ch = ‘x ’ ;
并可以反向轉(zhuǎn)換:
char c = ch ;
高精度數(shù)字:
,Java 提供兩個(gè)用于高精度計(jì)算的類:BigInteger 和BigDecimal 。雖然它們大體屬于“包裝器類”的范疇,但二者都沒有對(duì)應(yīng)的基本類型。
BigInteger 支持任意精度整數(shù),即在運(yùn)算中,可以準(zhǔn)確表示任何大小整數(shù)值,而不會(huì)丟失任何信息。
BigDecimal 支持任何精度的定點(diǎn)數(shù),可以用它進(jìn)行精確的貨幣計(jì)算。
3. Java中的數(shù)組
C 和C 中使用數(shù)組危險(xiǎn),因?yàn)樗鼈兊臄?shù)組就是內(nèi)存塊(arrays are only blocks of memory)??(我記得c 中,數(shù)組和數(shù)組中的數(shù)據(jù)也應(yīng)該是分別存在stack 和heap 中,這里的內(nèi)存塊指的是什么??)
如果一個(gè)程序要訪問(wèn)其自身內(nèi)存塊之外的數(shù)組,或在數(shù)組初始化之前使用內(nèi)存(程序中常見的錯(cuò)誤),就會(huì)產(chǎn)生難以預(yù)料的后果。
Java 會(huì)確保數(shù)組會(huì)被初始化,而且不能在它的范圍外被訪問(wèn)。這種范圍檢查,以每個(gè)數(shù)組上少量的內(nèi)存開銷、以及運(yùn)行時(shí)的下標(biāo)檢查為代價(jià)。但換來(lái)了安全性和效率的提高。
Java 處理數(shù)組的原理:
創(chuàng)建數(shù)組對(duì)象時(shí),實(shí)際上創(chuàng)建了一個(gè)引用數(shù)組(an array of references),并且每個(gè)引用都會(huì)自動(dòng)被初始化為一個(gè)特定值,該值擁有自己的關(guān)鍵字null 。一旦java 看見null ,就知道這個(gè)引用還沒有指向某個(gè)對(duì)象。在使用任何以用之前,必須為其指定一個(gè)對(duì)象,如果試圖使用一個(gè)還是null 的引用,在運(yùn)行時(shí)就會(huì)報(bào)錯(cuò)。
而對(duì)于用來(lái)存放基本數(shù)據(jù)類型的數(shù)組,編譯器也能確保這種數(shù)組的初始化,它會(huì)將這種數(shù)組所占的內(nèi)存全部置零。
2.3 永遠(yuǎn)不要銷毀對(duì)象
1 .作用域(scope )
Java 和C/C 一樣,用{}表示作用域。
2. 對(duì)象的作用域
對(duì)象和基本類型生命周期不同,它可以存活于scope 之外。
由new 創(chuàng)建的對(duì)象,只要你需要,就會(huì)一直保留下去。對(duì)象的銷毀由Java 中的垃圾回收器(garbage collector),它會(huì)監(jiān)視用new 創(chuàng)建的所有對(duì)象,并辨別那些不會(huì)再被引用的對(duì)象。隨后,釋放這些對(duì)象的內(nèi)存空間,以便供其他新對(duì)象使用。
2.4 創(chuàng)建新的數(shù)據(jù)類型:類
1. 字段和方法(Fields and methods)
,字段(Fields )指類中的數(shù)據(jù)成員(data member)(c#中將其稱為attribute )
方法(method )指類中的成員函數(shù)(member function)
2. 基本成員默認(rèn)值:
若類的某個(gè)成員是基本數(shù)據(jù)類型,即使沒對(duì)其進(jìn)行初始化,Java 也會(huì)自動(dòng)給其賦默認(rèn)值,如下表所示:

注意:Java 中局部變量的值如果沒有初始化,為隨機(jī)值,和在類中的數(shù)據(jù)成員不同。
2.5 方法、參數(shù)和返回值(Methods,arguments,and return values)
Java 的方法決定了一個(gè)對(duì)象能夠接收什么樣的信息。調(diào)用方法的行為通常被稱為發(fā)送消息給對(duì)象(sending a message to an object)。
這本書上提到參數(shù)是對(duì)象,基本數(shù)據(jù)類型的變量難道不能作參數(shù)嗎?應(yīng)該也行吧。
2.6 構(gòu)建一個(gè)java 程序
1. 名字可見性(Name visibility)
名稱管理,Java 規(guī)定,程序員自己的類庫(kù)名,是程序員反轉(zhuǎn)的Internet 域名。
比如,一個(gè)人域名為:MindView.net ,則他的應(yīng)用工具類庫(kù)名就為:net.mindview.utility.foibles
2. 運(yùn)用其他構(gòu)件(using other components)
使用關(guān)鍵字import 來(lái)導(dǎo)入其它包中的類庫(kù)。
3. static關(guān)鍵字(創(chuàng)建靜態(tài)類)
通常,創(chuàng)建類時(shí)只創(chuàng)建了對(duì)象的模板,除非用new 來(lái)創(chuàng)建對(duì)象,否則并不分配存儲(chǔ)空間,對(duì)象也并沒有真的產(chǎn)生。
但是,兩種情形無(wú)法解決:
,1) 只想為某特定字段(field )分配單一存儲(chǔ)空間,而不去考慮究竟要?jiǎng)?chuàng)建多少對(duì)象,甚至根
本就不創(chuàng)建任何對(duì)象;
2) 希望某種方法不與包含它的類的任何對(duì)象關(guān)聯(lián)在一起,即,沒創(chuàng)建對(duì)象,也能調(diào)用該方
法。
這時(shí),就需要用到static 關(guān)鍵字。
當(dāng)聲明一個(gè)事物(類)是static 時(shí),就意味著這個(gè)字段(field ,指類中數(shù)據(jù)成員嗎,中文書上翻譯為“域”,明顯不妥)或方法不會(huì)與包含它們的那個(gè)累的任何對(duì)象實(shí)例關(guān)聯(lián)在一起。 注意:
不管用包含靜態(tài)成員的類創(chuàng)建了多少個(gè)對(duì)象,靜態(tài)成員所占用的也只有一份存儲(chǔ)空間; 類的靜態(tài)成員被所有對(duì)象所共享;
類的靜態(tài)成員可以被類直接引用,也可以通過(guò)任何該類的成員引用,但注意,操作的都是同一個(gè)成員。
2.7 Your first Java program
public class ShowProperties
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
//第一行顯示當(dāng)前操作系統(tǒng)中獲取的所有“屬性”,提供環(huán)境信息
//list()方法將當(dāng)前操作系統(tǒng)屬性列表發(fā)送給它的參數(shù):System.out System. getProperties ().list(System.out );
//在控制臺(tái)中輸出用戶名和java 庫(kù)路徑
System. out .println(System.getProperty ("user.name" ));
System. out .println(System.getProperty ("java.library.path" )); }
}
//最終結(jié)果是在前面顯示系統(tǒng)屬性列表,然后顯示系統(tǒng)用戶名和系統(tǒng)path 值
編譯和運(yùn)行:我使用的是MyEclipse Enterprise Workbench 8.0,感覺很不錯(cuò),嘿嘿~
2.8 注釋和嵌入式文檔
1. 注釋文檔
,javadoc是用于提取注釋的工具,它是JDK 安裝的一部分,可以查找出程序內(nèi)的特殊注釋標(biāo)簽。它不僅解析由這些標(biāo)簽標(biāo)記的信息,也將毗鄰注釋的類名或方法名抽取出來(lái)。如此,就可以用最少的工作量,生成相當(dāng)好的程序文檔。前提必須是,要將程序和注釋寫在同一文件中。 javadoc 輸出的是一個(gè)HTML 文件。如果想對(duì)Javadoc 處理過(guò)的信息執(zhí)行特殊的操作(例如,產(chǎn)生不同格式的輸出),那么可以通過(guò)編寫自己的被稱為“doclets ”的javadoc 處理器來(lái)實(shí)現(xiàn)。相關(guān)資料:http://MindView.net/Books/Better Java
javadoc 的介紹不包含在JDK 文檔中,需要在sun 網(wǎng)站上另外下載,解壓后,查閱“tooldocs ”子目錄
2.javadoc 語(yǔ)法
所有javadoc 命令都只能在“/**”注釋中出現(xiàn),結(jié)束于“*/”。
兩種方式:嵌入HTML ,或使用“文檔標(biāo)簽”(doc tags)
獨(dú)立文檔標(biāo)簽(standalone doc tags):以“@”字符開頭的命令,且位于注釋行最前; 行內(nèi)文檔標(biāo)簽(inline doc tags):以“@”開頭,可以位于任意位置,但需括在花括號(hào)內(nèi)。 三種類型的注釋文檔,分別對(duì)應(yīng)于注釋位置后的三種元素:類、字段和方法。
例如:
//: object/Documentation1.java
/** A class comment*/
public class Documentation1
{
/** A field comment */ public int i ; /** A method comment */
public void f() {}
} /// :~
注意:javadoc 只能為public 和protected 成員進(jìn)行文檔注釋提取。private 和包內(nèi)可訪問(wèn)成員(package-access members)的注釋會(huì)被忽略掉,在輸出結(jié)果中不會(huì)顯示(不過(guò)可以用-private 進(jìn)行標(biāo)記,以便把private 成員的注釋也包括在內(nèi))
3. 嵌入式HTML
javadoc 通過(guò)產(chǎn)生HTML 文檔傳送HTML 命令,這使得你可以從分利用HTML 來(lái)格式化顯示注釋文檔。
例://: object/Documentation2.java
,/**
*
* System.out.println(new Date());
*
*/
///:~
也可以像在Web 文檔中那樣運(yùn)用HTML ,對(duì)普通文本按照自己需求進(jìn)行格式化:
//: object/Documentation3.java
/**
* You can even insert a list:
*
- Item one
*
- Item two
*
- Item three
*
*
*/
///:~
4. 一些標(biāo)簽示例
1)@see:引用其他類
@see標(biāo)簽允許用戶引用其他類的文檔。javadoc 會(huì)在其生成的HTML 文件中,通過(guò)@see標(biāo)簽鏈接到其它文檔。
格式: @see classname
@see fully-qualified-classname @see fully-qualified-classname#method-name
上述每種格式都會(huì)在生成的文檔中加入一個(gè)具有超鏈接的“See Also”(參見)條目。但javadoc 不會(huì)檢查你所提供的超鏈接是否有效。
2) { @link package.class#member label }
該標(biāo)簽與@see 極為相似,只是它位于行內(nèi),并且使用“l(fā)abel ”作為超鏈接文本,而不是用“See Also”。
,3) { @docRoot }
該標(biāo)簽產(chǎn)生到文檔根目錄的相對(duì)路徑,用于文檔樹頁(yè)面的顯式超鏈接。
4 ){ @inheritDoc}
從當(dāng)前這個(gè)類的最直接基類中繼承相關(guān)文檔到當(dāng)前的文檔注釋中。
5)@version
格式:@version version-information
其中,“version-information ”可以是你認(rèn)為適合包含在版本說(shuō)明中的重要信息。如果javadoc 命令行使用了“-version ”標(biāo)記,那么就從生成的HTML 文檔中特別提取出版本信息。
6)@author
格式:@author author-information
和作者相關(guān)的信息。用javadoc 命令行的-author 標(biāo)記,可以從生成的HTML 文檔中特別提取作者信息。
7)@since
允許你指定程序代碼最早使用的版本,可以在HTML java 文檔中看到它被用來(lái)指定所用的JDK 版本的情況。
8)@param
用于方法文檔中,格式:@param parameter–name description
其中,parameter-name 是方法的參數(shù)名,description 是可延續(xù)數(shù)行的文本,終止于新的文檔標(biāo)簽出現(xiàn)之前。
9)@return
用于方法文檔,格式:@return description
用來(lái)描述返回值含義,可持續(xù)數(shù)行。
10)@throws
格式:@throws fully-qualified-class-name description
其中 fully-qualified-class-name 給出一個(gè)異常類的無(wú)歧義名字(完整名),而該異常類在別處定義。description (可持續(xù)數(shù)行)告訴你為什么此特殊類型的異常會(huì)在方法調(diào)用中出現(xiàn)。
11) @deprecated
用于指出一些 已由改進(jìn)的新特性所取代,建議用戶不要再使用這些舊屬性。因?yàn)樵诓痪玫膶?lái)它們很可能會(huì)被刪除如果使用一個(gè)標(biāo)記為@deprecated的方法,則會(huì)引起編譯器發(fā)布警告 在Java SE5中,Javadoc 標(biāo)簽@deprecated已經(jīng)被@Deprecated注釋所取代。
,5. 文檔示例
//: object/HelloDate.java
import java.util.*;
/** The first Thinking in Java example program.
* Displays a string and today’s date.
*@author Bruce Eckel
*@author
*@version 4.0
*/
public class HelloDate
{
/** Entry point to class & applation * @param args array of string arguments * @throws exceptions No exceptions thrown */ public static void main( String[] args) {
}
}
/* Output: (55 match)
Hello ,It’s:
Date ……
*///:~
第一行用一個(gè)“:”作為特殊記號(hào),說(shuō)明這是包含源文件名的注釋行。該行包含文件的路徑信息,隨后是文件名。
最后一行的“///:~”標(biāo)志源代碼清單的結(jié)束。自此,通過(guò)編譯器的執(zhí)行檢查后,文檔就可以自動(dòng)更新成本書的文本。 System.out.println(“Hello ,It’s: ”); System.out.println( new Date() );
,/*Output表示輸出的開始部分將由這個(gè)文件生成,通過(guò)這種形式,它會(huì)自動(dòng)的測(cè)試已驗(yàn)證其準(zhǔn)確性。
2.9 編碼風(fēng)格(Coding Style)
Camel-casing
2.10 總結(jié)
2.11 練習(xí)