創(chuàng)建句柄時恒序,我們希望它同一個新對象連接。通常用new關(guān)鍵字達到這一目的具帮。new的意思是:“把我變成這些對象的一種新類型”谭网。所以在上面的例子中汪厨,可以說:
String s = new String("asdf");
它不僅指出“將我變成一個新字串”,也通過提供一個初始字串愉择,指出了“如何生成這個新字串”劫乱。
當然,字串(String)并非唯一的類型锥涕。Java配套提供了數(shù)量眾多的現(xiàn)成類型衷戈。對我們來講,最重要的就是記住能自行創(chuàng)建類型层坠。事實上殖妇,這應(yīng)是Java程序設(shè)計的一項基本操作,是繼續(xù)本書后余部分學(xué)習(xí)的基礎(chǔ)窿春。
2.2.1 保存到什么地方
程序運行時拉一,我們最好對數(shù)據(jù)保存到什么地方做到心中有數(shù)。特別要注意的是內(nèi)存的分配旧乞。有六個地方都可以保存數(shù)據(jù):
(1) 寄存器。這是最快的保存區(qū)域磅氨,因為它位于和其他所有保存方式不同的地方:處理器內(nèi)部尺栖。然而,寄存器的數(shù)量十分有限烦租,所以寄存器是根據(jù)需要由編譯器分配延赌。我們對此沒有直接的控制權(quán),也不可能在自己的程序里找到寄存器存在的任何蹤跡叉橱。
(2) 堆棧挫以。駐留于常規(guī)RAM(隨機訪問存儲器)區(qū)域,但可通過它的“堆棧指針”獲得處理的直接支持窃祝。堆棧指針若向下移掐松,會創(chuàng)建新的內(nèi)存;若向上移粪小,則會釋放那些內(nèi)存大磺。這是一種特別快、特別有效的數(shù)據(jù)保存方式探膊,僅次于寄存器杠愧。創(chuàng)建程序時,Java編譯器必須準確地知道堆棧內(nèi)保存的所有數(shù)據(jù)的“長度”以及“存在時間”逞壁。這是由于它必須生成相應(yīng)的代碼流济,以便向上和向下移動指針锐锣。這一限制無疑影響了程序的靈活性,所以盡管有些Java數(shù)據(jù)要保存在堆棧里——特別是對象句柄绳瘟,但Java對象并不放到其中刺下。
(3) 堆。一種常規(guī)用途的內(nèi)存池(也在RAM區(qū)域)稽荧,其中保存了Java對象橘茉。和堆棧不同,“內(nèi)存堆”或“堆”(Heap)最吸引人的地方在于編譯器不必知道要從堆里分配多少存儲空間姨丈,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時間畅卓。因此,用堆保存數(shù)據(jù)時會得到更大的靈活性蟋恬。要求創(chuàng)建一個對象時翁潘,只需用new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時歼争,會在堆里自動進行數(shù)據(jù)的保存拜马。當然,為達到這種靈活性沐绒,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間俩莽!
(4) 靜態(tài)存儲。這兒的“靜態(tài)”(Static)是指“位于固定位置”(盡管也在RAM里)乔遮。程序運行期間扮超,靜態(tài)存儲的數(shù)據(jù)將隨時等候調(diào)用√0梗可用static關(guān)鍵字指出一個對象的特定元素是靜態(tài)的出刷。但Java對象本身永遠都不會置入靜態(tài)存儲空間。
(5) 常數(shù)存儲坯辩。常數(shù)值通常直接置于程序代碼內(nèi)部馁龟。這樣做是安全的,因為它們永遠都不會改變漆魔。有的常數(shù)需要嚴格地保護坷檩,所以可考慮將它們置入只讀存儲器(ROM)。
(6) 非RAM存儲有送。若數(shù)據(jù)完全獨立于一個程序之外淌喻,則程序不運行時仍可存在,并在程序的控制范圍之外雀摘。其中兩個最主要的例子便是“流式對象”和“固定對象”裸删。對于流式對象,對象會變成字節(jié)流阵赠,通常會發(fā)給另一臺機器涯塔。而對于固定對象肌稻,對象保存在磁盤中。即使程序中止運行匕荸,它們?nèi)钥杀3肿约旱臓顟B(tài)不變爹谭。對于這些類型的數(shù)據(jù)存儲,一個特別有用的技巧就是它們能存在于其他媒體中榛搔。一旦需要诺凡,甚至能將它們恢復(fù)成普通的、基于RAM的對象践惑。Java 1.1提供了對Lightweight persistence的支持腹泌。未來的版本甚至可能提供更完整的方案。
2.2.2 特殊情況:主要類型
有一系列類需特別對待尔觉;可將它們想象成“基本”凉袱、“主要”或者“主”(Primitive)類型,進行程序設(shè)計時要頻繁用到它們侦铜。之所以要特別對待专甩,是由于用new創(chuàng)建對象(特別是小的、簡單的變量)并不是非常有效钉稍,因為new將對象置于“堆”里涤躲。對于這些類型,Java采納了與C和C++相同的方法嫁盲。也就是說篓叶,不是用new創(chuàng)建變量,而是創(chuàng)建一個并非句柄的“自動”變量羞秤。這個變量容納了具體的值,并置于堆棧中左敌,能夠更高效地存取瘾蛋。
Java決定了每種主要類型的大小。就象在大多數(shù)語言里那樣矫限,這些大小并不隨著機器結(jié)構(gòu)的變化而變化哺哼。這種大小的不可更改正是Java程序具有很強移植能力的原因之一。
主類型 | 大小 | 最小值 | 最大值 | 封裝器類型 |
---|---|---|---|---|
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ù)值類型全都是有符號(正負號)的,所以不必費勁尋找沒有符號的類型无宿。
主數(shù)據(jù)類型也擁有自己的“封裝器”(wrapper)類茵汰。這意味著假如想讓堆內(nèi)一個非主要對象表示那個主類型,就要使用對應(yīng)的封裝器孽鸡。例如:
char c = 'x';
Character C = new Character('c');
也可以直接使用:
Character C = new Character('x');
這樣做的原因?qū)⒃谝院蟮恼鹿?jié)里解釋蹂午。
1. 高精度數(shù)字
Java 1.1增加了兩個類栏豺,用于進行高精度的計算:BigInteger和BigDecimal。盡管它們大致可以劃分為“封裝器”類型豆胸,但兩者都沒有對應(yīng)的“主類型”奥洼。
這兩個類都有自己特殊的“方法”,對應(yīng)于我們針對主類型執(zhí)行的操作晚胡。也就是說灵奖,能對int或float做的事情,對BigInteger和BigDecimal一樣可以做估盘。只是必須使用方法調(diào)用瓷患,不能使用運算符。此外忿檩,由于牽涉更多尉尾,所以運算速度會慢一些。我們犧牲了速度燥透,但換來了精度沙咏。
BigInteger支持任意精度的整數(shù)。也就是說班套,我們可精確表示任意大小的整數(shù)值肢藐,同時在運算過程中不會丟失任何信息。
BigDecimal支持任意精度的定點數(shù)字吱韭。例如吆豹,可用它進行精確的幣值計算。
至于調(diào)用這兩個類時可選用的構(gòu)建器和方法理盆,請自行參考聯(lián)機幫助文檔痘煤。
2.2.3 Java的數(shù)組
幾乎所有程序設(shè)計語言都支持數(shù)組。在C和C++里使用數(shù)組是非常危險的猿规,因為那些數(shù)組只是內(nèi)存塊衷快。若程序訪問自己內(nèi)存塊以外的數(shù)組,或者在初始化之前使用內(nèi)存(屬于常規(guī)編程錯誤)姨俩,會產(chǎn)生不可預(yù)測的后果(注釋②)蘸拔。
②:在C++里,應(yīng)盡量不要使用數(shù)組环葵,換用標準模板庫(Standard TemplateLibrary)里更安全的容器调窍。
Java的一項主要設(shè)計目標就是安全性。所以在C和C++里困擾程序員的許多問題都未在Java里重復(fù)张遭。一個Java可以保證被初始化邓萨,而且不可在它的范圍之外訪問。由于系統(tǒng)自動進行范圍檢查,所以必然要付出一些代價:針對每個數(shù)組先誉,以及在運行期間對索引的校驗湿刽,都會造成少量的內(nèi)存開銷。但由此換回的是更高的安全性褐耳,以及更高的工作效率诈闺。為此付出少許代價是值得的。
創(chuàng)建對象數(shù)組時铃芦,實際創(chuàng)建的是一個句柄數(shù)組雅镊。而且每個句柄都會自動初始化成一個特殊值,并帶有自己的關(guān)鍵字:null(空)刃滓。一旦Java看到null仁烹,就知道該句柄并未指向一個對象。正式使用前咧虎,必須為每個句柄都分配一個對象卓缰。若試圖使用依然為null的一個句柄,就會在運行期報告問題砰诵。因此征唬,典型的數(shù)組錯誤在Java里就得到了避免。
也可以創(chuàng)建主類型數(shù)組茁彭。同樣地总寒,編譯器能夠擔保對它的初始化,因為會將那個數(shù)組的內(nèi)存劃分成零理肺。
數(shù)組問題將在以后的章節(jié)里詳細討論摄闸。