一起学JDK源码 -- Integer类

Integer类为java基本类型int的包装类,除了前面提到的Byte类,Short类中的大部分方法,Integer类中还提供了很多处理int类型的方法,接下来就让我们一起看看吧。

基础知识:

1.Java移位运算,移位运算也是java中位运算的一部分主要有以下三种:
前提:
1).移位是在将对应的数字转换为2进制后进行的,JAVA中用补码表示二进制数.
2).移位后总长度不变,比如int类型为32位,移动后还需补齐为32位,其它也类似
3).左边为低位,右边为高位
左移( << ):将运算符左边的对象,向左移动运算符右边指定的位数,并且在低位补零。向左移n 位,就相当于乘上2 的n 次方
右移( >> ):将运算符左边的运算对象,向右移动运算符右边指定的位数。如果是正数,在高位补零,如果是负数,则在高位补1
无符号右移( >>> ):将运算符左边的对象向右移动运算符右边指定的位数,并且在高位补0
注:
1).对于正数来说,右移或无称号右移n 位,就相当于除上2 的n 次方。
2).对于int类型(长度为32位)移位,左移(<<)n位,相当于移动 n % 32 位,如: 1 << 33 实际上是 1 << 1 也就是2,其它类型移动超过自身类型长度的也应该是类似,有兴趣的可以自行研究下。
3).java中没有无符号左移,原因很简单,左移补的是低位,java中高位的第一位表示符号位。

前言:

从本文开始,对于比较简单的属性和方法,或是在之前文章中讲过的文章中将不再列出,有兴趣的同学可以看下之前的文章,或是留言笔者将根据大家的需求考虑是否后续在文章中添加。

几个数组属性:

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'
};
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',
    } ;
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };

digits数组用于表示所有可能出现的字符,因为int支持从2进制到36进制,所以这里需要有36个字符才能表示所有不同进制的数字
DigitTens和DigitOnes两个数组也很好理解,它们主要用于获取0到99之间某个数的十位和个位,比如36,通过DigitTens数组直接取出来十位为3,而通过DigitOnes数组取出来个位为6。
sizeTable数组主要用在判断一个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;
        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类型的数字,转换为指定进制的数的字符串形式。
第一步判断:int类型支持的进制数为2(Character.MIN_RADIX)到36(Character.MAX_RADIX),如果不是这个范围就按10进制来处理。
第二步:如果是10进制,调用toString(i)(这个方法我们后面再讲),如果不是10进制,继续往下走。
第三步:接着它创建了一个长度为33的char类型的数组,诶,为什么这里长度为33,int类型的最大长度为32啊,看到后面你就知道了,还要给 '-'留一个位置,原来这个方法的转换并不是真正意义上的转换,都是按照正数来转换,如果是负数就在正数转换的结果上加'-'。
第四步:创建了两个局部变量negative和charPos其中negative是个标识,用来标识目标数是正数还是负数。charPos是用来指定转换后的数存储在缓冲数组中的位置。
第五步:转换,这里就是按照10进制的数转换为其它进制数的转换方法进行的。如果不清楚的同学可以看下另一篇文章Java中的进制转换
第六步:创建一个字符串对象返回。

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);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
}

这个方法用来将10进制的数转换为10进制的字符串形式。
第一步:判断如果是int类型的最小值,就直接返回一个固定的值,无需计算。
第二步:计算数字的长度,用到的stringSize方法,如下:

static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
}

可以看到它使用了sizeTable这个数组,巧妙的避免了除法和求余,以高效的求得对应字符串长度。方法实现很简单,但是思想我们可以借鉴。
第三步:创建了一个数组,然后调用getChars方法填充数组中的内容。getChars方法实现如下:

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

