huasheng

咱们对这个Integer封装类并不陌生,就连初学者都了解  ,下面咱们就解剖一下这个神奇的老哥

JDK源码 (在java.lang包下面)

  1. 首先这老哥继承一个Number 类 ,这个类的总体作用就是对数字的处理, 这个实现了Serializable(序列化接口),内置抽象方法(intValue,longValue,floatValue,doubleValue)以及byteValue,shortValue方法。
  2. 其次实现比较接口 Comparable ,其泛型是老哥,此接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法,相当于java的Arrays.sort方法,同时
    不需要指定一个comparator  有兴趣可以看一下此接口和comparator  的区别,老哥就有了自己和自己本身类型比较能力。
  3. 老哥是个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()); // true 

  isPrimitive方法 确定指定对象表示一个基本类型 ,有九个预定对象代表八个基本数据类型和void。 这些是由Java虚拟机创建,并且具有相同的名称为他们所代表的基本类型,即booleanbytecharshortintlongfloatdouble

       并且是该方法返回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));
    }
View Code
  •   此方法的第一个int i 是大兄弟要转换的参数 。 第二个参数是该参数是以几进制来显示  该方法返回值为String老朋友。

实现分析

  1. 首先判断了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);
}
View Code

首先判断是否等于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;
}
View Code
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;
        }
    }
View Code
//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',
    } ;
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)

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;
    }
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/52623703

 

相关文章: