真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java中的==和equals的區(qū)別有哪些

本篇內(nèi)容主要講解“Java中的==和equals的區(qū)別有哪些”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Java中的==和equals的區(qū)別有哪些”吧!

站在用戶的角度思考問題,與客戶深入溝通,找到泊頭網(wǎng)站設(shè)計(jì)與泊頭網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國(guó)際域名空間、雅安服務(wù)器托管、企業(yè)郵箱。業(yè)務(wù)覆蓋泊頭地區(qū)。

java內(nèi)存知識(shí)點(diǎn):

引用對(duì)象存儲(chǔ)的內(nèi)存是引用對(duì)象的內(nèi)存地址,類似0xa5、0xa6;基本數(shù)據(jù)類型存儲(chǔ)的常量值,例如1、2、5。==比較的就是存儲(chǔ)內(nèi)存內(nèi)容,因?yàn)橛锌赡苁莾?nèi)存地址,有可能是常量值,所以結(jié)果會(huì)產(chǎn)生混淆。

在詳細(xì)了解==與equals底層原理之前,先知道怎么使用:

  • == 遇到兩側(cè)都是對(duì)象時(shí),則比較對(duì)象的引用地址是否相同,否則全是比較其常量值是否相同

  • equals 左側(cè)必須是對(duì)象,調(diào)用該對(duì)象的equals方法,返回true則相等,對(duì)象的equals方法可被重寫

等號(hào)(==)比較的內(nèi)容與JVM底層原理

在說明之前先看下例子

public static void test1() {
     Integer a = new Integer(3);
     Integer b = 3;
     Integer c = 3;
     int d = 3;
     System.out.println(a == b);   // false
     System.out.println(a == d);   // true
     System.out.println(b == c);   // true
     System.out.println(b == d);   // true
}
a == b 為false的原因

== 兩側(cè)a和b都是對(duì)象類型,所以這里比較的是對(duì)象的引用地址是否相同。其中a是通過new關(guān)鍵字在堆內(nèi)存開辟的空間,其引用是指向該內(nèi)存空間的地址。而b則涉及到了Integer的享元模式,即JVM在啟動(dòng)時(shí)會(huì)針對(duì)Integer實(shí)例化一批Integer數(shù)據(jù)放到緩存池中,這批數(shù)據(jù)的范圍默認(rèn)是[-128,127],可以通過JVM參數(shù)調(diào)整,不在該范圍的Integer則通過new方式創(chuàng)建。通過享元模式在開發(fā)中使用該范圍內(nèi)的Integer數(shù)據(jù)時(shí),會(huì)直接從緩存池中獲取。而這里Integer b=3則是從緩存池中取出的,其引用地址也是批量初始化時(shí)開辟的內(nèi)存空間。二者不同,故返回false

a == d 為true的原因

==兩側(cè)中d是基本數(shù)據(jù)類型,另一側(cè)為包裝類型,這時(shí)會(huì)導(dǎo)致另一側(cè)自動(dòng)拆箱,Integer轉(zhuǎn)為int,轉(zhuǎn)換方法為Integer#intValue,然后才去比較。這樣==兩側(cè)就都是int類型。故二者相等。了解自動(dòng)拆箱能更清楚這點(diǎn)。

b == c 為true的原因

== 兩側(cè)都是對(duì)象類型,因此會(huì)比較地址。而這兩個(gè)對(duì)象在創(chuàng)建時(shí)符合Integer享元數(shù)據(jù)故從緩存池[-128,127]中獲取,二者引用地址指向的都是緩存池中Integer=3的內(nèi)存地址,故二者相等

b == d 為true的原因

==兩側(cè)中c是基本數(shù)據(jù)類型,另一側(cè)為包裝類型,比較原理與a == d一致。

