前言
今天我们来分析一下Integer源码。
Integer是八种包装类里面的比较常用的一种。那在使用时有什么注意及学习的地方呢?
让我们一起来看一下
分析
Integer是包java.lang下的一个类。
1 | public final class Integer extends Number implements Comparable<Integer> { |
其被定义成final类型,继承Number类实现Comparable接口。
1 | public static final int MIN_VALUE = 0x80000000; |
可以看出,其定义了Integer的最大值为2^31-1,最小值为-2^31。Integer的基本数据类型为int。
我们来看一下Integer的toString方法,是比较有趣的。
1 | public static String toString(int i) { |
方法中使用到了stringSize函数,就是求这个Integer数的长度,我们来看看他是如何实现的。
1 | final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, |
可以看到这段代码在计算Integer数长度时,构建了一个一维数组,然后拿x与数组每个值进行比较。而未使用我们经常说的除法或乘法计算长度。我们可以看下源码里的注释。
1 | // I use the "invariant division by multiplication" trick to |
我们知道计算机在计算除法效率要比加减乘法低。所以为了避免除法,提高计算效率,采用此种方法。
正好我们可以看看Long的toString方法里的stringSize方法。
1 | // Requires positive x |
可以看到使用了乘法。你或许会问为什么没有像Integer那样构建一个数组去比较?额,如果要构造数组,那要构造一个19位的数组,里面有1-19位的数,代码写起来很多很臃肿吧,而且构造好的数组会长期放在内存中,我们知道,在实际应用中,Integer的使用频率要比Long高多了,长期让Long里面的一个数组占据内存空间也不太合理。以上是我个人见解。
我们再来看一下parseInt方法。
1 | public static int parseInt(String s, int radix) |
我们可以看到此方法首先进行异常处理,然后判断传入String是否有正负号,然后截取位数,使用乘法,用减法得到int值,然后判断正负并返回结果。
我们再来看下Integer的内部类IntegerCache。
这儿可以说是一个坑,也是比较有意思的地方。
1 | private static class IntegerCache { |
很容易理解这段代码,初始化Integer后,IntegerCache会缓存[-128,127]之间的数据,这个区间的上限可以配置,取决于java.lang.Integer.IntegerCache.high这个属性,这个属性在VM参数里为-XX:AutoBoxCacheMax=2000进行设置调整或者VM里设置-Djava.lang.Integer.IntegerCache.high=2000。所以Integer在初始化完成后会缓存[-128,max]之间的数据。
并且我们可以看到valueOf方法。
1 | public static Integer valueOf(int i) { |
可以看到valueOf方法,在cache范围内,返回的是缓存的值,是相同的对象,不在cache范围内,才会新建Integer。
由于有了Integer缓存,我们可以测试以下代码。
1 | Integer a=1; |
可以看到结果为true,false,false。
这样,我们在比较Integer时,如果仅仅比较值相等,建议使用equals方法比较。
我们可以看下Integer的equals方法。
1 | public boolean equals(Object obj) { |
可以看到它会先判断类型是否符合,然后进行拆箱比较操作。
同样,在Long,Byte,Short,我们也可以看到缓存,其缓存数据长度均是-128到127。
1 | //Long |
关于缓存的意义:在该范围内数据比较常用,添加缓存提高性能。不用每次都新建,浪费系统资源。
同时根据Integer的hashCode方法,我们可以看到,Integer的hashCode返回本身的int值。
1 |
|
结论
以上就是Integer的源码分析,可以看到,对于偏底层的一些调用频繁的类,Java都做了很多方面的优化。包括从性能及内存开销等诸多方面。是值得我们学习和理解的。