Java中判断200==200为false但127==127为true的疑问
1. 问题的引出
1.1 200==200为false吗?
代码:
package com.niewj.nio;
/**
* Created by niewj on 2020/9/20 11:24
*/
public class Test {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println("包装类 127 == 127: "+ (i1 == i2));
Integer i3 = 200;
Integer i4 = 200;
System.out.println("包装类 127 == 127: "+ (i3 == i4));
int i5 = 200;
int i6 = 200;
System.out.println("非包装int型 200 == 200: " + (i5 == i6));
}
}
控制台输出:
包装类 127 == 127: true
包装类 127 == 127: false
非包装int型 200 == 200: true
看现象得结论:
1). 原生类型的200==200为true
2). 包装类Integer的200==200为false
3). 包装类Integer的127==127为true
看结论得疑惑? why??
对象类型的==比较的不是 对象的内存地址吗? 怎么这两个对象难道是一个?? 下来我们找找原因:
1.2 为什么包装类127==127为true
翻开随身携带的记事本, 写着许多事, 都是关于你….你讨厌被冷漠….习惯被守候…
跑题了, 我们不唱歌!
翻开jdk的源码, Integer:829行:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
看代码能开出些许动过刀的嫌疑, 没错, 方法的文档注释里也说了:
1). -128到127的值被缓存
此方法将始终缓存范围为-128到127(包括在内)的值,并可能缓存此范围之外的其他值。
没错了, 这就是问题的原因, 辣么, 咋个实的现?
2. 问题的原因探究
Integer类内部有个static的内部类IntegerCache, 就是实现的原因:
2.1 IntegerCache的实现:
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() {}
}
类的文档注释:
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=
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.
机器人翻译:
缓存以支持JLS要求的-128和127(包括)之间值自动装箱的对象标识语义。缓存在第一次使用时初始化。缓存的大小可以由-XX:AutoBoxCacheMax=
选项控制。在VM初始化期间,java.lang.Integer.IntegerCache。高属性可以设置和保存在私人系统的阳光属性。VM类。
2.2 IntegerCache类实现简析:
1). IntegerCache类特点:
- static class : 只随着类的加载, 初始化一次, 跟后续的不管多少个Integer的实例无关;
- static Integer cache[] : 所有Integer实例都可以共享的 cache;
- static int low=-128: 最小值为 -128;
- static int high; 最大值(static里有逻辑, 默认是127)
- static{}块中初始化逻辑
2). 着重分析下static{}块中初始化逻辑
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")
代码中读取了这个属性, 然后用它跟127比, 取大的, 作为最大缓存值;
关于这个属性的意义和操作空间, 我们在最后一部分详述:问题的逆转?! 部分
现在只要知道, 默认的话, 就是:
- 初始化了一个 static的Integer数组, 容量是256个, 存放的内容就是-128到127之间的值
- 我们在声明和初始化一个Integer变量的时候, 如果值的区间在 -128到127之间, 就会从这个cache数组中取;
- 由于这个cache数组是静态的, 所以所有的Integer的实例可以共享
- 所以, 包装类型的Integer 127==127 实际上都是这个cache中的第一次初始化的127的Integer对象, 必然是同一个, 就true了;
- 但是200就不一样了, cache的区间只有 -128到127, 200的并没有缓存, 所以会每次初始化新的Integer对象实例
3). Integer的-128到127之间的值被缓存, 非此区间的每次都是新实例对象;
3. 问题的扩展:Byte/Short/Long/Character
原因已经找到了, 我们该结束了? 不不不, 这可是一道面试题, 你不发挥发挥? 你不发挥可能就被挥发了….危….
Integer有缓存了, 那么其他的整数类型呢?
Byte/Short/Character/Long 会不会也是共犯?
我们揪出源码审问一下:
3.1 Byte的缓存-ByteCache
Byte类还没打就招了:
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
由于Byte本身体弱能力有限, 看到刑拘就招认了; 我本身就只占一个字节, 也没什么花花肠子, 我坦白吧: 我甚至都不用什么属性配置类, 我的实现也很简单粗暴, 够用就行!
1). ByteChache实现了Byte中的-128到127的缓存
3.2 Short的缓存-ShortCache
Short占2个字节, 它的实现也简单粗暴, 够用就行, 不耍花花绕:
private static class ShortCache {
private ShortCache(){}
static final Short cache[] = new Short[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Short((short)(i - 128));
}
}
1). ShortCache实现了Short中的-128到127的缓存
3.3 Long的缓存-LongCache:
Long可是占8个字节的, 壮的一匹! 但是实现也是简单粗暴, 纯情的壮汉一枚!
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
1). LongCache实现了Long中的-128到127的缓存
3.4 Character的缓存CharacterCache:
Character表示, 我只有2个字节, 本身是要表示字符的, 表示数字就是兼职交个朋友, 我也很纯情:
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
1). CharacterCache实现了Character中的-128到127的缓存
4. 问题有逆转?!
再看个程序:
package com.niewj.nio;
/**
* -XX:AutoBoxCacheMax=256
* System.out.println(sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"));
* Created by niewj on 2020/9/19 0:24
*/
public class Test {
public static void main(String[] args) {
Integer i3 = 256;
Integer i4 = 256;
Integer i5 = 257;
Integer i6 = 257;
System.out.println("包装类 256 == 256: "+ (i3 == i4));
System.out.println("包装类 257 == 257: "+ (i5 == i6));
}
}
程序输出:
包装类 256 == 256: true
包装类 257 == 257: false
什么??!! 包装类Integer啊! 256==256怎么是true? 257==257怎么又是false?
鬼火?! what the shark! 啥情况?
没错, 我们在main方法的注释里已经标明了, 是在启动时, 加了JVM参数, 设置了:
-XX:AutoBoxCacheMax=256
所以此时, Integer缓存的范围就不是 -128到127了, 而是-128到256;
JVM给了Integer类一个操作空间, 可以扩大缓存的范围! 我们通过JVM参数扩大到256了, 所以256比较是同一个对象, 但是257就否了!
4.1 -XX:AutoBoxCacheMax此JVM参数可以扩大Integer里cache的上限值
1). 设置jvm参数: -XX:AutoBoxCacheMax=256
package com.niewj.nio;
public class Test {
public static void main(String[] args) {
Integer i3 = 256;
Integer i4 = 256;
Integer i5 = 257;
Integer i6 = 257;
System.out.println("包装类 256 == 256: "+ (i3 == i4));
System.out.println("包装类 257 == 257: "+ (i5 == i6));
System.out.println("java.lang.Integer.IntegerCache.high=\t" + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"));
}
}
控制台输出:
包装类 256 == 256: true
包装类 257 == 257: false
java.lang.Integer.IntegerCache.high= 256
可以看到, 这个参数已经被改了: java.lang.Integer.IntegerCache.high
2). -XX:AutoBoxCacheMax参数改的就是java.lang.Integer.IntegerCache.high的值
4.2 那么, Byte/Short/Long/Character这些类呢? 能扩容么
package com.niewj.nio;
public class Test {
public static void main(String[] args) {
Integer i1 = 256;
Integer i2 = 256;
System.out.println("Integer包装类 256 == 256: "+ (i1 == i2));
Short s1 = 256;
Short s2 = 256;
System.out.println("Short包装类 256 == 256: "+ (s1 == s2));
Long l1 = 256L;
Long l2 = 256L;
System.out.println("Long包装类 256 == 256: "+ (l1 == l2));
Character c1 = 256;
Character c2 = 256;
System.out.println("Character包装类 256 == 256: "+ (c1 == c2));
System.out.println("java.lang.Integer.IntegerCache.high=\t" + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"));
}
}
控制台输出:
Integer包装类 256 == 256: true
Short包装类 256 == 256: false
Long包装类 256 == 256: false
Character包装类 256 == 256: false
java.lang.Integer.IntegerCache.high= 256
其实从上面的代码分析我们也可以看出, 除了Integer, 其他的类都没有做参数读取; 都只用了127这个固定值;
1). 只有Integer的缓存127最大值可以参数修改
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com