美文网首页
Integer源码分析

Integer源码分析

作者: EricDD | 来源:发表于2020-02-16 22:13 被阅读0次

    Integer

    1. Integer是int的包装类而int是一种基本数据类型。
    2. Integer是面向对象的,所以必须实例化。
    3. Integer是对象的引用。int则是存储数据。

    类图

    1.png
    public final class Integer extends Number implements Comparable<Integer> {
        // 
    }
    
    1. final 修饰 Integer类没有子类,类中方法默认都是final(final方法不能被子类覆盖)
    2. Integer 继承了 Number类则可使用Number类中方法
    3. Integer 实现了Comparable 接口,所以实现了compareTo方法。

    属性

    private属性

    //真正存储int值
    private final int value;
    //序列化相关,Integer实现了 Serializable
    @Native private static final long serialVersionUID = 1360826667806852920L;
    

    public属性

    //int 最小值
    @Native public static final int   MIN_VALUE = 0x80000000;
    //int 最大值
    @Native public static final int   MAX_VALUE = 0x7fffffff;
    //int
    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
    //int bit 位数
    @Native public static final int SIZE = 32;
    //int byte 位数
    public static final int BYTES = SIZE / Byte.SIZE;
    

    构造方法

    public Integer(int value) {
        this.value = value;
    }
    //根据string参数构造新Integer对象
    public Integer(String s) throws NumberFormatException {
        // 10 string转int默认为10进制
        this.value = parseInt(s, 10);
    }
    

    常见方法

    parseInt()

    返回一个int基本数据类型

    //返回一个十进制的int
    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }
    //返回一个指定进制的int
    //该方法会抛出NumberFormatException 
    // 1. 字符串参数为null
    // 2. radix 小于 Character.MIN_RADIX 或者大于 Character.MAX_RADIX
    // 3. 字符串不是int类型数据
    // 4. 指定进制无法表达字符串
    public static int parseInt(String s, int radix) throws NumberFormatException{
    }
    

    valueOf()

    返回一个Integer对象

    // 返回一个十进制Integer
    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }
    // 返回一个指定进制Integer
    public static Integer valueOf(String s, int radix) throws NumberFormatException {
        // parseInt()
        return Integer.valueOf(parseInt(s,radix));
    }
    // 内部类 Integer 缓存
    public static Integer valueOf(int i) {
        // 如果 i 在 IntegerCache 之间,则返回缓存中取出。
        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 {
            // 默认最大缓存值
            int h = 127;
            // 读取设置的最大缓存值
            String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    // 设置最大值小于127 则最大值为默认值 127
                    // 设置最大值为Integer.MAX_VALUE 则最大值为 Integer.MAX_VALUE - 128 - 1
                    i = Math.max(i, 127);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // 传入的数值不能解析为int,忽略。最大值为默认值 127
                }
            }
            high = h;
            // 初始化缓存数组,长度为 127 + 128 + 1
            cache = new Integer[(high - low) + 1];
            //循环初始化 Integer 对象,并放入缓存数组
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
    
            // assert 断言关键字
            assert IntegerCache.high >= 127;
        }
        // 空构造
        private IntegerCache() {}
    }
    

    valueOf(String s)valueOf(String s, int radix)这两个将字符串转换为Integer对象的方法实现很简单。

    主要是涉及到缓存内部类的`方法。

    Integer a = 10; //this is autoboxing
    Integer b = Integer.valueOf(10); //under the hood
    

    当程序使用第一次使用Integer.valueOf()是则会缓存 -127 到 128 到IntegerCache.cache数组中(缓存的是Integer对象)。

    注意:构造函数不会使用缓存。

    Integer a = 10;自动装箱编译后Integer b = Integer.valueOf(10);

    所以当初始化Integer对象时优先使用Integer a = 10;

    设置最大缓存值方式

    -Djava.lang.Integer.IntegerCache.high=1000
    -XX:AutoBoxCacheMax=1000

    例子
    public static void main(String[] args) {
        Integer integer1 = 100; // 无缓存,Integer.valueOf(100); new Integer() 放入缓存。
        Integer integer2 = 100; // 缓存中读取。同一个Integer对象
        Integer integer3 = new Integer(100); // 构造函数不会使用缓存
    
        if(integer1 == integer2){
            System.out.println("integer1 == integer2");
        }else{
            System.out.println("integer1 != integer2");
        }
    
        if(integer1 == integer3){
            System.out.println("integer1 == integer3");
        }else{
            System.out.println("构造函数不会使用缓存");
            System.out.println("integer1 != integer3");
        }
    
        if(integer2 == integer3){
            System.out.println("integer2 == integer3");
        }else {
            System.out.println("构造函数不会使用缓存");
            System.out.println("integer2 != integer3");
        }
    
        Integer integer5 = 300; // 缓存默认缓存 -127 到 128
        Integer integer6 = 300; // 超出缓存
    
        if(integer5 == integer6){
            System.out.println("integer5 == integer6");
        }else{
            System.out.println("integer5 != integer6");
        }
    }
    
    IntegerCache1.png
    IntegerCache2.png

    decode()

    //接受十进制、十六进制、八进制字符串,返回Integer对象
    //通过截取字符串获取进制。然后调用valueOf()
    public static Integer decode(String nm) throws NumberFormatException {}
    

    最终调用valueOf()方法,故而使用缓存。

    getInteger()

    // 根据key读取系统属性值。并转为Integer对象
    public static Integer getInteger(String nm) {
        return getInteger(nm, null);
    }
    // 如果系统属值不存在则返回默认值 val
    public static Integer getInteger(String nm, int val) {
        Integer result = getInteger(nm, null);
        return (result == null) ? Integer.valueOf(val) : result;
    }
    public static Integer getInteger(String nm, Integer val) {
        String v = null;
        try {
            //读取系统属性
            v = System.getProperty(nm);
        } catch (IllegalArgumentException | NullPointerException e) {
        }
        //系统属性不存在则返回 val
        if (v != null) {
            try {
                return Integer.decode(v);
            } catch (NumberFormatException e) {
            }
        }
        return val;
    }
    

    最终调用valueOf(),故而使用缓存。

    toString()

    public static String toString(int i) !
        //i为最小值直接返回
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        // 返回整数 i 位数
        // ① 如果 i 为负数 则转为正整数。前方代码 i 为最小值是转为正整数会溢出。
        // ② 假设 i 等于 10 则 9 < x < 99 。i 循环到 1 ,返回 i + 1 ,10 为2位数。
        // ③ 假设 i 等于 -10 ,+1 是标示负号。-10 为3位数 
        // 设计真精妙啊
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 
        // 创建一个位数长度的char数组
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }
    
    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                          99999999, 999999999, Integer.MAX_VALUE };
    
    // ② 假设 i 等于 10 则 9 < x < 99 。i 循环到 1 ,返回 i + 1 ,x 位数 2 。
    static int stringSize(int x) {
        // i ++ 进行无限循环
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }
    // i 参数,index i 位数,buf index 长度的char数组
    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
        // 当 i 大于等于 65536 时,每循环一次将 i 最后两位存到 buf 数组中
        while (i >= 65536) {
            // 除法 假如 i 为 65536 q = 655
            q = i / 100;
            // 相当于  r = i - (q * 100);
            // r = 36
            r = i - ((q << 6) + (q << 5) + (q << 2));
            //将i设置为取出 最后两位的数字
            i = q;
            //从数组中获取两位char数值存入buf
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }
    
        // Fall thru to fast mode for smaller numbers
        // 当 i 小于 65536 时 循环剩余位 存到 buf 数组 中
        // 以上代码将 65536 最后两位存入 buf 后,i = 655
        for (;;) {
            // 相当于 q = i / 10。此时 q = 65 
            q = (i * 52429) >>> (16+3);
            // 相当于 r = i-(q*10) 。此时 r = 5
            r = i - ((q << 3) + (q << 1)); 
            // 将 i 最后一位存入 buf
            buf [--charPos] = digits [r];
            // 设置 i 为剩余 数字,然后下次循序
            i = q;
            // i == 0 循环结束,将所有数字拆分存入 buf 中
            if (i == 0) break;
        }
        if (sign != 0) {
            //存入负号
            buf [--charPos] = sign;
        }
        // 36 位是 3 
        // 36 / 10 结果是 3
        final static char [] DigitTens = {
            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',// 0 - 9
            '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',// 10 - 19
            '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',// 20 - 29
            '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',// 30 - 39
            '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',
            } ;
        // 36 位 取出是 6。
        // 36 % 10 余数 6
        final static char [] DigitOnes = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',// 0 - 9
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',// 10 - 19
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',// 20 - 29
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',// 30 - 39
            '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',
            } ;
    }
    

    代码上方基本上每一步的注释都有写,debug几次就好了。

    int i = 65536;
    int q = i / 100;
    int r = i - ((q << 6) + (q << 5) + (q << 2));
    System.out.println(i);
    System.out.println(r);
    System.out.println("r = "+ i % 100);
    

    上方代码是从getChars()i >= 65536后的一段代码,发现使用了众多的位运算得到的结果其实和除法求余是一样的。

    使用位运算的原因就是要比直接乘除==效率要高==

    其他基本类型

    Byte

    基本和Integer大同小异。

    1. 同样是final修饰
    2. 继承了Number实现了Comparable

    parseByte()

     public static byte parseByte(String s, int radix) throws NumberFormatException {
         //将字符串转为 int 
         int i = Integer.parseInt(s, radix);
         // 如果超出 byte 范围 抛出异常
         if (i < MIN_VALUE || i > MAX_VALUE)
             throw new NumberFormatException(
             "Value out of range. Value:\"" + s + "\" Radix:" + radix);
         // 向下转换
         return (byte)i;
     }
    

    valueOf()

    public static Byte valueOf(byte b) {
        final int offset = 128;
        //同样是从内部缓存类中读取
        return ByteCache.cache[(int)b + offset];
    }
     private static class ByteCache {
         private ByteCache(){}
         
         static final Byte cache[] = new Byte[-(-128) + 127 + 1];
         // 默认缓存 -128 到 127 数字
         static {
             for(int i = 0; i < cache.length; i++)
                 cache[i] = new Byte((byte)(i - 128));
         }
     }
    

    Short

    参考Byte和Integer

    Long

    参考Byte和Integer

    相关文章

      网友评论

          本文标题:Integer源码分析

          本文链接:https://www.haomeiwen.com/subject/loxvfhtx.html