面试题之-Java中判断200==200为false但127==127为true的疑问
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=<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.
机器人翻译:
缓存以支持JLS要求的-128和127(包括)之间值自动装箱的对象标识语义。缓存在第一次使用时初始化。缓存的大小可以由-XX:AutoBoxCacheMax=<size>选项控制。在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最大值可以参数修改
jvm学习-01-类加载过程
风雪十年灯
SegmentFault 思否面试闯关挑战赛!
SegmentFault思否赞 13阅读 11.2k评论 16
疫情已过,2023 我的前端面试记录
linong赞 11阅读 513
如何写一个让面试官满意的 Generator 执行器?
Samon赞 12阅读 3.8k
vue面试题
墨城赞 5阅读 423
简历上的项目,需要这样描述才有亮点!
小傅哥赞 4阅读 478
记一次旁观他人的技术面试
陟上晴明赞 4阅读 553
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。