Java初學(xué)者必須掌握的幾個(gè)問(wèn)題發(fā)布者:本站 時(shí)間:2020-05-06 14:05:13
對于這個(gè)系列里的問(wèn)題,每個(gè)學(xué)Java的人都應該搞懂。當然,如果只是學(xué)Java玩玩就無(wú)所謂了。如果你認為自己已經(jīng)超越初學(xué)者了,卻不很懂這些問(wèn)題,請將你自己重歸初學(xué)者行列。
問(wèn)題一:我聲明了什么!
String s = "Hello world!"; |
許多人都做過(guò)這樣的事情,但是,我們到底聲明了什么?回答通常是:一個(gè)String,內容是“Hello world!”。這樣模糊的回答通常是概念不清的根源。如果要準確的回答,一半的人大概會(huì )回答錯誤。
這個(gè)語(yǔ)句聲明的是一個(gè)指向對象的引用,名為“s”,可以指向類(lèi)型為String的任何對象,目前指向"Hello world!"這個(gè)String類(lèi)型的對象。這就是真正發(fā)生的事情。我們并沒(méi)有聲明一個(gè)String對象,我們只是聲明了一個(gè)只能指向String對象的引用變量。所以,如果在剛才那句語(yǔ)句后面,如果再運行一句:
String string = s; |
我們是聲明了另外一個(gè)只能指向String對象的引用,名為string,并沒(méi)有第二個(gè)對象產(chǎn)生,string還是指向原來(lái)那個(gè)對象,也就是,和s指向同一個(gè)對象。
問(wèn)題二:"=="和equals方法究竟有什么區別?
==操作符專(zhuān)門(mén)用來(lái)比較變量的值是否相等。比較好理解的一點(diǎn)是:
int a=10; int b=10; 則a==b將是true。 但不好理解的地方是: String a=new String("foo"); String b=new String("foo"); 則a==b將返回false。 |
根據前一帖說(shuō)過(guò),對象變量其實(shí)是一個(gè)引用,它們的值是指向對象所在的內存地址,而不是對象本身。a和b都使用了new操作符,意味著(zhù)將在內存中產(chǎn)生兩個(gè)內容為"foo"的字符串,既然是“兩個(gè)”,它們自然位于不同的內存地址。a和b的值其實(shí)是兩個(gè)不同的內存地址的值,所以使用"=="操作符,結果會(huì )是false。誠然,a和b所指的對象,它們的內容都是"foo",應該是“相等”,但是==操作符并不涉及到對象內容的比較。
對象內容的比較,正是equals方法做的事。
看一下Object對象的equals方法是如何實(shí)現的:
boolean equals(Object o){ return this==o; } |
Object對象默認使用了= =操作符。所以如果你自創(chuàng )的類(lèi)沒(méi)有覆蓋equals方法,那你的類(lèi)使用equals和使用==會(huì )得到同樣的結果。同樣也可以看出,Object的 equals方法沒(méi)有達到equals方法應該達到的目標:比較兩個(gè)對象內容是否相等。因為答案應該由類(lèi)的創(chuàng )建者決定,所以Object把這個(gè)任務(wù)留給了類(lèi)的創(chuàng )建者。
看一下一個(gè)極端的類(lèi):
Class Monster{ private String content; ... boolean equals(Object another){ return true;} } |
我覆蓋了equals方法。這個(gè)實(shí)現會(huì )導致無(wú)論Monster實(shí)例內容如何,它們之間的比較永遠返回true。
所以當你是用equals方法判斷對象的內容是否相等,請不要想當然。因為可能你認為相等,而這個(gè)類(lèi)的作者不這樣認為,而類(lèi)的equals方法的實(shí)現是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列碼的集合 (HashSet,HashMap,HashTable),請察看一下java doc以確認這個(gè)類(lèi)的equals邏輯是如何實(shí)現的。
問(wèn)題三:String到底變了沒(méi)有?
沒(méi)有。因為String被設計成不可變(immutable)類(lèi),所以它的所有對象都是不可變對象。請看下列代碼:
String s = "Hello"; s = s + " world!"; |
s所指向的對象是否改變了呢? 從本系列第一篇的結論很容易導出這個(gè)結論。我們來(lái)看看發(fā)生了什么事情。在這段代碼中,s原先指向一個(gè)String對象,內容是"Hello",然后我們對 s進(jìn)行了+操作,那么s所指向的那個(gè)對象是否發(fā)生了改變呢?答案是沒(méi)有。這時(shí),s不指向原來(lái)那個(gè)對象了,而指向了另一個(gè)String對象,內容為 "Hello world!",原來(lái)那個(gè)對象還存在于內存之中,只是s這個(gè)引用變量不再指向它了。
通過(guò)上面的說(shuō)明,我們很容易導出另一個(gè)結論,如果經(jīng)常對字符串進(jìn)行各種各樣的修改,或者說(shuō),不可預見(jiàn)的修改,那么使用String來(lái)代表字符串的話(huà)會(huì )引起很大的內存開(kāi)銷(xiāo)。因為String對象建立之后不能再改變,所以對于每一個(gè)不同的字符串,都需要一個(gè)String對象來(lái)表示。這時(shí),應該考慮使用StringBuffer類(lèi),它允許修改,而不是每個(gè)不同的字符串都要生成一個(gè)新的對象。并且,這兩種類(lèi)的對象轉換十分容易。
同時(shí),我們還可以知道,如果要使用內容相同的字符串,不必每次都new一個(gè)String。例如我們要在構造器中對一個(gè)名叫s的String引用變量進(jìn)行初始化,把它設置為初始值,應當這樣做:
public class Demo { private String s; ... public Demo { s = "Initial Value"; } ... } 而非 s = new String("Initial Value"); |
后者每次都會(huì )調用構造器,生成新對象,性能低下且內存開(kāi)銷(xiāo)大,并且沒(méi)有意義,因為String對象不可改變,所以對于內容相同的字符串,只要一個(gè)String對象來(lái)表示就可以了。也就說(shuō),多次調用上面的構造器創(chuàng )建多個(gè)對象,他們的String類(lèi)型屬性s都指向同一個(gè)對象。
上面的結論還基于這樣一個(gè)事實(shí):對于字符串常量,如果內容相同,Java認為它們代表同一個(gè)String對象。而用關(guān)鍵字new調用構造器,總是會(huì )創(chuàng )建一個(gè)新的對象,無(wú)論內容是否相同。
至于為什么要把String類(lèi)設計成不可變類(lèi),是它的用途決定的。其實(shí)不只 String,很多Java標準類(lèi)庫中的類(lèi)都是不可變的。在開(kāi)發(fā)一個(gè)系統的時(shí)候,我們有時(shí)候也需要設計不可變類(lèi),來(lái)傳遞一組相關(guān)的值,這也是面向對象思想的體現。不可變類(lèi)有一些優(yōu)點(diǎn),比如因為它的對象是只讀的,所以多線(xiàn)程并發(fā)訪(fǎng)問(wèn)也不會(huì )有任何問(wèn)題。當然也有一些缺點(diǎn),比如每個(gè)不同的狀態(tài)都要一個(gè)對象來(lái)代表,可能會(huì )造成性能上的問(wèn)題。所以Java標準類(lèi)庫還提供了一個(gè)可變版本,即StringBuffer。
選擇我們,優(yōu)質(zhì)服務(wù),不容錯過(guò)
1. 優(yōu)秀的網(wǎng)絡(luò )資源,強大的網(wǎng)站優(yōu)化技術(shù),穩定的網(wǎng)站和速度保證
2. 15年上海網(wǎng)站建設經(jīng)驗,優(yōu)秀的技術(shù)和設計水平,更放心
3. 全程省心服務(wù),不必擔心自己不懂網(wǎng)絡(luò ),更省心。
------------------------------------------------------------
24小時(shí)聯(lián)系電話(huà):021-58370032