简单谈一下java中的==与equals()方法

今天在java问答区看到一个问题,Integer a=100,b=100,c=200,d=200;为什么a==b返回true而c==d返回false?简单的想了一下,又看到问题下面的评论确实解释了原因。不过在这里我先不作解释,下面慢慢比较中,相信你就会明白原因。

谈到==很容易想到另一个跟它功能很相似的equal()方法,下面我们就从程序入手来看二者吧。

package com.szhtp.cmbchina.normal.test;
/**
 * ==与equal比较
 * @Description:
 * @author Biejh
 * @date 2017年9月12日 下午4:28:45
 */
public class NormalCompare {
    public static void main(String[] args) {
	String str1, str2, str3, str4;
	str1 = "axby";
	str2 = "axby";
	str3 = new String("axby");
	str4 = str3.intern();
	System.out.println(str1 == str2);
	System.out.println(str1 == str3);
	System.out.println(str1 == str4);
	System.out.println(str1.equals(str2));
	System.out.println(str1.equals(str3));
	System.out.println(str1.equals(str4));
    }
}

运行结果呢,意料之中分别是true,false,true,true,true,true。那么为什么呢?这里我们要说一下,"=="在java中如果是值对象,比较的是值,而如果是引用对象,比较的则是引用;而equal比较的是对象的内容。所以第二个false和后三个true毫无疑问,而第一个true和第三个true是为什么呢?

原来,根据jvm运行机制,为了提升性能和减少开销,避免字符串在内存中重复创建,会在它的常量缓冲池里维护一片区域用于所有字符串的存储,也有地方将该区域称为字符串池(String Pool)。对于我们这样赋值型的创建字符串,程序在jvm运行时,会在String Pool中查询是否已有该字符串的存在,如果有就将该内容的引用地址返回给被赋值变量,如果没有就在该区域创建一个新的字符串,并将引用地址返回。由此说明,第一个true是因为str1在String Pool中已创建了"axby",在对str2赋值时,它引用的地址和str1引用的地址是一样的。而第三个true又是为什么呢?

我们看到程序中str4=str3.intern();而str3=new String("axby");也就是说str3的创建是new出来的,它跟str1和str2指向的地址空间是不一样的,但因为调用了intern()方法致使str4和str1,str2的地址空间一样了,那么intern()方法究竟是什么呢,我们看一下源码及说明:

/**
  * Returns a canonical representation for the string object.
  * <p>
  * A pool of strings, initially empty, is maintained privately by the
  * class <code>String</code>.
  * <p>
  * When the intern method is invoked, if the pool already contains a
  * string equal to this <code>String</code> object as determined by
  * the {@link #equals(Object)} method, then the string from the pool is
  * returned. Otherwise, this <code>String</code> object is added to the
  * pool and a reference to this <code>String</code> object is returned.
  * <p>
  * It follows that for any two strings <code>s</code> and <code>t</code>,
  * <code>s.intern()&nbsp;==&nbsp;t.intern()</code> is <code>true</code>
  * if and only if <code>s.equals(t)</code> is <code>true</code>.
  * <p>
  * All literal strings and string-valued constant expressions are
  * interned. String literals are defined in section 3.10.5 of the
  * <cite>The Java&trade; Language Specification</cite>.
  *
  * @return  a string that has the same contents as this string, but is
  *          guaranteed to be from a pool of unique strings.
  */
  public native String intern();

简单翻译一下:一个初始为空的字符串池,它由类String独自维护。当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。 对于任意两个字符串s和t,当且仅当s.equals(t)为true时,s.instern() == t.instern才为true。所有字面量字符串和字符串赋值常量表达式都使用 intern方法进行操作。

注释的大概意思就是调用intern()方法时,如果pool中已经含有一个和该字符串内容一样的已经被创建的对象,那么返回的字符串地址就是pool中已创建的字符串的地址;如果没有则先在pool中创建,再将地址返回。这么说来str1==str4返回true就没什么疑问了,原来str4指向的地址跟str1的地址相同。

