java的封包解包及Integer内部缓存

导语:

之前项目组进行了代码评审,大家讨论的时候有位大神抛出了一个观点,在使用比较时不要使用封装类Integer进行比较,容易出问题。因为描述的不是特别清楚,我在家又仔细思考了下。下面是研究成果,与大家分享。

java的自动装箱

我觉得有必要先解释一下这个概念,我将从此引申此文的后续。

简单解释下封包拆包,其实就是java会自动把基本类型封装成对象。我百度了下这个关键字。第一篇文章是这么写的:

java netty拆包 java封包拆包_java


很简单吧,但是我写了个测试类之后发现了个问题。

java netty拆包 java封包拆包_java netty拆包_02


请看代码第19到第22行,我们都知道,在java中双等于==是比较对象,equals方法是要看调用的对象是否有重写这个方法,要看实际。

在Integer对象中,equals被这么重写。

java netty拆包 java封包拆包_java netty拆包_03


那么问题来了:

System.out.println(c_1 == f_1);// true 有问题,按照我百度的第一篇文章描述c_1和f_1都是new的一个新Integer对象,此处不应该为trueSystem.out.println(g_1 == h_1);// false 没问题,==比较对象,虽然值都为1,但都是new出来的Integer,所以对象不同。但如果这样为什么c和f是true??? 所以事实到底是怎么样的呢,我对class文件进行了反编译,看到了这样的内容:

java netty拆包 java封包拆包_java_04


可以看到,当我编写这样的代码时

Integer c_1 = 1;

java最后实际执行的是:

Integer c_1 = Integer.valueOf(1);

而不是我百度来的:

Integer c_1 = new Integer(1);

在让我们看看valueOf这个方法:

java netty拆包 java封包拆包_java_05

顺着找到了一个叫 IntegerCache的内部类:

java netty拆包 java封包拆包_java_06


结合内部类及Integer的valueOf方法,大家应该明白了点什么了吧。

没错,实际的逻辑是这样的:

内部类的静态代码块初始化了一个长度为high+128长度的Integer数组(可以看到high跟环境有关,默认的话是127),当编写着使用java的自动装箱这一机制的时候,实际调用的是Integer的valueOf方法,方法会判断入参是否在范围内。
如果在范围内,返回其实是一个静态数组里的。也就是说Integer c_1 = 1 与 Integer f_1 = 1 返回的其实是同一个对象,而Integer g_1 = 128;由于超过了范围,是new 的一个新对象。

再回过头来看看百度的第一篇文章,我可以肯定告诉你,这个是不对的。

java netty拆包 java封包拆包_百度_07

我想说的是,百度是个好东西,人家分享知识也是好意。但汲取知识的你更需要抱着怀疑的心态去认真验证每一个说法,别人的写的真的不一定就是对的,包裹我上面写的所有内容。
共勉。

最后最后,其实java中还有很多同样机制的东西。

java netty拆包 java封包拆包_java_08