裝箱和拆箱、類型比較


轉自https://www.cnblogs.com/youyingchou/p/6386358.html

轉自https://blog.csdn.net/wyy6713/article/details/59500293

裝箱和拆箱

1.裝箱,值類型向引用類型轉換:

                      在托管堆中分配內存,分配的內存量是類型各字段所需的內存量+類型對象指針所需的內存量+同步塊索引所需的內存量。

                      值類型的字段復制到分配好的內存中

                      返回對象地址,現在對象地址是對象引用

2.拆箱,引用類型向值類型轉換:

                      獲取已裝箱類型中的未裝箱部分,也就是對象的原始值的各個字段

                      復制字段的值從堆中到棧中的值類型實例中

 

所以拆箱是不需要分配內存的,但是都要復制。

 

== Equals的比較

object的equlas是比較引用地址。equlas可以重寫。object的==為比較引用,為靜態方法。object的equlas為比較引用。所有類型的最終基類都是object。

 

引用類型:直接繼承與object。所以如果類型沒有重寫equlas的話,equlas默認是調用object的方法,會比較引用地址。如果重寫則調用重寫的方法。

如果類型沒有重寫==的話,會調用object的==,為比較引用地址。如果重寫,會自動調用類型的重寫的方法。

比如string類型,重寫了equlas和重寫了==,所以string的==和equlas都是比較值。

 

值類型:直接繼承System.ValueType,這個類型繼承object。System.ValueType把equlas重寫為比較值,所以如果沒有重寫,值類型的equlas都是比較值。如果重寫,則調用重寫的方法。

如果值類型沒有重載==的話,無法使用==操作。(因為都是值類型,比較引用沒任何意義,所以不調用object的==)

基元類型(比如int、double之類的)每個都重載了==,所以基元類型的==都比較值。

 

Test a = new Test { a = 1 };
Test b = new Test { a = 1 };

Console.WriteLine(a == b); //false 因為test沒有重載==,所以調用object的==,比較的是引用的地址,兩個引用類型的對象引用地址自然不同
Console.WriteLine(a.Equals(b));//false 因為Test類沒有重寫equlas,則調用的是Object.equals,而Object的equlas比較的是引用的地址

 

object c = 1;
object d = 1;

Console.WriteLine(c==d);//false  兩個參數都已經裝箱,成為引用類型,所以引用地址不同。這里調用的是object的==,所以比較引用地址
Console.WriteLine(c.Equals(d));//true,這里調用的是object.equlas,但是值類型的基類System.ValueType重寫了equlas,所以這里比較的時候比較的兩個參數的值

 

string e = "123";
string f = "123";//編譯源代碼時,編譯器必須處理每個字面值字符串,並在托管模塊的元數據中嵌入。但是這樣會讓元數據變得龐大,所以引用該字符串的所有代碼都被修改成引用元數據中的同一個字符串,所以引用地址相同。(字符串池)因此這里e和f引用的同一個字符串


Console.WriteLine(ReferenceEquals(e, f));//true 所以說明這兩個參數的引用地址相等
Console.WriteLine(e == f); //true string類型==比較值
Console.WriteLine(e.Equals(f));//true string類型equlas比較值
Console.WriteLine((object)e == (object)f); //true 這里的==調用的是object的==,比較引用,但是因為引用e和f的引用地址相同,所以還是返回true


string z = string.Copy(e);
Console.WriteLine(ReferenceEquals(z, e));//false 直接copy,另外分配內存復制值進去,就和普通引用對象一樣,所以引用地址不同
Console.WriteLine(z==e); //true 雖然引用地址已經不相同了,但是string的==比較值,所以相等

Console.WriteLine(z.Equals(e));//true 雖然引用地址已經不相同了,但是string的equals比較值

 

object j = z;
object k = e;
Console.WriteLine(j.Equals(k));//true 因為string類型重寫了object的equlas方法,所以當調用object的equlas方法的時候執行的是string的方法,比較j和k的值
Console.WriteLine(j == k);//false 調用的object的==,比較引用,所以是false(需要注意的是==是靜態方法,所以這里調用的==會直接執行object的,而上面的equlas不同,因為進行了重寫,所以會最終調用string的equlas方法)
Console.WriteLine(ReferenceEquals(j, k)); //false 地址不同

 