现在我们回归到开头我看到的那个问题,为什么返回的结果不一样呢?我们知道Integer不是java的基本数据类型,它是int的封装类。Integer可以为null,它是一个类对象,而int是值。我们看一下Integer的构造方法:

 /**
   * Constructs a newly allocated {@code Integer} object that
   * represents the specified {@code int} value.
   *
   * @param   value   the value to be represented by the
   *                  {@code Integer} object.
   */
   public Integer(int value) {
        this.value = value;
   }
/**
  * Constructs a newly allocated {@code Integer} object that
  * represents the {@code int} value indicated by the
  * {@code String} parameter. The string is converted to an
  * {@code int} value in exactly the manner used by the
  * {@code parseInt} method for radix 10.
  *
  * @param      s   the {@code String} to be converted to an
  *                 {@code Integer}.
  * @exception  NumberFormatException  if the {@code String} does not
  *               contain a parsable integer.
  * @see        java.lang.Integer#parseInt(java.lang.String, int)
  */
  public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
  }

然后我们来通过程序做一下比较:

package com.szhtp.cmbchina.mass.test;
/**
 * ==与equal比较
 * @Description:
 * @author Biejh
 * @date 2017年9月12日 下午4:28:45
 */
public class NormalCompare {
    public static void main(String[] args) {
	String str1, str2, str3, str4;
	str1 = "axby";
	str2 = "axby";
	str3 = new String("axby");
	str4 = str3.intern();
	System.out.println(str1 == str2);
	System.out.println(str1 == str3);
	System.out.println(str1 == str4);
	System.out.println(str1.equals(str2));
	System.out.println(str1.equals(str3));
	System.out.println(str1.equals(str4));
	System.out.println();
	
	char chr1, chr2, chr3;
	chr1 = '1';
	chr2 = '1';
	chr3 = new Character('1');
	System.out.println(chr1 == chr2);
	System.out.println(chr1 == chr3);
	System.out.println(Character.compare(chr1, chr2));
	System.out.println(Character.compare(chr1, chr3));
	System.out.println();
	
	float f1, f2, f3;
	f1 = 0.1f;
	f2 = 0.1f;
	f3 = new Float(0.1f);
	System.out.println(f1 == f2);
	System.out.println(f1 == f3);
	System.out.println(Float.compare(f1, f2));
	System.out.println(Float.compare(f1, f3));
	
	double d1, d2, d3;
	d1 = 0.1;
	d2 = 0.1;
	d3 = new Double(0.1);
	System.out.println(d1 == d2);
	System.out.println(d1 == d3);
	System.out.println(Double.compare(d1, d2));
	System.out.println(Double.compare(d1, d3));
	System.out.println();
	
	Integer t1, t2, t3, t4, t5, t6, t7, t8;
	int n1 = 100;
	t1 = 100;
	t2 = 100;
	t3 = 200;
	t4 = 200;
	t5 = new Integer(100);
	t6 = new Integer(100);
	t7 = new Integer(200);
	t8 = new Integer(200);
	System.out.println(n1 == t1);
	System.out.println(t1 == t2);
	System.out.println(t3 == t4);
	System.out.println(t5 == t6);
	System.out.println(t7 ==t8);
	System.out.println(t1.equals(t2));
	System.out.println(t3.equals(t4));
	System.out.println(t5.equals(t6));
	System.out.println(t7.equals(t8));
	
    }
}

Integer部分的运行结果是true,true,false,false,false,true,true,true,true。我们可以先看一下equals()在Integer中的源码:

/**
  * 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()方法是value比较所以返回为true,我们毫无疑问。那么那些false呢?

t5,t6,t7,t8都是new出来的,由于引用地址空间不一样所以,所以对于==比较返回为false也毫无疑问。而t3和t4是通过字面量赋值,为什么==比较会不一致呢?我们看一下JDK中对Integer的valueOf()的解释:

public class temp {
    /**
     * 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 -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.
     */

    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) {
                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);
            }
            high = h;

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

        private IntegerCache() {}
    }

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

}

可以看到,对于Integer的字面量赋值,如果其value如果在-128到127之间的,一切值对象引用是在cache中,而当超过这个范围的值,需要new,其引用的地址空间必然是不一样的,总结一句就是对Integer类型的数值的比较,用equals()的value比较稳妥一些。

转载于:https://my.oschina.net/u/3676895/blog/1536659

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值