JVM匯編指令分析

  public static void test1();
    Code:
       0: new           #3                  // class java/lang/Integer  ## 在堆內(nèi)存開辟空間,其引用入棧, stack[0]=ref (ref表示為引用對(duì)象)
       3: dup                               ## 復(fù)制棧頂元素一份, stack[1]=ref stack[0]=ref
       4: iconst_3							## 入棧常量int=3,剛開辟空間的引用指向常量int=3,stack[1]=ref_3 stack[0]=ref_3		
       5: invokespecial #4                  // Method java/lang/Integer."":(I)V  ## 調(diào)用JVM內(nèi)部生成的方法,該方法返回this=stack[0],且出棧,此時(shí)stack[0]=ref_3
       8: astore_0                          ## 出棧->入槽,將棧頂元素放入槽0 即stack[0]->salt[0]=ref_3,之后??眨ㄟ@里指的是操作數(shù)棧)
       9: iconst_3                          ## 入棧,從常量池中獲取int=3放入棧頂,即 stack[0]=3
      10: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;  ## 調(diào)用靜態(tài)方法Integer#valueOf
      13: astore_1							## 出棧->入槽1,stack[0]=salt[1]=Integer.valueOf(3).注意該方法
      14: iconst_3							## 入棧常量3,stack[0]=3
      15: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;  ## 調(diào)用靜態(tài)方法Integer#valueOf
      18: astore_2							## 出棧->入槽2,stack[0]=salt[2]=Integer.valueOf(3).注意該方法	 
      19: iconst_3                          ## 入棧常量3,stack[0]=3
      20: istore_3							## 出棧->入槽3,stack[0]=salt[3]=3	 		
      21: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream; ## 調(diào)用打印方法
      24: aload_0                           ## 出槽0->入棧 salt[0]=stack[0]=ref_3
      25: aload_1                           ## 出槽1->入棧 salt[1]=stack[0]=Integer.valueOf(3),stack[1]=ref_3
      26: if_acmpne     33					## 比較棧頂2元素引用類型的值(這里的值是內(nèi)存地址,例如0xa5,0xa6),當(dāng)結(jié)果不等時(shí)跳轉(zhuǎn)到33 			
      29: iconst_1                          ## 分支1:上邊指令不跳轉(zhuǎn),則繼續(xù),這里為入棧int1  
      30: goto          34                  ## 跳轉(zhuǎn)34直接打印結(jié)果,即兩引用類型不等則直接打印,否則跳轉(zhuǎn)33
      33: iconst_0                          ## 分支2: 對(duì)比兩引用類型相等,則入棧int=0,然后打印,這里的boolean類型,實(shí)際為int類型(0、1)
      34: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
      37: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream; ##第一個(gè)System.out.println(a == b);結(jié)束
      40: aload_0                           // 出槽0->入棧 salt[0]=stack[0]=ref_3
      41: invokevirtual #8                  // Method java/lang/Integer.intValue:()I   ## 調(diào)用靜態(tài)方法Integer#intValue
      44: iload_3                           ## 出棧->入槽3,stack[0]=salt[3]=3	 
      45: if_icmpne     52                  ## 比較int類型數(shù)值大小,當(dāng)結(jié)果不等時(shí)跳轉(zhuǎn)52,否則繼續(xù)
      48: iconst_1                          ## 分支1:數(shù)值相等時(shí)執(zhí)行,入棧常量1 stack[0]=1,這里同上,boolean值=true
      49: goto          53                     
      52: iconst_0                          ## 分支2:數(shù)值不相等時(shí)執(zhí)行,入棧常量0,stack[0]=0,這里同上,boolean值=false
      53: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V  ## 第二個(gè) System.out.println(a == d); 結(jié)束
      56: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream; 
      59: aload_1                           ## 出槽1->入棧 salt[1]=stack[0]=Integer.valueOf(3)
      60: aload_2                           ## 出槽2->入棧 salt[2]=stack[0]=Integer.valueOf(3),stack[0]=Integer.valueOf(3)
      61: if_acmpne     68                  ## 引用地址比較: 
      64: iconst_1                          ## 分支1: true
      65: goto          69
      68: iconst_0                          ## 分支2: false 
      69: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V    ## 第三個(gè)System.out.println(b == c);結(jié)束
      72: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      75: aload_1							## 出槽1->入棧 salt[1]=stack[0]=Integer.valueOf(3)
      76: invokevirtual #8                  // Method java/lang/Integer.intValue:()I 調(diào)用靜態(tài)方法Integer#intValue
      79: iload_3                           ## 出槽3->入棧 salt[3]=stack[0]=3,stack[0]=Integer.valueOf(3).intValue
      80: if_icmpne     87                  ## 比較int類型數(shù)值大小,當(dāng)結(jié)果不等時(shí)跳轉(zhuǎn)87,否則繼續(xù)
      83: iconst_1                          ## 分支1: true
      84: goto          88
      87: iconst_0                          ## 分支2: false
      88: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V   ## 第四個(gè)System.out.println(b == d);結(jié)束
      91: return                            ## 方法結(jié)束
匯編指令結(jié)論

== 兩側(cè)都是引用類型時(shí),會(huì)直接比較引用類型的存儲(chǔ)值(存儲(chǔ)的是指向的對(duì)象的地址)來(lái)判斷結(jié)果

== 兩側(cè)任何一側(cè)為引用類型時(shí),會(huì)將引用類型轉(zhuǎn)為基本數(shù)據(jù)類型(存儲(chǔ)的是常量值),然后對(duì)比數(shù)值大小來(lái)判斷結(jié)果

基本數(shù)據(jù)類型裝箱流程(這也是包裝類型的構(gòu)建過程):加載基本數(shù)據(jù)常量,調(diào)用其對(duì)應(yīng)的包裝類型的valueOf方法將基本數(shù)據(jù)類型轉(zhuǎn)為包裝類型。

包裝類型拆箱流程:調(diào)用其對(duì)應(yīng)的xxValue方法獲取其基本數(shù)據(jù)類型常量,例如Integer的intValue

再看一個(gè)示例

    public static void test2() {
        Integer a = new Integer(150);
        Integer b = 150;
        Integer c = 150;
        int d = 150;
        System.out.println(a == b);   // false
        System.out.println(a == d);   // true
        System.out.println(b == c);   // false
        System.out.println(b == d);   // true
    }
b == c 為false的原因

