`
xiaoheliushuiya
  • 浏览: 401805 次
文章分类
社区版块
存档分类
最新评论

Java 7源码分析第3篇 - Java数值类型

 
阅读更多

这一篇主要介绍Java中经常使用的整数类型 - Integer

首先来说一下位运算,位运算应用是非常广的。无论是名企的笔试、面试,还是Java的源代码,这种应用随处可见。关于位运算我不想说太多,可以给大家推荐一篇非常不错的博文,地址如下:

http://blog.csdn.net/morewindows/article/details/7354571

同样,在Integer.java类的源代码中,Java的设计师们为了提高效率使用了大量的位运算,首先来看一个简单的进制转换源代码:

    //无符号整数 16进制,向右移动4位
    public static String toHexString(int i) {
        return toUnsignedString(i, 4);
    }
    //无符号整数 8进制,向右移动3位
    public static String toOctalString(int i) {
        return toUnsignedString(i, 3);
    }
   //无符号整数  2进制,向右移动2位
    public static String toBinaryString(int i) {
        return toUnsignedString(i, 1);
    }
    // 对无符号整数进行转换
    private static String toUnsignedString(int i, int shift) {
        char[] buf = new char[32];
        int charPos = 32;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[--charPos] = digits[i & mask];
            i >>>= shift;//i=i>>>shift
        } while (i != 0);

        return new String(buf, charPos, (32 - charPos));
    }
可以看一下对于10进制无符号整数的进制转换。由于缺少边界等的检查,所以toUnsignedString()方法并没有公开,而是提供了多个常用进制转换的方法。最后返回了转换后的字符串表示形式。至于源码中为什么要定义一个32的字符数组后面将会分析到。

我们知道,Java中的int数值类型占用4个字节,有时候需要对表示一个int数值的4个字节做一些特殊的处理,如字节反转、位反转等,源代码如下:

 // 将4个字节的顺序进行逆转,并不是对位进行逆转
    public static int reverseBytes(int i) {
        return ((i >>> 24)           ) |
               ((i >>   8) &   0xFF00) |
               ((i <<   8) & 0xFF0000) |
               ((i << 24));
    }
    // 对位进行逆转
    public static int reverse(int i) {
        // HD, Figure 7-1
        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;
    }

大家可以好好研究一下如上的代码是怎么实现他们的功能的。继续看下面的两个方法:

   public static int highestOneBit(int i) {
        // HD, Figure 3-1
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }   
    public static int lowestOneBit(int i) {
        return i & -i;
    }

highestOneBit()作用是取 i 这个数的二进制形式最左边的最高一位且高位后面全部补零,最后返回int型的结果。而lowestOneBit()自然就是取最右边的第一位1,其前面全部置0后的结果。代码类中还提供了其他的一些方法,这些方法也大量用到了位操作,有兴趣的可以自己去看一下。
来看一下类中的其他重要的方法。

 // 如下的这些ASCII字符可能会表示为数字.26个字符和10个数字,加起来是36
    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)
            radix = 10;
        /* Use the faster version */
        if (radix == 10) {
            return toString(i);
        }
        char buf[] = new char[33];
        boolean negative = (i < 0);
        int charPos = 32;
        // 当i为一个正数时,变为负数
        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));
    }
如上源代码片段首先定义了一个digits字符数组,这些数组是可以表示数值类型的。如在十六进制中,a可以表示10,b可以表示11一样,26个字母同样可以代表一个两位数的整数,加上10个数字后,共有32个字符可以用来表示数值。由此可知,Java中支持的最大进制为32,如果大于32,则按10进制进行处理。接下来有一个toString()方法,功能是将十进制的i转换为radix进制的数,并以字符串的形式进行返回。如下测试代码:
System.out.println(Integer.toString(13,19));
System.out.println(Integer.toString(173,29));
运行结果如下:d // 13 5s // 5*29+28

需要注意的是toString()方法的实现代码,可以看到对于一个有符号数i,都是转换为负数来进行进制转换的,这样可以统一进行有符号数的处理。那么可不可以转换为正数进行处理呢?这时候就需要考虑边界的问题了。举个例子,如byte类型,表示的最小整数为-128,而最大的整数为127,这时候将负数转换为正数就会超出类型所表示的范围,在这里的道理是一样的。
继续看另外一个toString()方法的源代码:

 public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        //举例:如果是9,这时size为1,而如果为-9时,为2,还要存储一个"-"号
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);// 计算出保存整数需要的字符数组大小
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(0, size, buf);
    }
    //如下存储的是10进制的数
    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
        99999999, 999999999, Integer.MAX_VALUE };// 最大的MAX_VALUE为2147483647
    // Requires positive x
    static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }
功能就是将一个有符号的整数转换为字符串,首先是将这个整数转换为字符数组,然后将这个字符数组转换为字符串,getChars()方法的源代码如下:

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

        // Generate two digits per iteration
        // 处理超过4个字节能表示的最大数范围的数,也就是处理Unicode的增补字符
        while (i >= 65536) {// 2^16=65536
            q = i / 100;
            // 2^6+2^5+2^4=100
            r = i - ((q << 6) + (q << 5) + (q << 2));// 相当于r = i - (q * 100);
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

        // Fall thru to fast mode for smaller numbers
        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;
        }
    }

为了能够快速将数值转换为字符类型,程序采用了空间换时间的策略,预先使用数组存储了一些需要的数。

这段代码看的我火急火燎的,主要有几个问题没弄明白:

(1)为什么要对大于和小于65536的数要分开处理?

(2)q=(i*52429)>>>(16+3)不太看得懂

哪位大神给指点一下感激不尽~~~

好了,我们继续往下看,再一个主要的方法就是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;
    }

如上函数的功能就是将字符串转换为整数,接受以八进制、十进制和16进制格式书写的字符串,测试如上方法:

System.out.println(Integer.decode("0x0011"));
System.out.println(Integer.decode("0X0011"));
System.out.println(Integer.decode("011"));
System.out.println(Integer.decode("-011"));
最后运行的结果如下:19 19 9 -9

合法的字符串表示的数值最后都是以十进制的形式返回的,或者还可以使用parseInt()方法,源代码如下:

 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;
    }
由于这个函数是公开的,所以对边界条件的检查特别严格。将指定进制的有符号整数转换为十进制的整数返回,测试代码如下:

parseInt("0", 10) returns 0
parseInt("473", 10) returns 473
parseInt("+42", 10) returns 42
parseInt("-0", 10) returns 0
arseInt("-FF", 16) returns -255
parseInt("1100110", 2) returns 102
parseInt("2147483647", 10) returns 2147483647
parseInt("-2147483648", 10) returns -2147483648
parseInt("2147483648", 10) throws a NumberFormatException
parseInt("99", 8) throws a NumberFormatException
parseInt("Kona", 27) returns 411787
需要注意的是:

parseInt("Kona", 10) throws a NumberFormatException //因为十进制不可能出现K字符,所以出现异常
程序会出现错误,因为10进制的数值中只能出现0到9的数字,这主要是通过Character.digit()函数进行判断的。关于这个函数的源代码将在字符源码剖析中解析。



PS:后续要讲解一下bigInteger类
















分享到:
评论

相关推荐

    java范例开发大全(pdf&源码)

    第3篇 Java面向对象编程 第8章 面向对象(教学视频:72分钟) 226 8.1 类 226 实例148 简单的通讯录类 226 实例149 简单的长度单位转换类 227 实例150 卡车和卡车司机之间的关系 229 实例151 双色球 231 8.2 成员...

    Java核心技术II(第8版)

    第三章 网络 3.1 连接到服务器 3.1.1 套接字超时 3.1.2 因特网地址 3.2 实现服务器 3.2.1 为多个客户端服务 3.2.2 半关闭 3.3 可中断套接字 3.4 发送E-Mail 3.5 建立URL连接 3.5.1 URL和URI 3.5.2 使用URLConnection...

    Java范例开发大全(全书源程序)

    第3篇 Java面向对象编程 第8章 面向对象(教学视频:72分钟) 226 8.1 类 226 实例148 简单的通讯录类 226 实例149 简单的长度单位转换类 227 实例150 卡车和卡车司机之间的关系 229 实例151 双色球 231 8.2...

    java 面试题 总结

    Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型封装类 booleanBoolean charCharacter byte...

    超级有影响力霸气的Java面试题大全文档

     Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型 封装类 boolean Boolean char Character ...

    MySQL5.1参考手册官方简体中文版

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    MySQL 5.1官方简体中文参考手册

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    MySQL 5.1参考手册

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    网管教程 从入门到精通软件篇.txt

    网管教程 从入门到精通软件篇 ★一。★详细的xp修复控制台命令和用法!!! 放入xp(2000)的光盘,安装时候选R,修复! Windows XP(包括 Windows 2000)的控制台命令是在系统出现一些意外情况下的一种非常有效的...

    PHP3程序设计

    第3章 PHP中的数据处理 19 3.1 数值 19 3.1.1 数字 19 3.1.2 文本 20 3.2 变量 23 3.2.1 标量 23 3.2.2 数组变量 24 3.2.3 多维数组 27 3.2.4 变量替换 28 3.2.5 动态变量名 31 3.3 常量 31 3.4 操作符 31 3.4.1 ...

    mysql官方中文参考手册

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    MYSQL中文手册

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K...

    MySQL 5.1参考手册中文版

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K...

    c#学习笔记.txt

    可以把任何类型的数值给object类型. 7,string类型 string 类的实例表示 Unicode 字符串。尽管 string 是引用类型,但相等运算符(== 和 !=)被定义为比较 string 对象(而不是引用)的“值”(7.9.7 字符串相等...

    MySQL 5.1中文手冊

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    MySQL 5.1参考手册 (中文版)

    11.1.1. 数值类型概述 11.1.2. 日期和时间类型概述 11.1.3. 字符串类型概述 11.2. 数值类型 11.3. 日期和时间类型 11.3.1. DATETIME、DATE和TIMESTAMP类型 11.3.2. TIME类型 11.3.3. YEAR类型 11.3.4. Y2K事宜和日期...

    Python语言程序设计源代码.zip

    第3章Python组合数据类型。组合数据类型是Python语言区别于其他高级编程语言的一大特色,通过组合数据类型,省去了其他语言各种复杂数据结构的设计,给编程人员带来了极大的方便,这也是Python流行于数据分析领域的...

    jpivot学习总结.doc

    属性很多,并且是 schema 编写的关键,使用它可以构成一个结构树, Level 的先后顺序决定了 Level 在这棵树上的的位置,最顶层的 Level 位于树的第一级,依次类推。 Level 的属性如下: 属性名 含义 name 名称 ...

Global site tag (gtag.js) - Google Analytics