天天看點

從Java的堆棧到Equals和==的比較

棧與堆都是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.基本資料類型,也稱原始資料類型。byte,short,char,int,long,float,double,boolean 

  他們之間的比較,應用雙等号(==),比較的是他們的值。 

2.複合資料類型(類) 

  當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放位址,是以,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。 java當中所有的類都是繼承于object這個基類的,在object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的記憶體地 址,但在一些類庫當中這個方法被覆寫掉了,如string,integer,date在這些類當中equals有其自身的實作,而不再是比較類在堆記憶體中的存放位址了。

  對于複合資料類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基于他們在記憶體中的存放位置的位址值的,因為object的equals方法也是用雙等号(==)進行比較的,是以比較後的結果跟雙等号(==)的結果相同。

從Java的堆棧到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.再稍微改動一下程式,會有更奇怪的發現:

從Java的堆棧到Equals和==的比較

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");}  

}  

我們将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.再次更改程式:

從Java的堆棧到Equals和==的比較

s2 = s2.intern();  

這次加入:s2 = s2.intern();

s1 == s2

原 來,(java.lang.string的intern()方法"abc".intern()方法的傳回值還是字元串"abc",表面上看起來好像這個方 法沒什麼用處。但實際上,它做了個小動作:檢查字元串池裡是否存在"abc"這麼一個字元串,如果存在,就傳回池裡的字元串;如果不存在,該方法會 把"abc"添加到字元串池中,然後再傳回它的引用。

檢視源碼發表小結:

從Java的堆棧到Equals和==的比較

<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。