专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java源码分析 Integer

公有常量介绍

    /**
     *表示int的最小值 - 2^31
     */
    @Native public static final int   MIN_VALUE = 0x80000000;

    /**
     *表示int的最大值 2^31 - 1
     */
    @Native public static final int   MAX_VALUE = 0x7fffffff;

    /**
     * 表示基本类型int的实例
     */
    @SuppressWarnings("unchecked")
    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

常用方法的源码分析

toString

public static String toString(int i, int radix)//radix代表进制,用来把i转化为指定进制字符串

public static String toString(int i)//把i转化为十进制字符串

toString(int i, int radix)的源码如下


/** * 所有可能用来把数字表示为字符串的字符 */ final static char[] digits = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; public static String toString(int i, int radix) { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)//Character.MIN_RADIX,Character.MAX_RADIX分别为2和36 radix = 10; /* 如果转化为十进制,则用toString,这个方法更快*/ if (radix == 10) { return toString(i); } char buf[] = new char[33];//由于表示Integer的最小值的二进制需要33位 boolean negative = (i < 0);//判断i是否为负数 int charPos = 32;//记录位置 if (!negative) { i = -i; } /*这段代码通过基数取余,来获取每一位的值,如果看不懂,直接带入十进制来看*/ /*首先:设 i = 123 radix = 10*/ /*第一步:i = -123(上面i=-i) < -10成立,循环开始*/ /*第二步:buf[32] = digits[3] = 3,charPos = 31 digits在上面常量里有*/ /*第三步:i = 123/10 = 12*/ /*重复这一过程*/ while (i <= -radix) { buf[charPos--] = digits[-(i % radix)]; i = i / radix; } //见过首位(例如上面123的1)放入数组 buf[charPos] = digits[-i]; if (negative) {//判断是否需要加上 - buf[--charPos] = '-'; } //返回结果 return new String(buf, charPos, (33 - charPos)); }

toString(int i)的源码如下

 public static String toString(int i) {
        if (i == Integer.MIN_VALUE)//如果等于最小值的话,直接返回
            return "-2147483648";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);//获取i的长度,看下面
        char[] buf = new char[size];
        getChars(i, size, buf);//将数字i的各位存入数组中
        return new String(buf, true);
}

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',
        } ;

    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',
        } ;

static void getChars(int i, int index, char[] buf) {
        int q, r;
        int charPos = index;
        char sign = 0;

        if (i < 0) {
            sign = '-';
            i = -i;
        }

        // 每次循环生成两位数
        while (i >= 65536) {
            q = i / 100;
            r = i - ((q << 6) + (q << 5) + (q << 2));//表示 r = i - (q * 100);
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

        // 当 i < 65536时执行这个
        for (;;) {
            q = (i * 52429) >>> (16+3);//结果近似于 q = 0.1*i
            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;
        }
    }

    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++)//根据与sizeTable中数的比较,得出x的长度
            if (x <= sizeTable[i])
                return i+1;
    }

parseInt

parseInt的源码如下

public static int parseInt(String s, int radix)//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') {
                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) {
                //获取字符,并转为int
                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;
    }

    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }

valueOf

valueOf的源码如下

    public static Integer valueOf(String s, int radix) throws NumberFormatException {//调用valueOf(int i)方法
        return Integer.valueOf(parseInt(s,radix));
    }

    public static Integer valueOf(String s) throws NumberFormatException {//调用valueOf(String s, int radix),且radix默认为10
        return Integer.valueOf(parseInt(s, 10));
    }

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)//IntegerCache.low = -128 IntegerCache.high = 127,如果i在这两个数之间,就调用IntegerCache进行缓存
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);//否则之间创建一个Integer
    }

构造器

    private final int value;

    public Integer(int value) {
        this.value = value;
    }

    public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
    }

从上面源码可以看出给Integer赋的值是不可以改变的,由于private final int value;是常量。但是在编程中 我们可以通过Integer i = 1; i = 3;直接赋值,这是怎么回事

我们将上面的代码进行反编译,反编译之后的代码如下:

        Integer i = new Integer(1);
        i = Integer.valueOf(3);

IntegerCache

转载这里

