刚来深圳不久,边找工作边刷题中。周末下雨刷了一道String的题目,大概意思如下:
public class TestString {
public static void main(String[] args) {
String a = "a";
String aa;
aa = "a";
String b = "b";
String ab = "ab";
System.out.println((a+b) == ab);
System.out.println("--------------");
System.out.println((aa+b) == ab);
}
}
String是引用数据类型,底层虽然是不可变的(final),但是此时字符串常量池已存在a,aa,b,ab,四个对象,而==比较的是地址值,很显然此时对应的结果为false,false,运行结果如下:
如果在题目原有的String前面再加一个final呢?结果又是怎样?
public class TestString {
public static void main(String[] args) {
final String a = "a";
final String aa;
aa = "a";
final String b = "b";
final String ab = "ab";
System.out.println(a+b);//为了方便对比反编译的结果
System.out.println("--------------");
System.out.println((a+b) == ab);
System.out.println("--------------");
System.out.println((aa+b) == ab);
}
}
运行结果如下:
对于我这种小白来说有点amazing,后来我尝试反编译得到了以下结果:
public class TestString
{
public TestString()
{
}
public static void main(String args[])
{
String a = "a";
String aa = "a";
String b = "b";
String ab = "ab";
System.out.println("ab");
System.out.println(true);
System.out.println("--------------");
System.out.println((new StringBuilder(String.valueOf(aa))).append("b").toString() == "ab");
}
}
final修饰的String为常量,编译器会自动将(a+b)转换成ab,此时常量池中存在ab,当有相同的值时会优先对比常量池,有则直接指向这个地址,所以也就有了true这个结果。
再做改变则是如果==换成equals,此时比较的是字符串内容,得到的结果则是true,true。
说到==和equals的区别,不难想到经典的Integer和int的问题,查看Integer其中两个方法的源码如下:
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() {}
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
由源码很容易知道,Integer有一个IntegerCache()的方法,其作用是缓存-128~127的数,超过这个范围时,会new一个新的Integer的对象。所以我写了如下的测试代码:
public class TestInteger {
public static void main(String[] args) {
Integer a = new Integer(10);//Integer a = 10;
Integer c = new Integer(128);//Integer c = 128;
int b = 10;
int d = 128;
System.out.println(a == b);//true拆装箱
System.out.println(a.equals(b));//true值大小相等
System.out.println("--------------");
System.out.println(c == d);//true拆装箱
System.out.println(c.equals(d));//true值大小相等
System.out.println("--------------");
Integer e = 6;
Integer f = new Integer(6);
Integer x = 6;
Integer y = new Integer(6);
Integer g = 360;
Integer h = new Integer(360);
System.out.println(e == f);//false产生了新的值的拷贝导致地址值不一样
System.out.println(e.equals(f));//true值大小相等
System.out.println("--------------");
System.out.println(e == x);//true值大小相等
System.out.println(f == y);//true从缓存中获取都是指向同一个地址
System.out.println("--------------");
System.out.println(g == h);//false地址值不一样
System.out.println(g.equals(h));//true值大小相等
}
}
运行结果如下:
总结:final修饰的String编译器会直接拼接而不是通过String调用new StringBuild()的方法,==比较的是地址值,new()会改变所指向地址值的改变,Integer缓存方法(-128~127)是例外,equals比较的是内容。