今天在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() == 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™ 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比较稳妥一些。