本文将介绍 Java 中 Integer 缓存的相关知识。这是 Java 5 中引入的一个有助于节省内存、提高性能的特性。 首先看一个使用 Integer 的示例代码,展示了 Integer 的缓存行为。接着我们将学习这种实现的原因和目的。 你可以先猜猜下面 Java 程序的输出结果。很明显,这里有一些小陷阱,这也是我们写这篇文章的原因。

    /**

     * 测试Integer的缓存 IntegerCache.cache

     */

    private static void testIntegerCache() {

        System.out.println("---int---");

        int a = 127, b = 127;

        System.out.println(a == b);         //true

        a = 128;

        b = 128;

        System.out.println(a == b);         //true

        System.out.println("---Integer---");

        Integer aa = 127, bb = 127;

        System.out.println(aa == bb);       //true

        aa = 128;

        bb = 128;

        System.out.println(aa == bb);       //false

        System.out.println(aa.equals(bb));  //true

    }

执行结果就如同上面的那样,我就不贴图来展示啦,我把原文的例子给换成自己的了。

在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。 上面的规则适用于整数区间 -128+127。 这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。 Java 编译器把原始类型自动转换为封装类的过程称为自动装箱(autoboxing),这相当于调用 valueOf 方法

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

现在我们知道了 JDK 源码中对应实现的部分在哪里了。我们来看看 valueOf 的源码。下面是 JDK 1.8.0 build 25 中的代码。

    /**

     * Returns an {@code Integer} instance representing the specified

     * {@code int} value.  If a new {@code Integer} instance is not

     * required, this method should generally be used in preference to

     * the constructor {@link #Integer(int)}, as this method is likely

     * to yield significantly better space and time performance by

     * caching frequently requested values.

     *

     * This method will always cache values in the range -128 to 127,

     * inclusive, and may cache other values outside of this range.

     *

     * @param  i an {@code int} value.

     * @return an {@code Integer} instance representing {@code i}.

     * @since  1.5

     */

    public static Integer valueOf(int i) {

        if (i >= IntegerCache.low && i <= IntegerCache.high)

            return IntegerCache.cache[i + (-IntegerCache.low)];

        return new Integer(i);

    }

在创建新的 Integer 对象之前会先在 IntegerCache.cache (是个Integer类型的数组)中查找。有一个专门的 Java 类来负责 Integer 的缓存。

IntegerCache 类

IntegerCache 是 Integer 类中一个私有的静态类。我们来看看这个类,有比较详细的文档,可以提供我们很多信息。

    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    private static class IntegerCache {

        static final int low = -128;

        static final int high;

        static final Integer cache[];

        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) {

                try {

                    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);

                } catch( NumberFormatException nfe) {

                    // If the property cannot be parsed into an int, ignore it.

                }

            }

            high = h;

            cache = new Integer[(high - low) + 1];

            int j = low;

            for(int k = 0; k < cache.length; k++)

                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)

            assert IntegerCache.high >= 127;

        }

        private IntegerCache() {}

    }

Javadoc 详细的说明这个类是用来实现缓存支持,并支持 -128 到 127 之间的自动装箱过程。最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改。 缓存通过一个 for 循环实现。从小到大的创建尽可能多的整数并存储在一个名为 cache 的整数数组中。这个缓存会在 Integer 类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

实际上在 Java 5 中引入这个特性的时候,范围是固定的 -128 至 +127。后来在 Java 6 中,最大值映射到 java.lang.Integer.IntegerCache.high,可以使用 JVM 的启动参数设置最大值。这使我们可以根据应用程序的实际情况灵活地调整来提高性能。是什么原因选择这个 -128 到 127 这个范围呢?因为这个范围的整数值是使用最广泛的。 在程序中第一次使用 Integer 的时候也需要一定的额外时间来初始化这个缓存。

Java 语言规范中的缓存行为

在 Boxing Conversion 部分的Java语言规范(JLS)规定如下: 如果一个变量 p 的值属于:-128至127之间的整数(§3.10.1这个估计是版本号吧),true 和 false的布尔值 (§3.10.3),’u0000′ 至 ‘u007f’ 之间的字符(§3.10.4)中时,将 p 包装成 a 和 b 两个对象时,可以直接使用 a == b 判断 a 和 b 的值是否相等。

其他缓存的对象 这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。 有 ByteCache 用于缓存 Byte 对象 有 ShortCache 用于缓存 Short 对象 有 LongCache 用于缓存 Long 对象 有 CharacterCache 用于缓存 Character 对象 Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

文章永久链接:https://tech.souyunku.com/28198

未经允许不得转载:搜云库技术团队 » Java源码分析 Integer

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们