咱们对这个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<Integer> c1 = Integer.TYPE;
Class<Integer> c2 = int.class;
System.out.println(c1 == c2); // true
System.out.println(c1.isPrimitive()); // true
System.out.println(c2.isPrimitive()); // trueisPrimitive方法 确定指定
类
对象表示一个基本类型 ,有九个预定类
对象代表八个基本数据类型和void。 这些是由Java虚拟机创建,并且具有相同的名称为他们所代表的基本类型,即boolean
,byte
,char
,short
,int
,long
,float
和double
并且是该方法返回
true
的唯一类
对象结论:
c1和c2的虚拟机类对象是一样的,Integer在虚拟机中还是拆箱为int。
老哥.toString(int i, int radix)
public static String toString(int i, int radix) { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) radix = 10; /* Use the faster version */ if (radix == 10) { return toString(i); } char buf[] = new char[33]; boolean negative = (i < 0); int charPos = 32; if (!negative) { i = -i; } while (i <= -radix) { buf[charPos--] = digits[-(i % radix)]; i = i / radix; } buf[charPos] = digits[-i]; if (negative) { buf[--charPos] = '-'; } return new String(buf, charPos, (33 - charPos)); }
- 此方法的第一个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进制的方式显示。
if (radix == 10) { return toString(i); }
如果正好等于10的话 那么直接调用toString(i)方法。
public static String toString(int i) { if (i == Integer.MIN_VALUE) return "-2147483648"; int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); char[] buf = new char[size]; getChars(i, size, buf); return new String(buf, true); }
首先判断是否等于Integer.MIN_VALUE 如果等于的return -2147483648 说明老哥的最小值就是这个
其次判断是否小于0
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE }; static int stringSize(int x) { for (int i=0; ; i++) if (x <= sizeTable[i]) return i+1; }
stringSize方法主要判断大兄弟当前传的值 是几位数字 你可以试一下 如果当i等于2的时候 最后size为 当大于等于10的时候 size为2
然后就是看比较关键的部分了 getChars(i, size, buf); i为大兄弟你们传的值 size是值的位数 buf是之后的存储的地方
static void getChars(int i, int index, char[] buf) { int q, r; int charPos = index; char sign = 0; if (i < 0) { sign = '-'; i = -i; } // Generate two digits per iteration while (i >= 65536) { q = i / 100; // really: r = i - (q * 100); r = i - ((q << 6) + (q << 5) + (q << 2)); i = q; buf [--charPos] = DigitOnes[r]; buf [--charPos] = DigitTens[r]; } // Fall thru to fast mode for smaller numbers // assert(i <= 65536, i); 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; } if (sign != 0) { buf [--charPos] = sign; } }
//100以内的数字除以10的结果(取整) final static char [] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', } ; //100以内的数字对10取模的结果 final static char [] DigitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', } ;
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)
public static int parseInt(String s, int radix) throws NumberFormatException { /* * WARNING: This method may be invoked early during VM initialization * before IntegerCache is initialized. Care must be taken to not use * the valueOf method. */ if (s == null) { throw new NumberFormatException("null"); } 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; 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); } return negative ? result : -result; }
此方法 监听数字转化异常
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/52623703