本文主要大致思路为:
不管从工作中还是面试,这篇文章都应该好好看完,本人认为是非常有用的。
案例
Integer是基本类型int的封装类。平时不管是入坑多年的小伙伴还在入坑路上的小伙伴,都应该知道的使用频率是相当高。
下面模仿订单支付,做了一个订单支付状态枚举类PayStatusEnum
1 | java复制代码 public class IntegerDemo { |
结果输出什么?
把上面代码中的8改成128后,又输出什么?
1 | java复制代码 public class IntegerDemo { |
答案慢慢道来。
解析案例
Integer整体阅览
构造方法
1 | java复制代码 private final int value; |
太简单了,没什么可讲的。
valueOf()方法
1 | java复制代码 public static Integer valueOf(String s) throws NumberFormatException { |
上面valueOf()方法中用到了IntegerCache,下面我们来聊聊。
IntegerCache
下面是IntegerCache源码和部分注释:
1 | java复制代码 /** |
整个静态块逻辑为:
那么,如何设置java.lang.Integer.IntegerCache.high的值呢?
The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
注释中已经说清楚,可以使用-XX:AutoBoxCacheMax=设置。
写个demo来debug看看
1 | java复制代码 public class IntegerDemo { |
设置-XX:AutoBoxCacheMax=100
开始debug
看看high的值
是127,那就对了,因为上面
设置-XX:AutoBoxCacheMax=130
开启debug模式
注意:low=-128是不会变的,整个缓存初始化过程并没有对low进行修改,再说low是常量。
-XX:AutoBoxCacheMax最大能设置成多大?
因为Integer的最大值是2147483647 ,所以我们这里使用这个值试试,
开始debug,直接报OOM了
为什么会OOM呢?
如果-XX:AutoBoxCacheMax没有设置值,那么对应数组是这样的。
equals()方法
上面的案例中有equals方法,这里把这个方法也拿出来聊聊
1 | java复制代码 private final int value; |
回到上面的案例中
当我们使用equals方法比较两个对象是否相等的时候其实就是比较他们的value值。
所以不管是128还是8,equals后肯定都是true。
当引用类型使用==进行比较的时候,此时比较的是两个引用的对象的地址,是不是同一个。
1 | java复制代码 public class IntegerDemo { |
我们看看器class文件中的字节码;
本地变量表
每个本地变量赋值的过程
这里我们可以得出一个结论:
Integer c =8;就是Integer c = Integer.valueOf(8);
上面Integer b = Integer.valueOf(8);,那就说明变量b和c都是使用Integer.valueOf()获取到的。
valueOf方法中
-XX:AutoBoxCacheMax不设置
上面关于IntegerCache的low和high已经进行了说明,low永远是-128,所以当我们没有设置
-XX:AutoBoxCacheMax 的值的时候,这时候 high=127。
当Integer.valueOf(8);的时候,就会进入上面代码中的if中。然后从IntegerCache中的数组cache中获取。
但是IntegerCache中的cache数组是个常量数组。
言外之意,就是一旦给这个数组cache赋值后,就不会变了。
Integer b = Integer.valueOf(8);和Integer c=8;
b和c不就是都指向同一个引用地址吗?
所以 b==c为true;
但是Integer b=Integer.valueOf(128);
此时就不进入if中,而是直接new一个Integer对象
所以此时
Integer b=Innteger.valueOf(128) ;和Integer c = 128;
都会各自new 一个Integer对象,
此时的b==c为false。
这里也就是网上很多文章也就说到这里,就是比较当前int i 这个i是不是在-128到127范围之内。
-XX:AutoBoxCacheMax设置为130
如果我们把-XX:AutoBoxCacheMax设置为130。那么上面
Integer b=Innteger.valueOf(128) ;和Integer c = 128;
也会进入if条件中。
最后b==c为true。
如何避坑
Integer是基本类型int的封装类,那么在平常使用的时候需要注意几点:
1,如果使用Integer,注意Integer的默认是null,容易引起空指针异常NullPointerException。
2,如果使用int类型,注意int类型的初始值是0,很多设计某某状态时,很喜欢用0作为某个状态,这里要小心使用。
3,另外从内存使用层面来讲,int是基本数据类型,只占用4个字节,Integer是一个对象,当表示一个值时Integer占用的内存空间要高于int类型,从节省内存空间考虑,建议使用int类型(建议了解一下Java对象内存布局)。
4,Integer使用的时候,直接赋值,Integer c = 8,不要new Integer(8)。因为直接赋值就是Integer.valueOf方法使用缓存,没必要每次都new一个新对象,有效提高内存使用。
5,如果系统中大量重复的使用比127大的数,建议JVM启动的时候为-XX:AutoBoxCacheMax=size 适当的大小,提升内存使用效率(但是也不能太大,上面我们已经演示了可能出现OOM)。
面试题
面试题1
1 | java复制代码 Integer num1 = new Integer(10); |
面试题2
1 | java复制代码 Integer num3 = 100; |
面试题3
1 | java复制代码 Integer num5 = 1000; |
把上面看完了,在回头来看看这种面试题,还难吗?
如果在面试中遇到面试题3,可以适当反问一下面试官是否有对缓存范围进行调整,或许某些面试官都会懵逼。
赤裸裸的吊打面试官。
总结
1.引用类型的==是比较对应的引用地址。
2.Integer中使用的默认缓存是-128到127。但是可以在JVM启动的时候进行设置。
3.Integer b=Integer.valueOf(8);和Integer b=8;是一样的效果。
4.Integer.valueOf()方式,比较是否在缓存范围之内,在就直接从缓存中获取,不在new一个Integer对象。
5.每次使用new来创建Integer对象,是用不到IntegerCache缓存的。
6.Integer中的使用缓存的方式也可以理解为享元模式。
本文转载自: 掘金