JDK 源码之基础类 Integer
Contents
本片文章主要介绍 Integer 类中一些比较常用的方法,并分析该类在 Java 中的作用。
总览
在 java.lang 下的 Integer 类是我们平时经常使用的类,首先看一下该类的继承或实现关系,如下图所示:
可以看到,Integer 类继承了 Number 这一抽象类,又实现了 Comparable 接口。
抽象类 Number 是
BigDecimal、BigInteger、Byte、Double、Float、Integer、Long以及Short类的父类。而 Number 类的子类必须提供将所表示的数值转换为
byte、double、float、int、long以及short的方法。
Comparable 接口中只有一个 compareTo(T o) 方法,用于比较元素之间的大小。
属性
Integer 类中的重要属性(域)如下所示:
其中:
MIN_VALUE = 0x80000000表示最小的常数值为 $ -2^{31} $,即 -2147483648;MAX_VALUE = 0x7fffffff表示最大的常数值为 $ 2^{31}-1 $,即 2147483647;SIZE用于表示 int 的二进制补码形式的值的比特位数;SIZES用于表示 int 的二进制补码形式的字节数,值为SIZE / Byte.SIZE = 32 / 8,即等于4;public static final Class<Integer> TYPE表示原始类型int的 Class 实例。
此外,DigitTens 与 DigitOnes如下所示:这俩用于获取从 0 到 99 之间某个数的 个位 和 十位。例如 12,从 DigitOnes 中取出 2,而从 DigitTens 中取出 1。
|
|
digits 用于将数字表示为字符串的所有可能字符,如下所示:
|
|
sizeTable 属性与 stringSize(int x) 方法一起使用。它们用于计算一个 int 型的数字所对应字符串的长度,例如 12 所对应的字符串的长度就是 2,而 123 所对应的字符串的长度就是 3。
一般我们在计算一个数字有几位的时候,通常使用求余或除法进行计算,但这里使用了一个 sizeTable 数组来进行实现,也可以达到求位数的目的。
|
|
内部类 IntegerCache
IntegerCache 作为 Integer 的内部类,从命名方式就可以很清晰的看出,它主要和 缓存 有关。先看源码:
|
|
- 从第
4行可以看到,该缓存是使用Integer类型的cache[]数组实现的,默认范围是[-128, 127]。 - 从第
23到第26行可以看出,默认实例化了 256 个Integer对象。
为什么需要缓存呢?
从文章开头你可以看到,int 的范围很大,将它们全部缓存起来的话代价很高。所以,当有一个新的 Integer 对象处于 [-128, 127] 中的时候,直接从缓存数组 cache[] 中取就好了,不用再重新实例化了,这样可以减小系统的开销。此外,在第 7 行的注释也说了,h 的值也可以通过设置虚拟机参数进行更改,即 Djava.lang.Integer.IntegerCache.high=xxx。
常用方法
构造方法
两个构造方法如下所示:
|
|
至于 parseInt() 方法是什么,我们接着往下看。
parseInt() 方法
这里有两个 parseInt() 方法,这里主要看带有两个参数的方法即可,如下所示:
|
|
parseInt(String s, int radix) 方法与《剑指 Offer》中的题目「把字符串转换成整数」是类似的,都是将字符串转换成整数。
具体的解释已经在代码中标注了,但有一点需要注意的是:在转换过程中使用的是负数进行运算,因为 int 范围的最小值 Integer.MIN_VALUE 在变成正数的时候会导致溢出,所以这里用负数进行计算。
getChars() 方法
该方法主要是将某个 int 类型的数值放到 char 类型的 buf 数组里面,如下所示:
|
|
该方法将 int 类型的数值分为 高位的两个字节 和 低位的两个字节 分别进行处理。
int 占 4 个字节,1 字节 = 8 位,4 字节 = 32 位。
while (i >= 65536)用于处理 int 高位的两个字节,也就是高 16 位。- 对于
for (;;)内部,处理的是低 16 位,这里在计算 乘法 的时候使用的是 加法 和 移位 操作,计算起来更加高效。同时digits数组用于获取对应的字符,这一点在之前已经提到过。
toString() 方法
这里有 3 个重载的 toString() 方法,如下所示:
|
|
对于第二个 toString() 方法,首先用 stringSize() 方法得到数值 i 的位数,然后利用 getChars() 方法获取数值 i 所对应的 char 数组,最后再 new 一个 String 返回。
对于第三个 toString() 方法,多了一个带有 进制 的参数。只要不在 2 到 36 进制之内的都会被处理成 十进制,这里首先将数值转换成 负数 进行处理,十进制转换成其它进制的方法就是不断的除以进制数,从而得到余数,然后将余数反过来,从而得到最后的结果。
valueOf() 方法
这里有三个 valueOf() 方法,如下所示:
|
|
重点看第一个 valueOf() 方法,这里有个 IntegerCache 缓存,对于缓存范围内的 Integer 对象,直接从缓冲中取就可以了,否则就需要重新 new 一个 Integer 对象,即实例化一个 Integer 对象。
这里涉及到了 自动装箱/自动拆箱,对于 -128 <= i <= 127 范围内的数值,返回的是缓存中的对象,并没有重新创建一个新对象,例如 Integer a = 123;,这里就实现了 自动装箱,将 int 类型的 123 转换成对应的包装类 Integer,反编译源码可以看到,执行的就是调用了 ``valueOf()方法,即Integer a = Integer.valueOf(123);`。
而对于范围以外的数,比如 128 来说,则会直接返回一个 Integer 对象。
而 自动拆箱 就是将 包装类型 转换成 基本数据类型,如下所示:
|
|
这里的 int b = a; 通过反编译源码后可以看到,其实是调用了 intValue() 方法,即 int b = a.intValue();。将 Integer 类型转换成 int 类型。
equals() 方法
equals() 方法首先判断对象 obj 是不是 Integer 类型,如果是的话,再将 obj 强转为 Integer,通过 自动拆箱 再比较值是否相等。如下所示:
|
|
compare() 方法
这里直接 return 了一个三目运算,还是嵌套的三目运算。如果 x < y,则返回 -1;如果 x == y,则返回 0;如果 x > y,则返回 1。
|
|
除了以上的方法,其它的例如 reverse(int i)、bitCount(int i)、xxxValue() 等方法,在这里就不详细的展开了,你可以通过查看源码进行阅读。