標籤:equals equals和的區別
認識Java中堆和棧
棧與堆都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式員不能直接地設定棧或堆。
Java的堆是一個運行時資料區,類的對象從中分配空間。這些對象通過new、newarray、anewarray和 multianewarray等指令建立,它們不需要程式碼來顯式的釋放。堆是由記憶體回收來負責的,堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配記憶體的,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在運行時動態分配記憶體,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧資料可以共用。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數(,int, short, long, byte, float, double, boolean, char)和物件控點。棧有一個很重要的特殊性,就是存在棧中的資料可以共用。
假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中建立一個變數為a的引用,然後尋找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在建立完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜尋棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
要注意這種資料的共用與兩個對象的引用同時指向一個對象的這種共用是不同的,因為這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間的。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。
String是一個特殊的封裝類資料。
可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來建立,第一種是用new()來建立對象的,它會在存放於堆中。每調用一次就會建立一個新的對象。 而第二種是先在棧中建立一個對String類的對象引用變數str,然後尋找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指 向”abc”,如果已經有”abc” 則直接令str指向“abc”。
比較類裡面的數值是否相等時,用equals()方法;當測試兩個封裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個對象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是產生不同的對象。每一次產生一個。
因此用第二種方式(String str1 ="abc")建立多個”abc”字串,在記憶體中其實只存在一個對象而已. 這種寫法有利與節省記憶體空間. 同時它可以在一定程度上提高程式的運行速度,因為JVM會自動根據棧中資料的實際情況來決定是否有必要建立新對象。而對於String str = new String("abc")的代碼,則一概在堆中建立新對象,而不管其字串值是否相等,是否有必要建立新對象,從而加重了程式的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,建立了String類的對象str。擔心陷阱!對象可能並沒有被建立!而可能只是指向一個先前已經建立的 對象。只有通過new()方法才能保證每次都建立一個新的對象。
由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程式效率。
************************************************************************************************************************************************************
Java中Equals和==的區別
java中的資料類型,可分為兩類:
1.基礎資料型別 (Elementary Data Type),也稱未經處理資料類型。byte,short,char,int,long,float,double,boolean
他們之間的比較,應用雙等號(==),比較的是他們的值。
2.複合資料型別(類)
當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放地址,所以,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的記憶體地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆記憶體中的存放地址了。
對於複合資料型別之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在記憶體中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較後的結果跟雙等號(==)的結果相同。
<span style="font-family:Microsoft YaHei;font-size:12px;">public class TestString {public static void main(String[] args) {String s1 = "Monday";String s2 = "Monday";if (s1 == s2) {System.out.println("s1 == s2");} else {System.out.println("s1 != s2");}}}</span>
編譯並運行程式,輸出:s1 == s2說明:s1 與 s2 引用同一個 String 對象 -- "Monday"!
2.再稍微改動一下程式,會有更奇怪的發現:
<span style="font-family:Microsoft YaHei;font-size:12px;">public class TestString {public static void main(String[] args) {String s1 = "Monday";String s2 = new String("Monday");if (s1 == s2){System.out.println("s1 == s2");}else{System.out.println("s1 != s2");}if (s1.equals(s2)) {System.out.println("s1 equals s2");}else{System.out.println("s1 not equals s2");}}}</span>
我們將s2用new操作符建立
程式輸出:
s1 != s2
s1 equals s2
說明:s1 s2分別引用了兩個"Monday"String對象
3. 字串緩衝池
原來,程式在啟動並執行時候會建立一個字串緩衝池當使用 s2 = "Monday" 這樣的表達是建立字串的時候,程式首先會在這個String緩衝池中尋找相同值的對象,在第一個程式中,s1先被放到了池中,所以在s2被建立的時候,程式找到了具有相同值的 s1
將s2引用s1所引用的對象"Monday"
第二段程式中,使用了 new 操作符,他明白的告訴程式:"我要一個新的!不要舊的!"於是一個新的"Monday"Sting對象被建立在記憶體中。他們的值相同,但是位置不同,一個在池中遊泳一個在岸邊休息。哎呀,真是資源浪費,明明是一樣的非要分開做什麼呢?
4.再次更改程式:
<span style="font-family:Microsoft YaHei;font-size:12px;">public class TestString {public static void main(String[] args) {String s1 = "Monday";String s2 = new String("Monday");s2 = s2.intern();if (s1 == s2){System.out.println("s1 == s2");}else{System.out.println("s1 != s2");}if (s1.equals(s2)) {System.out.println("s1 equals s2");}else{System.out.println("s1 not equals s2");}}}</span>
這次加入:s2 = s2.intern();
程式輸出:
s1 == s2
s1 equals s2
原 來,(java.lang.String的intern()方法"abc".intern()方法的返回值還是字串"abc",表面上看起來好像這個方 法沒什麼用處。但實際上,它做了個小動作:檢查字串池裡是否存在"abc"這麼一個字串,如果存在,就返回池裡的字串;如果不存在,該方法會 把"abc"添加到字串池中,然後再返回它的引用。
)
************************************************************************************************************************************************************
查看源碼發表小結:
<span style="font-family:Microsoft YaHei;font-size:12px;">public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }</span>
上面的這一段是String的equals源碼,通過細心的解讀我們可以發現其中的奧妙。
String重寫的equals方法保留了Object比較兩個對象內容地址是否相等。在此基礎上增加了一個比較String的值是否相等。這是什麼意思呢。 如果equals括弧裡的對象不是String類型的,那麼比較他和原對象的記憶體位址是否相等。如果instanceofString 那麼先把這個對象轉為String,再把它的值變為char數組,也就是下面的value,如果他們的char數組長度相等再對這個數組的char進行一個個遍曆比較是否一樣。如果都一樣就equals為true,否則為false。
從Java的堆棧到Equals和==的比較