int h = 1;
int i = 1;


Console.WriteLine(h==i); //true 值類型==比較的兩個數的值
Console.WriteLine(h.Equals(i));//true ,值類型的基類System.ValueType重寫了equlas,使比較的時候比較的兩個參數的值

 

ReferenceEquals

object的靜態類型方法,比較兩個參數的引用地址。和==操作運算符很像,但是==是可以被重載的。所以比較引用類型的引用地址時候用這個最好。(這個方法其實就是調用默認的==比較引用地址)

 

Equlas的重寫

1.判斷傳遞的值是否為null,如果為null,則返回false

2.判斷傳遞的值和this是否引用同一地址,如果同一地址,則返回true

3.判斷傳遞的值的類型和this的類型是否一致,類型不一致,則不可能相等,返回false

4.根據傳遞的值的字段和this的字段進行比較,只要有不一致,則返回false

5.調用基類的equlas,如果為true,則返回true,如果為false,則返回false。

——————————————————————————————第二篇:基本類型優先於裝箱基本類—————————————————————————————————

在基本類型和裝箱基本類型中有3個主要區別:
1、基本類型只有值,裝箱基本類型具有與它們的值不同的統一性;
2、基本類型只有功能完備的值,而每個裝箱基本類型除了它對應基本類型的所有功能值外,還有個非功能值–null;
3、基本類型比裝箱基本類型更節省時間和空間。

看下面的一個小例子:

public class Unbelievable {
    static Integer i;
    public static void main(String[] args) {
        if (i == 42) {
            System.out.println("Unbelievable");
        }
    }
}
View Code

它不是打印出“Unbelievable”,而是在計算表達式i==42的時候,拋出“Exception in thread “main” java.lang.NullPointerException”。問題在於,i是個interger,而不是int,就像所有的對象引用域一樣,它的初始值為null,當程序計算表達式i==42的時候,它會將integer與int進行比較,幾乎在任何一種情況下,當在一項操作中混合使用基本類型和裝箱基本類型時,裝箱基本類型會自動拆箱,如果null對象引用被自動拆箱,就會得到一個NullPointerException異常,就像這個程序一樣,它可以在任何位置發生。修正這個問題很簡單,聲明i為int而不是integer就行了。

下面繼續看個小例子:

   public static void main(String[] args) {
        Long sum = 0L;
        long startTime = System.currentTimeMillis();
        for(long i = 0;i<Integer.MAX_VALUE;i++) {
            sum += i;
        }
        System.out.println(sum);
        System.out.println(System.currentTimeMillis() - startTime);
    }
View Code

這個程序運行起來比預期的要慢一些,因為不小心將局部變量sum聲明成了裝箱基本類型Long,程序編譯起來沒有警告或者錯誤,但是變量被反復的裝箱和拆箱,導致性能明顯下降。‘
將Long改成long之后,看看他們之間的性能比較:
long—–54ms;
Long—–6638ms,它們之間的性能相差了100多倍左右。
那么什么時候應該使用裝箱基本類型呢?他們有幾個合理的用處:
1.作為集合中的元素,鍵和值;
2.在參數化類型中,必須使用裝箱基本類型作為類型參數,因為java不允許使用基本類型,例如ThreadLocal
3.在會進行反射的方法調用,必須使用裝箱基本類型。

總之,在選擇的時候,基本類型要優先於裝箱基本類型。基本類型更加簡單,也更加快速。當程序用==操作進行兩個裝箱基本類型時,它做了一個同一性比較,這不是你所希望看到的。
當程序進行涉及裝箱和拆箱基本類型的混合類型計算的時候,它會進行拆箱,當程序進行拆箱時,會拋出NullPointerException異常。
最后,當程序裝箱了基本類型的值時,會導致高開銷和不必要的對象的創建。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM