咱们对这个Integer封装类并不陌生,就连初学者都了解 ,下面咱们就解剖一下这个神奇的老哥
JDK源码 (在java.lang包下面)
首先这老哥继承一个Number 类 ,这个类的总体作用就是对数字的处理, 这个实现了Serializable(序列化接口),内置抽象方法(intValue,longValue,floatValue,doubleValue)以及byteValue,shortValue方法。
其次实现比较接口 Comparable ,其泛型是老哥,此接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法,相当于java的Arrays.sort方法,同时
不需要指定一个comparator 有兴趣可以看一下此接口和comparator 的区别,老哥就有了自己和自己本身类型比较能力。
老哥是个final类 不能被继承,所以你见过老哥的儿子和孙子吗?
肯定会有些大兄弟看到这玩意会懵逼的 其实这两个玩意就是2个补码,不要那么紧张
这里的话 我就不解释了 给大兄弟们一个链接(因为我懒的看 )https://blog.csdn.net/u011531425/article/details/80712160
最后得出来 的结论 MIN_VALUE = -231-1 MAX_VALUE = 231-1 这是老哥的范围
Class.getPrimitiveClass("int") 为指定的对象返回虚拟机的类对象 实现 :static native Class getPrimitiveClass(String name); 这个类的话 是通过其他语言(好像是C写的)实现的 (native ),咱们不用管这哥们。
这个地方涉及到老哥.class 和 老哥.TYPE 以及 int.class的区别
我这里稍微的解释一下
老哥.class 这个很好理解就是返回老哥的类型类,
老哥.TYPE 这个就是虚拟机的类对象, 其实也就是int
int.class 返回的是int
做个测试:通过反射机制,看老哥.TYPE的类型类是不是原始类型
Class c1 = Integer.TYPE;
Class c2 = int.class;
System.out.println(c1 == c2); // true
System.out.println(c1.isPrimitive()); // true
System.out.println(c2.isPrimitive()); // true
isPrimitive方法 确定指定类对象表示一个基本类型 ,有九个预定类对象代表八个基本数据类型和void。 这些是由Java虚拟机创建,并且具有相同的名称为他们所代表的基本类型,即boolean , byte , char , short , int , long , float和double
并且是该方法返回true的唯一类对象
结论:
c1和c2的虚拟机类对象是一样的,Integer在虚拟机中还是拆箱为int。
老哥.toString(int i, int radix)
View Code
此方法的第一个int i 是大兄弟要转换的参数 。 第二个参数是该参数是以几进制来显示 该方法返回值为String老朋友。
实现分析
首先判断了radix
复制代码
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;
复制代码
Character.MIN_RADIX == (public static final int MIN_RADIX = 2;) // Character是字符的类
Character.MAX_RADIX == (public static final int MAX_RADIX = 36;)
这里解释一下为什么最小值是2 ? 如果不是2的话,输入汉字的话 ,这家伙就直接塌盘了。
如果radix < 2 或者 radix > 36 那么将会默认以10进制的方式显示。
1
2
3
if (radix == 10) {
return toString(i);
}
如果正好等于10的话 那么直接调用toString(i)方法。
View Code
首先判断是否等于Integer.MIN_VALUE 如果等于的return -2147483648 说明老哥的最小值就是这个
其次判断是否小于0
1
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
View Code
1
stringSize方法主要判断大兄弟当前传的值 是几位数字 你可以试一下 如果当i等于2的时候 最后size为 当大于等于10的时候 size为2
然后就是看比较关键的部分了 getChars(i, size, buf); i为大兄弟你们传的值 size是值的位数 buf是之后的存储的地方
View Code
View Code
复制代码
while (i >= 65536) {
q = i / 100;
//
r = i - ((q << 6) + (q << 5) + (q << 2)); //等价于r = i - (q * 100);
i = q;
// 取DigitOnes[r]的目的其实取数字r%10的结果 buf [--charPos] = DigitOnes[r];
// 取DigitTens[r]的目的其实是取数字r/10的结果
buf [--charPos] = DigitTens[r];
}
复制代码
疑问:为什么要和65536比较呢 ?
结论 :移位要比乘法的效率高 程序要尽量用移位代替乘法。
复制代码
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
复制代码
这里将代码计算一下
q = (i * 52429) >>> (16+3);
==> q = (i * 52429) / 524288;
==> q = i * 0.1
==> q = q/10
结论:这里可以看出来 移位要比 乘法除法 效率高 乘法比除法的效率高 这些都是以后在高效代码中需要注意的。
注意:这里的乘以 是 0.1
刚才那个疑问 :为什么要和65536比较呢 ?
这个主要因为基本数据类型int有关,最大不能超过(2^31-1)。如果使用一个太大的数字进行乘法加移位运算很容易导致溢出 ,那么为什么是65536这个数字呢?
我们还用 q = (i * 52429) >>> (16+3)来个解析
使用num1,num2,num3三个变量代替源代码中的数字,便于后面分析使用num1=65536, num2=52429 ,num3=19
既然我们要使用q = (i * num2) >>> (num3);的形式使用乘法和移位代替除法,那么n和m就要有这样的关系:
num2= (2^num3 /10 )
只有这样才能保证(i * num2) >>> (num3)结果接近于0.1。
2^13=8192, 820/8192=0.10009765625
2^14=16384, 1639/16384=0.10003662109375
2^15=32768, 3277/32768=0.100006103515625
2^16=65536, 6554/65536=0.100006103515625
2^17=131072, 13108/131072=0.100006103515625
2^18=262144, 26215/262144=0.10000228881835938
2^19=524288, 52429/524288=0.10000038146972656
2^20=1048576, 104858/1048576=0.1000003815
2^21=2097152, 209716/2097152 = 0.1000003815
2^22= 4194304, 419431/4194304= 0.1000001431
因为如果num3越大,就会要求i比较小,因为必须保证(i * num2) >>> (num3)的过程不会因为溢出而导致数据不准确。那么是怎么敲定num1=65536,num2= 524288, num3=19的呢? 这三个数字之间是有这样一个操作的:
(num1*num2) >>> num3
因为要保证该操作不能因为溢出导致数据不准确,所以num1和num2就相互约束。两个数的乘积是有一定范围的,不成超过这个范围,所以,num1增大,num2就要随之减小。相对以上次幂的计算 19的时候 最接近于0.1 所以就选择了这3个数字。
接下来的代码 就i不是那么难分析了 只要使用代入法就可以了 最后会将数据存储到buf数组里面。
老哥.parseInt(String s, int radix)
View Code
此方法 监听数字转化异常
throws NumberFormatException
第一个参数 是大兄弟们要转化的字符串 那如果说 字符串中没有数字的话 是不是就要报异常了 这个很好理解
第二个参数 应该有点熟悉把 这个就是咱们的base 字符串需要以几进制的形式显示。
实现分析
复制代码
// 首先判断字符串是否为null
if (s == null) {
throw new NumberFormatException("null");
}
// 判断是几进制形式显示 范围为 2 - 36 不然的话 ,会报异常
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
复制代码
复制代码
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
// 判断s字符串的长度
if (len > 0) {
// 取出字符串的第一个字符
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
复制代码
那咱们来举个例子(走debug)
public static void main(String[] args) {
Integer a = 101;
System.out.println(a.parseInt("600", 10));
}
我就那郁闷 600的第一个字符应该是6的啊 怎么会是54呢 然后我想了一下 我恍然大悟
之后的话 就很好理解了 ,最后返回int类型的。
老哥.valueOf(String s, int radix)
此方法 监听数字转化异常
throws NumberFormatException
第一个参数 是大兄弟们要转化的字符串 那如果说 字符串中没有数字的话 是不是就要报异常了 这个很好理解
第二个参数 应该有点熟悉把 这个就是咱们的base 字符串需要以几进制的形式显示。
实现分析
复制代码
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
复制代码
首先将字符串通过parseInt转为了int类型的
public static Integer valueOf(int i) {
// assert 关键字断言 断言 assert关键字需要在运行时候显式开启才能生效,否则断言就没有任何意义 同时 如果断言失败的 那么整个系统都会崩溃 不懂的话 可以通过链接去看看
https://blog.csdn.net/yangjiachang1203/article/details/52351880
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// 老哥自带的缓存
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
// static 静态代码块可知缓存的初始化是在第一次使用的时候。 通过 VM 参数 -XX:AutoBoxCacheMax=可以配置缓存的最大值。 在 VM 初始化期间, 缓存最大值 high, 可能被保存在 sun.misc.VM class 的私有系统属性里。
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) {
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);
}
// 初始化的时候 最大的值为high 127
high = h;
// 这个时候 缓存帮我们new一个对象 范围大小 -128 ~ 127 直接说结论把 : 当数据为 -128 ~ 127 的时候 提供了缓存服务 可以直接在缓存中拿数据
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
关于有些老哥和int的区别可以看一下这个 (个人推荐)
https://blog.csdn.net/wangyang1354/article/details/52623703https://www.cnblogs.com/huasheng/p/9995543.html