这个方法的作用就是将一个int类型的数按顺序放到char数组中。不过这里使用了很多的技巧。它把int的高位的两个字节和低位的两个字节分开处理,while (i >= 65536)就是处理高位的两个字节`。每次处理两位数,使用了一个技巧((q << 6) + (q << 5) + (q << 2)),就是 q * (2^6 +2^5 + 2^2) = q * 100。DigitTens和DigitOnes用来取十位和个位。后面就是对低位的两个数进行处理了,其本质就是求余,也用了一些技巧,(i * 52429) >>> (16+3)其实约等于i/10,((q << 3) + (q << 1))其实等于q*10,最后通过digits数组获取到对应的字符。这里面使用乘法和移位运算代替除法和取余,用移位运算和加法代替乘法,可见在运算效率上加法和移位高于乘法,乘法高于除法。
第四步:根据填充好的char数组创建字符串对象返回
注:
1.这个方法也就是toString(int i, int radix)方法中对于10进制的处理。可以看到对于10进制的转化,可是花了大功夫在优化上,为了避免使用除法,应用各种移位操作来进行,即使在不得不使用除法的地方也尽可能用除以100而不是除以10来减少除法次数。
2.这里使用getChars去填充char数组,显然写这段代码的之前是一个写C语言这种面向过程的人。对于java这种面向对象的语言来说,这里我们应该用 char[] buf = getChars(i, size);这种方式,以后大家写java代码我也推荐大家使用这种方式。什么没看明白,自己去百度下面向过程和面向对象方面的知识吧。

int类型转换为其它进制的字符串形式:

//转换为16进制
public static String toHexString(int i) {
        return toUnsignedString0(i, 4);
}
//转换为8进制
public static String toOctalString(int i) {
        return toUnsignedString0(i, 3);
}
//转换为2进制
public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
}

可以看到这几个转换的方法都调用了同一个方法toUnsignedString0只是传的参数不同,toUnsignedString0方法如下:

private static String toUnsignedString0(int val, int shift) {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);
        char[] buf = new char[chars];
        formatUnsignedInt(val, shift, buf, 0, chars);
        // Use special constructor which takes over "buf".
        return new String(buf, true);
}

第一步:获取目标值value的二进制形式有效位长度,Integer.numberOfLeadingZeros(val)这个方法是计算value的二进制高位共有多少位0,后面我们再讲这个方法。
第二步:获取缓冲数组的大小
第三步:调用formatUnsignedInt方法,这个方法才是核心,原因如下:

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        int charPos = len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[offset + --charPos] = Integer.digits[val & mask];
            val >>>= shift;
        } while (val != 0 && charPos > 0);
        return charPos;
}

这里就是将10进制数转换为其它进制数的代码体现了,其中 1 << shift相当于2^shift,radix - 1如果转换为16进制就是15如果是8进制就是7,这里是为了后面做&的计算,你想一下如果转换为16进制,一个数&上15(1111),就是取得这个数字的低4位,这样4位4位的计算不就转换为这个数的16进制了吗,转换为8进制同理。后面的val&mask 就相当于 val%(mask+1)也就是进制转换的过程了。
第四步:根据buf数组,创建String对象后返回。

parseInt:

public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        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) {
                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;
}

parseInt有两个方法,作用是将指定进制的数的字符串形式转换为int类型。看上面这个就行。
第一步:参数校验。字符串为null或是radix不在可转换的进制范围内就抛出异常。
第二步:核心处理逻辑是字符串转换数字,其它进制转成十进制,如521为8进制,则结果为5* 8^2 + 28^1 + 18^0.上面的转换方法也差不多是根据此方法,只是稍微转变了思路( (5 * 8+2)*8)+1结果都是一样的。规律就是从左到右遍历字符串的每个字符,然后乘以进制数,再加上下一个字符,接着再乘以进制数,再加上下个字符,不断重复,直到最后一个字符。除此之外另外一个不同就是上面的转换不使用加法来做,全都转成负数来运算,其实可以看成是等价了。因为负数Integer.MIN_VALUE变化为正数时会导致数值溢出,所以全部都用负数来运算。
第三步:返回正确的结果如果是负数就添加负号。

无符号转换:

public static String toUnsignedString(int i, int radix) {
        return Long.toUnsignedString(toUnsignedLong(i), radix);
}
public static long toUnsignedLong(int x) {
        return ((long) x) & 0xffffffffL;
}

这两个方法就是将目标数转换为无符号表示的long或是字符串,无符号意思就是将二进制中的高位的第一位不当作符号位来看待。其中toUnsignedString使用了Long.toUnsignedString方法,一直跟踪源码可以发现实现方式与Integer中的实现方式相同,这里就不再重复了,不明白的同学可以看下Integer中的toString方法和toUnsignedString0方法。

parseUnsignedInt:

public static int parseUnsignedInt(String s, int radix)
                throws NumberFormatException {
        if (s == null)  {
            throw new NumberFormatException("null");
        }
        int len = s.length();
        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar == '-') {
                throw new
                    NumberFormatException(String.format("Illegal leading minus sign " +
                                                       "on unsigned string %s.", s));
            } else {
                if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
                    (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits
                    return parseInt(s, radix);
                } else {
                    long ell = Long.parseLong(s, radix);
                    if ((ell & 0xffff_ffff_0000_0000L) == 0) {
                        return (int) ell;
                    } else {
                        throw new
                            NumberFormatException(String.format("String value %s exceeds " +
                                                                "range of unsigned int.", s));
                    }
                }
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
}

将目标字符串数字转换为指定进制的无符号整型。
第一步:获取第一个字符,判断是否为'-',如果是'-'就抛出异常。
第二步:判断长度小于5或是小于9的10进制,使用parseInt方法长度小于5是因为int类型的最大数转换为最大进制36进制是6位。parseInt方法在上面已经讲过了。
第三步:如果超出第二步判断的范围,就要使用long来转换了,否则可能溢出。 if ((ell & 0xffff_ffff_0000_0000L) == 0) 若未超过int无符号数支持的范围,即数字高八位为0,则返回int值,否则抛出异常,数字超过int可表示的范围。

IntegerCache:

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);
                    i = Math.max(i, 127);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
}

可以看出Integer类也有如同Byte和Short类类似的缓存池机制,而不同点在于Integer的缓存池可以改变上限的大小,通过 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");来获取上限,这个参数可以在JVM中配置。但是在设置的时候这个值是要大于或等于127小于Integer.MAX_VALUE。如果小于127以127为上限。注意。下限是-128这个是不可以改变的。

getInteger:

public static Integer getInteger(String nm, Integer val) {
        String v = null;
        try {
            v = System.getProperty(nm);
        } catch (IllegalArgumentException | NullPointerException e) {
        }
        if (v != null) {
            try {
                return Integer.decode(v);
            } catch (NumberFormatException e) {
            }
        }
        return val;
}

这个方法是从系统属性中查找数据然后转换为对应的Integer对象,如果系统中不存在待查找的属性,则返回null。单从方法名上看Integer.getInteger与Integer.valueOf(由于valueOf方法在之前的Byte类Short类中已经讲过了,都是相似的,这里不赘述)方法功能是一样的,但实际则不然,我们在使用的时候也要小心一点。

decode:

public static Integer decode(String nm) throws NumberFormatException {
        int radix = 10;
        int index = 0;
        boolean negative = false;
        Integer result;
        if (nm.length() == 0)
            throw new NumberFormatException("Zero length string");
        char firstChar = nm.charAt(0);
        // Handle sign, if present
        if (firstChar == '-') {
            negative = true;
            index++;
        } else if (firstChar == '+')
            index++;
        // Handle radix specifier, if present
        if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
            index += 2;
            radix = 16;
        }
        else if (nm.startsWith("#", index)) {
            index ++;
            radix = 16;
        }
        else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
            index ++;
            radix = 8;
        }
        if (nm.startsWith("-", index) || nm.startsWith("+", index))
            throw new NumberFormatException("Sign character in wrong position");
        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? Integer.valueOf(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            // If number is Integer.MIN_VALUE, we'll end up here. The next line
            // handles this case, and causes any genuine format error to be
            // rethrown.
            String constant = negative ? ("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }
        return result;
}

这个方法是用来解析2进制,8进制,10进制16进制表示的字符串的。我们用数字前面加0表示8进制如056,数字前面加0x或是#,如0x1234或是#1234,表示16进制,默认数字为10进制。注意这个方法也不是太智能,如你传100这个字符串,他就不知道是10进制还是2进制,默认当10进制来转换了。又如你给定字符串 0000 0000 0000 0100 ,你想让他按2进制来解析,但实际上他按8进制来解析。所以使用的时候如果有这种有歧义的字符串,要小心一些。

无符号比较:

public static int compareUnsigned(int x, int y) {
        return compare(x + MIN_VALUE, y + MIN_VALUE);
}
public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

无符号就是二进制的最高位符号位不当作符号位来,比较,实现过程也很简单。那么这个函数用来比较两个数-1是最大的,因为-1的所有位都是1,当然最大。

divideUnsigned:

public static int divideUnsigned(int dividend, int divisor) {
        return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));
}

divide意为除的意思,这个方法就是将两个数转换为无符号的数,然后再相除。

remainderUnsigned:

public static int remainderUnsigned(int dividend, int divisor) {
        // In lieu of tricky code, for now just use long arithmetic.
        return (int)(toUnsignedLong(dividend) % toUnsignedLong(divisor));
}

将两个数转换为无符号的数,然后再计算余数,可以看到源码的注释 // In lieu of tricky code, for now just use long arithmetic.意思就是因为这个情况很棘手,所以现在暂时用long型数据,具体待我们看到Long类的源码时再研究

highestOneBit:

public static int highestOneBit(int i) {
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }

乍一看这个方法是干什么的,不明白。按照位运算|和移位运算一步一步的跟下来,你就会发现,将目标数转换为二进制后。保留这个数的最高位1,即从右边开始数第一个1保留。然后其它位全变为0后的结果。如10 ,二进制为1010,保留最高位1,其它位全变0就是1000,结果就为8.

lowestOneBit:

public static int lowestOneBit(int i) {
        return i & -i;
}

同highestOneBit类似,从左边开始数第1位1保留,其它全变0后的结果。

numberOfLeadingZeros:

public static int numberOfLeadingZeros(int i) {
        if (i == 0)
            return 32;
        int n = 1;
        if (i >>> 16 == 0) { n += 16; i <<= 16; }
        if (i >>> 24 == 0) { n +=  8; i <<=  8; }
        if (i >>> 28 == 0) { n +=  4; i <<=  4; }
        if (i >>> 30 == 0) { n +=  2; i <<=  2; }
        n -= i >>> 31;
        return n;
    }

这个函数的作用就是,取一个int类型数的二进制左边共有多少位0.如10的二进制为1010,int共有32位,那么左边就有28个0,即numberOfLeadingZeros(10)结果为28。这个方法作用很简单,但实现这个方法用的一个思想却是很重要,值得我们一学。有兴趣的同学可以看下我的别一篇文章从JDK源码看二分思想

numberOfTrailingZeros:

public static int numberOfTrailingZeros(int i) {
        int y;
        if (i == 0) return 32;
        int n = 31;
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        return n - ((i << 1) >>> 31);
    }

同numberOfLeadingZeros类似,这个方法的作用就是取一个int类型的二进制右边共有多少位0。如10的二进制为1010.右边共有1个0.numberOfTrailingZeros(10)结果就为1。

bitCount:

public static int bitCount(int i) {
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
}

这个方法的作用是,将一个int类型的数转换为二进制后,数里面有多少个1。如10,二进制为1010,有两个1,那么bitCount(10)结果就为2。其核心思想是使用二分法,两两一组相加,之后四个四个一组相加,接着八个八个,最后就得到各位之和了。 第一行是计算每两位中的 1 的个数 , 并且用该对应的两位来存储这个个数 , 如 : 01101100 转换后为 01011000 , 即先把前者每两位分段 01 10 11 00 , 分别有 1 1 2 0 个 1, 用两位二进制数表示为 01 01 10 00, 合起来为 01011000。 第二行是计算每四位中的 1 的个数 , 并且用该对应的四位来存储这个个数 。如 : 01101100 经过第一行计算后得 01011000 , 然后把 01011000 每四位分段成 0101 1000 , 段内移位相加 : 前段 01+01 =10 , 后段 10+00=10, 分别用四位二进制数表示为 0010 0010, 合起来为 00100010 . 下面的各行以此类推 , 分别计算每 8 位 ,16 位 ,32 位中的 1 的个数 。

rotateLeft:

public static int rotateLeft(int i, int distance) {
        return (i << distance) | (i >>> -distance);
       
    }

(i << distance) 先把尾巴那几位空出来成0,然后(i >>> -distance)获得前面的那几位,然后按位或运算,就旋转了,即循环左移。

rotateRight:

public static int rotateRight(int i, int distance) {
        return (i >>> distance) | (i << -distance);
}

跟前面的rotateLeft一样,先把位置空出来,然后取得对应的二进制位,按位或运算,就成了。

reverse:

public static int reverse(int i) {
        i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
        i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
        i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
        i = (i << 24) | ((i & 0xff00) << 8) |
            ((i >>> 8) & 0xff00) | (i >>> 24);
        return i;
}

将目标数转换为32位二进制数据后,高位和低位对应位置数据互换。如10转换为2进制后为00000000 000000000 00000000 0001010则转换后数据为01010000 00000000 00000000 00000000怎么样很好理解吧。其实Integer中的很多操作都是转换为二进制后,再处理的,光看代码很复杂。把数据转换成二进制,然后再对照代码看就很简单了。

signum:

public static int signum(int i) {
        return (i >> 31) | (-i >>> 31);
}

这个方法我还没研究明白是干什么用的,i >> 31这个如果i为正数运算后全变为0,如果是负数运算后全变为1(指的是二进制位),(-i >>>31) 如果i为正数,运算后为1,如果i为负数运算后为0。两个数做|运算后,如果i为正则结果为1,如果为负数结果为-1,如果是0则结果为0,估计是运来判断一个数是正数负数还是0吧。如果是这样的话直接跟0做比较不就好了,写这段代码干什么呢,搞不明白。

reverseBytes:

public static int reverseBytes(int i) {
        return ((i >>> 24)           ) |
               ((i >>   8) &   0xFF00) |
               ((i <<   8) & 0xFF0000) |
               ((i << 24));
}

这个方法同Byte类的reverseBytes方法有点类似。但这里是高8位与低8位互换,中间的两个8位互换之后的结果。转换成二进制后就一目了然了,我就不举例了。

其它方法:

//求两个数之和
public static int sum(int a, int b) {
   return a + b;
}
//取两个数中较大的数
public static int max(int a, int b) {
    return Math.max(a, b);
}
//取两个数中较小的数
public static int min(int a, int b) {
    return Math.min(a, b);
}

这几个方法挺简单的,大家一看就明白了。

注:

  • Integer类中的很多方法都是操作二进制数的。光看代码可能一头雾水,转换为二进制后,再辅助代码就直观多了。
  • 这个类中涉及了很多的位运算,如果对位运算不清楚的可以看下一起学JDK源码 -- Byte类和本章的基础知识部分。
  • 这个类中有一些方法如signum、reverseBytes等操作二进制数的思想到是很精彩。但实际不知道有什么用。不知道是作者炫耀它的思想,还是我太菜没看出来他的意图。

查看所有目录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,138评论 0 13
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,172评论 1 44
  • 1 关键字 1.1 关键字的概述 Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或...
    哈哈哎呦喂阅读 646评论 0 0
  • 在知识变现如此多元化的今天,掌握学习方法,能让你在人群中快速脱颖而出。身为普通人的我们,学习就成了一门非常重要的技...
    陶壹阅读 127评论 0 0
  • 1人最不愿意被别人救赎。我想在以后更多倾听别人。之前会想着自己学了挺多,给别人建议。所幸,还有点觉知。 2.不需要...
    刘姥姥2017阅读 75评论 0 0