Java中的对象比较

来源:互联网 发布:熔炉的真实事件 知乎 编辑:程序博客网 时间:2024/05/22 06:39

关于Java中的数值比较

首先明确一点:

“==” 永远是在比较地址,而equals是比较内容的值。

当我们书写这样的代码:

int a = 10;int b = 10;

JVM 其实是在栈(栈帧中的操作栈?)中查找 3 这个常量,如已经存在了,变量 a 的引用就指向存放 3 的地址,如果没有,就创建一个。因此给基本数据类型的变量赋值时,若它们的值相等,那它们在内存中的地址也相等。a == b 看似是值的比较,实际上也是常量地址的比较。

包装类的比较

Integer举例,看一下代码:

Integer n1 = 100;Integer n2 = 100;Integer n22 = Integer.valueOf(100);Integer n222 = new Integer(100);Integer n3 = 1000;Integer n4 = 1000;System.out.println(n1==n2); // trueSystem.out.println(n1==n22); // trueSystem.out.println(n1==n222); // falseSystem.out.println(n3==n4); // false

从输出结果可以看到,直接赋值 和使用 valueOf() 赋值的方式,两个变量的比较结果都是 true ,但是 new 一个新的对象来比较就是 false 。这是为什么呢?

自动拆装箱机制中可以了解到,直接给包装类赋值其实就是执行了 valueOf() 方法,然后我们又知道 == 比较的是两个对象的引用地址,这说明直接赋值和使用 valueOf() 方法没有改变对象的引用地址,因此比较结果是 true 。而 new 一个新对象肯定有不同的地址,所以比较结果是false。看一下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        // 最大值 high 可以被修改        ...省略        }        high = h;        cache = new Integer[(high - low) + 1];        int j = low;        for(int k = 0; k < cache.length; k++)            cache[k] = new Integer(j++);        ...省略    }}public static Integer valueOf(int i) {    if (i >= IntegerCache.low && i <= IntegerCache.high)        return IntegerCache.cache[i + (-IntegerCache.low)];    return new Integer(i);}

这个 IntegerCacheInteger 的静态内部类(嵌套类),而 cahce 是一个数组,作为一个常量池,顺序存储了最小值 low(-128) 到最大值 high(127) 之间的所有整数的 Integer对象。从源码中可以看出,当 i 的值在 [ low, high ] 范围内时,直接返回数组中对应值的地址,否则就创建一个新的对象。

因此前面 n1==n2 的结果是 true,而 n3==n4 的结果是 false,因为 n1n2 都是引用数组 cache 中100这个值的地址,而1000超过了范围,因此创建了新的对象,引用的地址就不一样了。

有常量池设置的包装类及其范围有:

包装类 常量范围 Byte [ -128, 127 ] Short [ -128, 127 ] Integer [ -128, 127 ] Long [ -128, 127 ] Character [ 0, 127 ]


  • 其中 byte 占8位的,可以表示的值也是在 [ -128, 127 ] 范围内,所以任何 Byte 对象比较结果都是 true
  • Integer 常量池的的最大值 high 可以通过JVM的参数设置来修改(从源码上看应该是这样),默认值是127。
  • Character 中常量池保存的是字符编码中,0 到 127 的字符。

FloatDouble 因为值是不连续的,所以不设置常量池,Boolean 也没必要设置常量池。

String 的比较

String 类也维护有缓冲池,给对象赋值时,会把字符串加入到缓冲池中,下次赋值时,若缓冲池中含有相同内容的字符串对象,则直接指向它在缓冲池中的地址。

String s1 = "hello";String s2 = "hello";System.out.println(s1==s2); // trueString s3 = "hel";String s4 = "lo";String s5 = "hel" + "lo";String s6 = s4 + s5;System.out.println(s1==s5); // trueSystem.out.println(s1==s6); // false

在给 s1 赋值后,对象被加入到 String 类的缓冲池中,因此在给 s2 赋值时,s2 的引用直接指向缓冲池中内容为”hello”的对象地址,因此 s1==s2 的结果自然就是 true 了。

编译阶段编译器会把两个字符串合成一个,因此 s1==s5 的结果也是 true。但编译器不会把两个对象合并,因此 s1==s6 的结果是 false,以为它们的引用地址不相同。

原创粉丝点击