1. 程式人生 > 其它 >基於基本型別對“==”和“equals()”的探究

基於基本型別對“==”和“equals()”的探究

首先宣告所有的探究都是基於jdk1.8.0_144版本上進行的。

開始之前我們先來看一段程式碼:

檢視程式碼


        int x = 0;
        double y = 0.0;
        System.out.println(x == y);//true
        System.out.println(Integer.hashCode(x));//0
        System.out.println(Double.hashCode(y));//0
        System.out.println(Objects.equals(x, y));//false

這裡我們聲明瞭兩個不同型別的變數,並且給他們賦了不同的值,可以用“==”進行判斷是發現返回的值是true,這個返回結果並不是我們所期待的,呼叫equals()方法返回的結果才是我們想看到的。同時,我們也看到了兩個變數他們的雜湊碼值是一樣的,這又是為什麼呢?

首先我們要知道java語言中是如何來給變數進行賦值的。

        Double m = Double.valueOf(9.9);//包箱
        double n = m.doubleValue();//拆箱
        Integer x = Integer.valueOf(6);//包箱
        int y = x.intValue();//拆箱

在Java中變數的賦值並不是我們所看到的那樣單純的將值賦給變數。每個基本資料型別Java中都會有一個對應的包裝類,每個類中呼叫.valueOf()就是對值進行自動包箱,呼叫.基本型別名Value()自動拆箱。下面我們來看一個例子:

檢視程式碼


        System.out.println("==========Integer==========");
        Integer a = new Integer(99);
        Integer b= new Integer(99);
        System.out.println(a == b);//false
        System.out.println(b.equals(a));//true
        Integer c = Integer.valueOf(99);
        Integer d = Integer.valueOf(99);
        System.out.println(c == d);//true
        System.out.println(c.equals(d));//true
        int e = c.intValue();
        System.out.println(Integer.hashCode(e));//99
        int f = d.intValue();
        System.out.println(Integer.hashCode(f));//99
        System.out.println(e == f);//true
        System.out.println(Objects.equals(e,f));//true

這裡我們先new了兩個Integer類。此時堆中會有兩個new的Integer類,用“==”進行判斷返回值是false,由此我們可以看到“==”是比較兩個物件的記憶體地址是否相同的。後面我們對資料進行Integer型別的包箱,此時用“==”進行判斷返回值是true。好,我們來看一下valueOf()方法:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

這時候我們發現valueOf()方法中有一個IntegerCache的類:

檢視程式碼


    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

通過註解可以知道這個快取池是存在一個範圍的下限是-128,上限是127(包含127)。快取在第一次使用是會被初始化,它的大小可能由AutoBoxCacheMax=<size>這個選項控制。此時,變數c,d都是存放快取中99所在的記憶體地址,因此"=="判斷返回的是true。

接下來我們來看equals()方法:

檢視程式碼


    /**
     * Compares this object to the specified object.  The result is
     * {@code true} if and only if the argument is not
     * {@code null} and is an {@code Integer} object that
     * contains the same {@code int} value as this object.
     *
     * @param   obj   the object to compare with.
     * @return  {@code true} if the objects are the same;
     *          {@code false} otherwise.
     */
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

equals()比的是兩個物件的值,所以即使不是同一個物件,他們的值是相同的那麼返回的值也是true。

ok,我們Integer類講完了那我們來看看Doule類看看會有什麼不一樣呢?我先看一段程式碼:

檢視程式碼


        System.out.println("==========Double==========");
        Double j = new Double(123.0);
        Double k = new Double(123.0);
        System.out.println(j == k);//false
        System.out.println(j.equals(k));//true
        Double m = Double.valueOf(123);
        Double n = Double.valueOf(123);
        System.out.println(m == n);//false
        System.out.println(m.equals(n));//true
        System.out.println("------------------");
        double x = m.doubleValue();
        System.out.println(Double.hashCode(x));//1079951360
        double y = n.doubleValue();
        System.out.println(Double.hashCode(y));//1079951360
        System.out.println("-------------------");
        System.out.println(Objects.equals(x, y));//true
        System.out.println(x == y);//true

如果按照前面Integer類的情況,我們呼叫完valueOf()方法後應該會初始化一個快取區,我們會從快取區中得到我們想到的資料,將資料在快取區中的記憶體地址賦值給變數。可是實際上並不是這樣的。

    public static Double valueOf(double d) {
        return new Double(d);
    }
    public Double(double value) {
        this.value = value;
    }
    private final double value;
    public double doubleValue() {
        return value;
    }

它會new一個新的Double類將資料進行包箱,所以“m==n”返回值是false,它沒有我們上面所講的快取區這一個說法。上面我們講了equals()是對於物件值的比較因此返回的是true;由於doubleValue()是直接返回Double類包箱的值,返回出x,y的hashCode()值發現他們的雜湊碼值是相同的即他們是同一個物件。所以“x==y”返回值也為true。