问号脸:为什么 Java 中 “200==200” 为 false,而 ”100==100“ 为 true?
共 2965字,需浏览 6分钟
·
2020-11-11 13:47
点击上方蓝色字体,选择“设置星标”
优质文章,第一时间送达
阿里巴巴开发手册,(四)OOP 规约,第 7 条解释说:
【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。说明:对于 Integer var = ? 在 - 128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产 生,会复用已有对象,这个区间内的 Integer 值可以直接使用 == 进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
看下下面的代码,你猜结果是什么呢?请分析 30 秒看下结果。
public class IntegerTest {
public static void main(String[] args) {
Integer a = 100,
b = 100,
c = 200,
d = 200;
System.out.println(a == b);
System.out.println(c == d);
}
}
输出结果
true
false
是不是有一些惊喜呢?那么我们从源码上分析一下这个问题,只有你需要面试问题的时候也会说,因为缓存了 -128 到 127 之间的数值,但是为什么就缓存这么一小段呢?会不会其他包装类型也有类似的问题呢?
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过源码我们可以看出(Integer.class:892),如果 i 的内容大于 -128,小于 127,就会在 IntegerCache.cache
直接直接获取,所以如果不是在这个区间范围里面就是新对象,== 当然不相同了。我们继续阅读源码可以发现,这个配置是为了缓存对象,提高访问速度。当然我们可以通过-XX:AutoBoxCacheMax=
参数设置缓存的上限,也就是 IntegerCache.high
。所以是不是灵光一现?如果我们在平时处理一些常数级别的 Integer 的时候,恰好大于 127,那么就可以通过这个参数来提高程序的性能啦。
修改之后我们再看输出结果?
true
true
好的,那么问题又来了,我们看的源码是 valueOf 啊,莫非每次自动装箱的时候都调用这个方法吗?答案是对的,如果你还是想深入研究下,那么可以使用 IDEA 集成一下 javap 工具,反编译一下汇编代码,具体操作如下。我们需要Settings->Tools->External Tools
添加一个扩展工具,详细配置如下
很需要注意的是第二张图
Program 需要指定你 JDK 目录中 javap 的位置 Arguments 是扩展工具带的参与, -c $FileNameWithoutExtension$.class
,所以需要配置这个样子,可以点击 右侧是 Insert Macro… 查看所有关联项Working directory 要配置到 class 的输出目录,包括项目目录和 package 目录,所以内容是 $OutputPath$/$FileDirRelativeToSourcepath$
接下来我们就看到了如下内容
/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/javap -c IntegerTest.class
Compiled from "IntegerTest.java"
public class com.github.codedrinker.basic.IntegerTest {
public com.github.codedrinker.basic.IntegerTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 100
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: bipush 100
8: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: astore_2
12: sipush 200
15: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
18: astore_3
19: sipush 200
22: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: astore 4
27: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
30: aload_1
31: aload_2
32: if_acmpne 39
35: iconst_1
36: goto 40
39: iconst_0
40: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
43: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
46: aload_3
47: aload 4
49: if_acmpne 56
52: iconst_1
53: goto 57
56: iconst_0
57: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
60: return
}
我们可以清楚的看到 2,8,15,22
行对应内容是 Integer.valueOf
,所以由此可以确定,Java 在编译代码的时候,会把 Integer a =
转换为 valueOf
来赋值,是不是到此所有问题都迎刃而解?
对了,差点忘记刚才的问题,是不是别的封装类也有这个问题呢?我们继续阅读源码发现 Character
、Long
、Short
是不是同样有这个问题呢?好的,那么反编译 Long
的任务就交给你了。