2.2 所有對(duì)象都必須創(chuàng)建
創(chuàng)建句柄時(shí)禽最,我們希望它同一個(gè)新對(duì)象連接。通常用new關(guān)鍵字達(dá)到這一目的呛占。new的意思是:“把我變成這些對(duì)象的一種新類型”懦趋。所以在上面的例子中,可以說:
String s = new String("asdf");
它不僅指出“將我變成一個(gè)新字串”帜篇,也通過提供一個(gè)初始字串诫咱,指出了“如何生成這個(gè)新字串”。
當(dāng)然坎缭,字串(String)并非唯一的類型签钩。Java配套提供了數(shù)量眾多的現(xiàn)成類型凯亮。對(duì)我們來講,最重要的就是記住能自行創(chuàng)建類型假消。事實(shí)上,這應(yīng)是Java程序設(shè)計(jì)的一項(xiàng)基本操作臼予,是繼續(xù)本書后余部分學(xué)習(xí)的基礎(chǔ)啃沪。
2.2.1 保存到什么地方
程序運(yùn)行時(shí),我們最好對(duì)數(shù)據(jù)保存到什么地方做到心中有數(shù)创千。特別要注意的是內(nèi)存的分配追驴。有六個(gè)地方都可以保存數(shù)據(jù):
(1) 寄存器。這是最快的保存區(qū)域殿雪,因?yàn)樗挥诤推渌斜4娣绞讲煌牡胤剑禾幚砥鲀?nèi)部。然而爸业,寄存器的數(shù)量十分有限亏镰,所以寄存器是根據(jù)需要由編譯器分配。我們對(duì)此沒有直接的控制權(quán)薄霜,也不可能在自己的程序里找到寄存器存在的任何蹤跡纸兔。
(2) 堆棧。駐留于常規(guī)RAM(隨機(jī)訪問存儲(chǔ)器)區(qū)域汉矿,但可通過它的“堆棧指針”獲得處理的直接支持洲拇。堆棧指針若向下移曲尸,會(huì)創(chuàng)建新的內(nèi)存男翰;若向上移,則會(huì)釋放那些內(nèi)存蛾绎。這是一種特別快、特別有效的數(shù)據(jù)保存方式鹏倘,僅次于寄存器顽爹。創(chuàng)建程序時(shí),Java編譯器必須準(zhǔn)確地知道堆棧內(nèi)保存的所有數(shù)據(jù)的“長(zhǎng)度”以及“存在時(shí)間”捏题。這是由于它必須生成相應(yīng)的代碼肉渴,以便向上和向下移動(dòng)指針。這一限制無疑影響了程序的靈活性,所以盡管有些Java數(shù)據(jù)要保存在堆棧里——特別是對(duì)象句柄庸诱,但Java對(duì)象并不放到其中。
(3) 堆朱灿。一種常規(guī)用途的內(nèi)存池(也在RAM區(qū)域)钠四,其中保存了Java對(duì)象。和堆棧不同侣灶,“內(nèi)存堆”或“堆”(Heap)最吸引人的地方在于編譯器不必知道要從堆里分配多少存儲(chǔ)空間缕碎,也不必知道存儲(chǔ)的數(shù)據(jù)要在堆里停留多長(zhǎng)的時(shí)間。因此凡怎,用堆保存數(shù)據(jù)時(shí)會(huì)得到更大的靈活性。要求創(chuàng)建一個(gè)對(duì)象時(shí)统倒,只需用new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時(shí)耸成,會(huì)在堆里自動(dòng)進(jìn)行數(shù)據(jù)的保存坛缕。當(dāng)然,為達(dá)到這種靈活性毙沾,必然會(huì)付出一定的代價(jià):在堆里分配存儲(chǔ)空間時(shí)會(huì)花掉更長(zhǎng)的時(shí)間宠页!
(4) 靜態(tài)存儲(chǔ)。這兒的“靜態(tài)”(Static)是指“位于固定位置”(盡管也在RAM里)烤宙。程序運(yùn)行期間俭嘁,靜態(tài)存儲(chǔ)的數(shù)據(jù)將隨時(shí)等候調(diào)用」赵疲可用static關(guān)鍵字指出一個(gè)對(duì)象的特定元素是靜態(tài)的近她。但Java對(duì)象本身永遠(yuǎn)都不會(huì)置入靜態(tài)存儲(chǔ)空間。
(5) 常數(shù)存儲(chǔ)粘捎。常數(shù)值通常直接置于程序代碼內(nèi)部。這樣做是安全的泳桦,因?yàn)樗鼈冇肋h(yuǎn)都不會(huì)改變娩缰。有的常數(shù)需要嚴(yán)格地保護(hù),所以可考慮將它們置入只讀存儲(chǔ)器(ROM)梧奢。
(6) 非RAM存儲(chǔ)。若數(shù)據(jù)完全獨(dú)立于一個(gè)程序之外趋惨,則程序不運(yùn)行時(shí)仍可存在惦蚊,并在程序的控制范圍之外。其中兩個(gè)最主要的例子便是“流式對(duì)象”和“固定對(duì)象”蹦锋。對(duì)于流式對(duì)象莉掂,對(duì)象會(huì)變成字節(jié)流,通常會(huì)發(fā)給另一臺(tái)機(jī)器憎妙。而對(duì)于固定對(duì)象,對(duì)象保存在磁盤中褥符。即使程序中止運(yùn)行抚垃,它們?nèi)钥杀3肿约旱臓顟B(tài)不變。對(duì)于這些類型的數(shù)據(jù)存儲(chǔ)铣焊,一個(gè)特別有用的技巧就是它們能存在于其他媒體中魂迄。一旦需要惋耙,甚至能將它們恢復(fù)成普通的、基于RAM的對(duì)象湿酸。Java 1.1提供了對(duì)Lightweight persistence的支持灭美。未來的版本甚至可能提供更完整的方案仇穗。
2.2.2 特殊情況:主要類型
有一系列類需特別對(duì)待昵观;可將它們想象成“基本”、“主要”或者“主”(Primitive)類型扩所,進(jìn)行程序設(shè)計(jì)時(shí)要頻繁用到它們祖屏。之所以要特別對(duì)待买羞,是由于用new創(chuàng)建對(duì)象(特別是小的、簡(jiǎn)單的變量)并不是非常有效期丰,因?yàn)閚ew將對(duì)象置于“堆”里漠嵌。對(duì)于這些類型,Java采納了與C和C++相同的方法儒鹿。也就是說约炎,不是用new創(chuàng)建變量,而是創(chuàng)建一個(gè)并非句柄的“自動(dòng)”變量圾浅。這個(gè)變量容納了具體的值,并置于堆棧中喷鸽,能夠更高效地存取灸拍。
Java決定了每種主要類型的大小。就象在大多數(shù)語言里那樣混槐,這些大小并不隨著機(jī)器結(jié)構(gòu)的變化而變化轩性。這種大小的不可更改正是Java程序具有很強(qiáng)移植能力的原因之一。
主類型 | 大小 | 最小值 | 最大值 | 封裝器類型 |
---|---|---|---|---|
boolean | 1-bit | – | – | Boolean |
char | 16-bit | Unicode 0 | Unicode 216- 1 | Character |
byte | 8-bit | -128 | +127 | Byte[11] |
short | 16-bit | -215 | +215 – 1 | Short1 |
int | 32-bit | -231 | +231 – 1 | Integer |
long | 64-bit | -263 | +263 – 1 | Long |
float | 32-bit | IEEE754 | IEEE754 | Float |
double | 64-bit | IEEE754 | IEEE754 | Double |
void | – | – | – | Void1 |
①:到Java 1.1才有悯嗓,1.0版沒有脯厨。
數(shù)值類型全都是有符號(hào)(正負(fù)號(hào))的,所以不必費(fèi)勁尋找沒有符號(hào)的類型俄认。
主數(shù)據(jù)類型也擁有自己的“封裝器”(wrapper)類眯杏。這意味著假如想讓堆內(nèi)一個(gè)非主要對(duì)象表示那個(gè)主類型,就要使用對(duì)應(yīng)的封裝器岂贩。例如:
char c = 'x';
Character C = new Character('c');
也可以直接使用:
Character C = new Character('x');
這樣做的原因?qū)⒃谝院蟮恼鹿?jié)里解釋萎津。
1. 高精度數(shù)字
Java 1.1增加了兩個(gè)類,用于進(jìn)行高精度的計(jì)算:BigInteger和BigDecimal锉屈。盡管它們大致可以劃分為“封裝器”類型,但兩者都沒有對(duì)應(yīng)的“主類型”遂黍。
這兩個(gè)類都有自己特殊的“方法”俊嗽,對(duì)應(yīng)于我們針對(duì)主類型執(zhí)行的操作。也就是說芯咧,能對(duì)int或float做的事情竹揍,對(duì)BigInteger和BigDecimal一樣可以做。只是必須使用方法調(diào)用驶拱,不能使用運(yùn)算符晶衷。此外,由于牽涉更多税迷,所以運(yùn)算速度會(huì)慢一些锹漱。我們犧牲了速度,但換來了精度毕泌。
BigInteger支持任意精度的整數(shù)嗅辣。也就是說,我們可精確表示任意大小的整數(shù)值愿题,同時(shí)在運(yùn)算過程中不會(huì)丟失任何信息蛙奖。
BigDecimal支持任意精度的定點(diǎn)數(shù)字。例如仔夺,可用它進(jìn)行精確的幣值計(jì)算攒砖。
至于調(diào)用這兩個(gè)類時(shí)可選用的構(gòu)建器和方法,請(qǐng)自行參考聯(lián)機(jī)幫助文檔灶体。
2.2.3 Java的數(shù)組
幾乎所有程序設(shè)計(jì)語言都支持?jǐn)?shù)組掐暮。在C和C++里使用數(shù)組是非常危險(xiǎn)的,因?yàn)槟切?shù)組只是內(nèi)存塊樟结。若程序訪問自己內(nèi)存塊以外的數(shù)組精算,或者在初始化之前使用內(nèi)存(屬于常規(guī)編程錯(cuò)誤),會(huì)產(chǎn)生不可預(yù)測(cè)的后果(注釋②)驮履。
②:在C++里,應(yīng)盡量不要使用數(shù)組倒戏,換用標(biāo)準(zhǔn)模板庫(Standard TemplateLibrary)里更安全的容器恐似。
Java的一項(xiàng)主要設(shè)計(jì)目標(biāo)就是安全性。所以在C和C++里困擾程序員的許多問題都未在Java里重復(fù)葛闷。一個(gè)Java可以保證被初始化双藕,而且不可在它的范圍之外訪問。由于系統(tǒng)自動(dòng)進(jìn)行范圍檢查蔓彩,所以必然要付出一些代價(jià):針對(duì)每個(gè)數(shù)組赤嚼,以及在運(yùn)行期間對(duì)索引的校驗(yàn),都會(huì)造成少量的內(nèi)存開銷更卒。但由此換回的是更高的安全性蹂空,以及更高的工作效率。為此付出少許代價(jià)是值得的上枕。
創(chuàng)建對(duì)象數(shù)組時(shí)辨萍,實(shí)際創(chuàng)建的是一個(gè)句柄數(shù)組。而且每個(gè)句柄都會(huì)自動(dòng)初始化成一個(gè)特殊值爪飘,并帶有自己的關(guān)鍵字:null(空)拉背。一旦Java看到null,就知道該句柄并未指向一個(gè)對(duì)象犁罩。正式使用前,必須為每個(gè)句柄都分配一個(gè)對(duì)象昼汗。若試圖使用依然為null的一個(gè)句柄顷窒,就會(huì)在運(yùn)行期報(bào)告問題源哩。因此,典型的數(shù)組錯(cuò)誤在Java里就得到了避免谓着。
也可以創(chuàng)建主類型數(shù)組坛掠。同樣地,編譯器能夠擔(dān)保對(duì)它的初始化舷蒲,因?yàn)闀?huì)將那個(gè)數(shù)組的內(nèi)存劃分成零友多。
數(shù)組問題將在以后的章節(jié)里詳細(xì)討論。