標籤:
一、用引用操縱對象
在java中一切都被視為對象,但操縱的標識符實際上是對象的一個“引用”(reference)。可以
將這一情形想象成用遙控器(引用)來操縱電視機(對象)。只要握住這個遙控器,就能保持與電視
機的串連。當有人想改變頻道或減小音量是,實際操縱的是遙控器(引用),再由遙控器來調控電視
機(對象)。如果想在房間裡四處走走,同時仍能調控電視機,那麼只需攜帶遙控器(引用)而不是
電視機(對象)。
此外,即使沒有電視機,遙控器也可獨立存在。也就是說,你擁有一個引用,並不一定需要有一個
對象與它關聯。因此,如果想操縱一個詞或一個句子,則可以建立一個String引用:
String s;
但這裡所建立的只是引用,並不是對象。如果此時向s發送一個是訊息,就會返回一個執行階段錯誤。
這是因為此時s實際上沒有與任何事物相關聯(即,沒有電視機)。因此,一種安全的做法是:建立一
個引用的同時便進行初始化。
String s = "asdf";
二、必須由你建立所有對象
一旦建立了一個引用,就希望它能與一個新的對象相關聯。通常用new操作符來實現這一目的。new
關鍵字的意思是“給我一個新對象。”所以前面的例子可以寫成:
String s = new String("asdf");
它不僅表示“給我一個新的字串”,而且通過提供一個初始字串,給出了怎麼產生這個String的資訊。
1.儲存的地方
程式運行時,對象是怎麼進行放置安排的呢?特別是記憶體是怎麼分配的呢?對這些方面的瞭解會對你有
很大的協助。有五個不同的地方可以儲存資料:
1)寄存器:這是最快的儲存區,因為它位於不同於其他儲存區的地方——處理器內部。但是寄存器的數量及
其有限,所以寄存器根據需求進行分配。你不能直接控制,也不能在程式中感覺到基礎存在的任何跡象(另一
方面,C和C++允許你想編譯器建議寄存器的分配方式)。
2)堆棧:位於通用RAM(隨機訪問儲存空間)中,但通過堆棧指標可以從處理器那裡獲得直接支援。堆棧指標若
向下移動,則分配新的記憶體;若向上移動,則釋放那些記憶體。這是一種快速有效分配儲存方法,僅次於寄存器
。建立程式時,Java系統必須知道儲存在堆棧內所有項的確切生命週期,以便上下移動堆棧指標。這一約束限制
了程式的靈活性,所以雖然某些Java資料存放區於堆棧中——特別是對象引用,但是Java對象並不儲存於其中。
3)堆:一種通用的記憶體池(也位於RAM區),用於存放所有的Java對象。堆不同於堆棧的好處是:編譯器不需
要知道儲存的資料在堆裡存活多長時間。因此,在堆裡分配儲存有很大的靈活性。當需要一個對象時,只需用
new寫一行簡單的代碼,當執行這行代碼時,會自動在堆裡進行儲存分配。當然,為這種靈活性必須要付出相應
的代價:用堆進行儲存分配和清理可能比用堆棧進行儲存分配需要更多的時間(如果確實可以在Java中像在C++
中一樣在棧中建立對象)。
4)常量儲存:常量值通常直接存放在程式碼內部,這樣做事安全的,因為它們永遠不會被改變。有時,在嵌入
式系統中,常量本身會和其他部分隔離開,所以在這種情況下,可以選擇將其存放在ROM(唯讀記憶體)中。
5)非RAM儲存:如果資料完全存活於程式之外,那麼它可以不受程式的任何控制,在程式沒有運行時也可以存在
。其中兩個基本的例子是流對象和持久化對象。在流對象中,對象轉化成位元組流,通常被發送給另一台機器。在“
持久化對象”中,對象唄存放在磁碟上,因此,即使程式終止,它們仍可以保持自己的狀態。這種儲存方式的技巧
在於:把對象轉化成可以存放在其他媒介上的事物,在需要時,可恢複成常規的、基於RAM的對象。Java提供了對
輕量級持久化的支援,而諸如JDBC和Hibernate這樣的機制提供了更加複雜的對在資料庫中存放和讀取對象資訊的
支援。
2.基本類型
在程式設計中經常用到一系列類型,它們需要特殊對待。可以把它們想象成”基本”類型。之所以特殊對待,是因
為new將Object Storage Service在“堆”裡,故用new建立一個對象——特別是小的、簡單的變數,往往不是很有效。因此,對於這
些類型,Java採取與C和C++相同的方法。也就是說,不用new來建立變數,而是建立一個並非是引用的“自動”變數
。這個變數直接儲存“值”,共置於堆棧中,因此更加高效。
所有數實值型別都有加號或減號,所以不要去尋找無符號的數實值型別。
基本類型具有的封裝器類,使得可以在堆中建立一個非基本對象,用來表示對應的基本類型。例如:
char c = ‘x‘;
Character ch = new Character(c);
也可以這樣用:
Character ch = new Character(‘x‘);
Java SE5的自動封裝功能將自動地將基本類型轉換為封裝器類型:
Character ch = ‘x‘;
並可以反向轉換:
char c = ch;
高精度數字
Java提供了兩個用於高精度計算的類:BigInteger和BigDecimal。雖然它們大體上屬於封裝器類的範圍
但二者都沒有對應的基本類型。
不過,這兩個類包含的方法,提供的操作與對基本類型所能執行的操作相似。也就是說,能作用於int或float
的操作,也同樣能作用於BigInteger或BigDecimal。只不過必須以方法調用取代運算子方式來實現。由於這麼做
複雜了許多,所以運算速度回比較慢。在這裡,我們一速度換取精度。
BigInteger支援任意精度的整數。也就是說,在運算中,可以準確地表示任何大小的整數值,而不會丟失任何
資訊。
BigDecimal支援任何精度的定點數,例如,可以用它進行精確的貨幣計算。
3.Java中的數組
幾乎所有的程式設計語言都支援數組,在C和C++中使用數組是很危險的,因為C和C++中的數組就是記憶體塊。
如果一個程式要訪問其自身記憶體塊之外的數組,或在數組初始化前使用記憶體(程式中常見的錯誤),都會產生難以
預料的後果。
Java的主要目標之一是安全性,所以在C和C++裡困擾程式員的問題在Java裡不會再出現。Java確保數組會被
初始化,而且不能再他的範圍之外被訪問。這種範圍檢查,十一每個數組上少量的記憶體開銷及運行時的下標檢查為代
價的。但由此換來的是安全性和效率的提高,因此付出的代價是值得的(並且Java有時可以最佳化這些操作)。
當建立一個數組對象時,實際上就是建立了一個引用數組,並且每個引用都會自動被初始化為一個特定值,該值
擁有自己的關鍵字null。一旦Java看到null,就知道這個引用還沒有指向某個對象。在使用任何引用前,必須為其指
定一個對象;如果試圖使用一個還是null的引用,在運行時將會報錯。因此,常犯的數組錯誤在Java中就可以避免。
還可以建立用來存放基礎資料型別 (Elementary Data Type)的數組。同樣,編譯器也能確保這種數組的初始化,因為他會將這種數組所佔
的記憶體全部置零。
三、永遠不需要銷毀對象
1.對象的範圍
Java對象不具備和基本類型一樣的生命週期。當用new建立一個Java對象時,它可以存活於範圍之外。所以假
如你採用以下代碼:
{
String s = new String("a string");
}
引用s在範圍終點就消失了。然而,s指向的String對象仍然繼續佔據記憶體空間。在這一小段代碼中,我們無法在這
個範圍之後訪問這個對象,因為對它唯一的引用已超出了範圍的範圍。
那麼Java靠什麼才能防止這些對象填滿記憶體空間,進而阻塞你的程式呢?
Java有一個記憶體回收行程,用來監視用new建立的對象,並辨別那些不會再被引用的對象。隨後,釋放這些對象的記憶體
空間,以便供其他新的對象使用。也就是說,你只要建立對象,一旦不再需要,它們就會自行消失。這樣做就消除了這類
編程問題(即“記憶體泄露)。
Java——(一)一切都是對象