== 兩側(cè)都是對(duì)象類型。根據(jù)包裝類型的構(gòu)建(裝箱過程)會(huì)先調(diào)用ValueOf方法來(lái)實(shí)例化改包裝對(duì)象

針對(duì)Integer

public static Integer valueOf(int i) {
    // 享元范圍數(shù)據(jù)直接返回緩存對(duì)象 
	if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)];
	// 否則構(gòu)建對(duì)象
	return new Integer(i);
}

這里150不在默認(rèn)的享元范圍,則會(huì)重新構(gòu)建對(duì)象(開辟空間,加載常量,引用指向),此時(shí)==兩側(cè)比較的地址則不相等

針對(duì)Long: 享元范圍不可調(diào)[-128,127]

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

針對(duì)Boolean:是直接緩存的2個(gè)對(duì)象,從始至終只有2個(gè)實(shí)例:TRUE、FALSE

    public static Boolean valueOf(String s) {
        return parseBoolean(s) ? TRUE : FALSE;
    }

針對(duì)Byte

    public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }

針對(duì)Short

    public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }

針對(duì)Character

    public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

針對(duì)Float: 無(wú)緩存,全部開辟新空間

    public static Float valueOf(float f) {
        return new Float(f);
    }

針對(duì)Double:無(wú)緩存,全部開辟新空間

    public static Double valueOf(double d) {
        return new Double(d);
    }

equals比較的內(nèi)容

equals比較時(shí)左側(cè)一定為對(duì)象,那么先看下Object的equals方法

    public boolean equals(Object obj) {
        return (this == obj);
    }

可以看出來(lái),默認(rèn)的equals還是調(diào)用的==,而且是兩側(cè)都是對(duì)象的==,所以對(duì)比的內(nèi)存地址。equals的使用產(chǎn)生混淆的主要原因是equals能被重寫。

針對(duì)Integer: 重寫為基本數(shù)據(jù)類型值比大小

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

針對(duì)Long: 重寫為基本數(shù)據(jù)類型值比大小

    public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

針對(duì)Byte

    public boolean equals(Object obj) {
        if (obj instanceof Byte) {
            return value == ((Byte)obj).byteValue();
        }
        return false;
    }

針對(duì)Boolean

    public boolean equals(Object obj) {
        if (obj instanceof Boolean) {
            return value == ((Boolean)obj).booleanValue();
        }
        return false;
    }

針對(duì)Short

    public boolean equals(Object obj) {
        if (obj instanceof Short) {
            return value == ((Short)obj).shortValue();
        }
        return false;
    }

針對(duì)Float: 浮點(diǎn)類型轉(zhuǎn)int,然后比大小,轉(zhuǎn)換方式為將浮點(diǎn)存儲(chǔ)的二進(jìn)制數(shù)據(jù)當(dāng)做int類型二進(jìn)制直接讀取

    public boolean equals(Object obj) {
        return (obj instanceof Float)
               && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
    }

針對(duì)Double:浮點(diǎn)類型轉(zhuǎn)long,然后比大小,轉(zhuǎn)換方式為將浮點(diǎn)存儲(chǔ)的二進(jìn)制數(shù)據(jù)單子long類型二進(jìn)制直接讀取

    public boolean equals(Object obj) {
        return (obj instanceof Double)
               && (doubleToLongBits(((Double)obj).value) ==
                      doubleToLongBits(value));
    }

建議:Float與Double比較大小時(shí),使用equals,如果使用==則比較的是引用地址。

主要的混淆對(duì)象是String

針對(duì)Character:比較的是引用地址

public final boolean equals(Object obj) {
  return (this == obj);
}

針對(duì)String: 先比較地址,地址相同則為true,否則比較每一個(gè)char的地址,一個(gè)不同則為false

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

示例

    public static void test4(){
        String str1 = "const";
        String str2 = "const";
        String str3 = "con" + "st";
        String str4 = "con" + new String("st");
        String str5 = new String("const");
        String str6 = str5.intern();
        String str7 = "con";
        String str8 = "st";
        String str9 = str7 + str8;
        System.out.println(str1 == str2); //true
        System.out.println(str1 == str3); //true
        System.out.println(str1 == str4); //false
        System.out.println(str4 == str5); //false
        System.out.println(str1 == str6); //true
        System.out.println(str1 == str9); //false
    }

這里比較,涉及到了常量與變量的區(qū)別:常量在編譯期會(huì)直接放入常量池中,變量只會(huì)在運(yùn)行時(shí)進(jìn)行賦值。

編譯期優(yōu)化: 常量相加可被直接優(yōu)化,存儲(chǔ)的是優(yōu)化后的結(jié)果。

String#intern,會(huì)從常量池中獲取首次創(chuàng)建該字符串的地址引用,如果不存在則創(chuàng)建后放入,再返回該引用。注意該引用是常量池引用,與new出來(lái)的堆引用不是一個(gè)。

到此,相信大家對(duì)“Java中的==和equals的區(qū)別有哪些”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


網(wǎng)站題目:Java中的==和equals的區(qū)別有哪些
URL網(wǎng)址:http://weahome.cn/article/jieeej.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部