zoukankan      html  css  js  c++  java
  • Java Integer剖析

    Java Integer剖析

    获取String的整型值的方法

    • public Integer valueOf(String str) Java获取字符串的十进制Integer整型值
    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }
    

    底层调用的是Integer.parseInt(String s, int radix),然后通过Integer.valueOf(int i)将parseInt返回的int值封装成Integer对象。

    注意:Integer.valueOf(int i)中对需要封装成Integer的int值做了缓存,常用的Integer值,默认[-128~127]可直接通过缓存获取,否则新建Integer。这样也就导致了一个Integer的自动装箱的问题,后面谈到equals==时我们再来分析。

    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];//IntegerCache数组中存在,直接返回Integer 对象,否则创建新Integer对象
        return new Integer(i);
    }
    

    当然这里的缓存int的最大值是可以设置的,通过java.lang.Integer.IntegerCache.high属性来设置。

    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) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);//默认最小的max值是127
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);//确保cache数组的大小不超过Integer的最大限度
            }
            high = h;
    
            cache = new Integer[(high - low) + 1];//创建缓存数组,给定大小
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);//初始化缓存数组
        }
    
        private IntegerCache() {}
    }
    
    • public int parseInt(String str) 解析String的int值,返回int型数值

      parseInt(String str) ,底层调用int parseInt(String s, int radix), radix默认10

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

    parseInt(String s, int radix)的实现如下。这个方法也是很著名的atoi(字符串转int),面试题里面出现的概率很高——想想如果让自己写代码来实现,能否写的出来?

    /**
    *@param s 要转换成int的String字符串。parseInt只接收带‘+’,‘-’或纯数值(8进制,16进制,10进制),不自动判断进制数, 需要靠后面的radix来指定———区别于decode(String str)
    *@param radix String字符串中的数字的进制数
    *@return 转换后的十进制数
    */
    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);//获取char的int值
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;//如传入String为“123”,radix为10.计算过程为i = ((-1*10 - 2)*10 - 3)*10 
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }
    

    parseInt(String s,int radix)就是求int radix进制数String sradix进制数是多少。

    • Integer decode(String nm) decode方法可以接收带有'0x', '0X', '#'(16进制),'0'(8进制)前缀的字符串,自动判断进制数,底层调用的Integer.valueOf(String str, int radix)——> Integer.parseInt(String str, int radix)

      decode(String str)相对于parseInt(String str, int radix)多了自动判断进制数的功能,且返回值是Integer对象。

    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);//底层调用valueOf(String str, int radix) --> parseInt(String str, int 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;
    }
    
    • Integer.getInteger(String str, Integer val); 此方法用于获取系统属性的Integer值
    /**
    * 如果需要获取系统的属性值的话,推荐使用getInteger(String nm, Integer val),可以省去一层调用和一个判断
    */
    public static Integer getInteger(String nm, int val) {
        Integer result = getInteger(nm, null);
        return (result == null) ? Integer.valueOf(val) : result;
    }
    
    /**
    *@param nm 系统属性的名字,如"java.lang.Integer.IntegerCache.high"
    *@param val 获取系统属性失败的情况下的默认值
    *@return 属性对应的Integer值
    */
    public static Integer getInteger(String nm, Integer val) {
        String v = null;
        try {
            v = System.getProperty(nm);
        } catch (IllegalArgumentException e) {
        } catch (NullPointerException e) {
        }
        if (v != null) {
            try {
                return Integer.decode(v);//底层调用的decode,把str解析成对应的十进制Integer
            } catch (NumberFormatException e) {
            }
        }
        return val;
    }
    

    总结

    Atoi使用推荐 返回值Integer 返回值int
    str是十进制 valueOf(String str) parseInt(String str)
    str非十进制 decode(String str)(需解析radix) |valueOf(String str, int radix)(不需要解析radix) parseInt(String str, int radix)(str不能带radix标识,但可以带‘+’、‘-’号)

    Integer中的其它方法

    • compareTo(Integer anotherInteger) 比较两个Integer数值的大小
    /**
    * @param 要比较的另一个Integer
    * @return 相等返回0,小于anotherInteger返回-1,大于anotherInteger返回1
    */
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    

    底层使用的方法

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
    
    • Integer中的equals方法
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    

    关于equals(Object obj)==,自动装箱的坑

    前两天看到一个面试题,大体就是下面这样的代码:

    public class Test {
        public static void main(String[] args) throws Exception {
            Integer i1 = 10, i2 = 10, i3 = 128, i4 = 128;
            System.out.println(i1 == i2);
            System.out.println(i1.equals(i2));
            System.out.println(i3 == i4);
            System.out.println(i3.equals(i4));
        }
    }
    

    看这一段代码,我第一反应就是

    true
    true
    true
    true
    

    结果实际执行效果是

    true
    true
    false
    true
    

    仔细研究了一下,发现JVM在自动拆装箱的时候会调用valueOf()方法,让我们来看一下Integer的valueOf()方法:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    

    注释里写明了Integer会缓存[-128, 127]之间的值,结合代码也可以看出如果Integer对象携带的整形如果是[128, 127]之间则直接返回这个Integer,否则新建一个Integer。

    这个坑就显而易见了, Java中==比较的是地址,两个不同的对象地址显然不一样,所以会有上面令我匪夷所思的结果。 
    这坑让我意识到即使Java里有自动拆装箱, 也不能依赖这个特性,否则就是深渊呐,对象还是老老实实的用equals(T)比较吧

    • toString()方法
    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);
    }
    

    相关的方法实现

    • [ ] stringSize(int x); 返回正整数x的位数
    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };
    
    // Requires positive x, 返回正整数x的位数
    static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }
    
    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 signum(int i); 判断i的值是否大于0,如果i是正数,返回1;i等于0,返回0;i为负数,返回-1.
    public static int signum(int i) {
      // HD, Section 2-7
      return (i >> 31) | (-i >>> 31);
    }
    

    Integer高级方法总结

    • //highestOneBit。保留最高位的1,同时将低位全部清零

      System.out.println(Integer.highestOneBit(1023));
      System.out.println("lowest one bit: " + Integer.lowestOneBit(12));
      
    • //numberOfLeadingZeros。返回最高位的1之前0的个数。例如:1101000即104返回32-7=25

    System.out.println("number of leading zeros: " + Integer.numberOfLeadingZeros(104));//25
    System.out.println("number of leading zeros: " + Integer.numberOfLeadingZeros(2));//30
    
    • //numberOfTrailingZeros。返回最低位的1之后0的个数。例如:1101000即104返回3

    System.out.println("number of trailing zeros: " + Integer.numberOfTrailingZeros(104));//3

    • //reverse。反转二进制补码中位的顺序。即将第32位的值与第1位的值互换,第31位的值与第2位的值互换,等等,依次
    System.out.println("reverse: " + Integer.toBinaryString(Integer.reverse(7)));//得11100000000,即最低位的三个一跑到最高位去了
    System.out.println("reverse: " + Integer.toBinaryString(Integer.reverse(13)));//得到101100000
    
    • //reverseBytes:将第一个字节与第四个字节的位置互换,第二个字节与第三个字节位置互换

    System.out.println("reverse bytes: " + Integer.toHexString(Integer.reverseBytes(0x4835)));//打印35480000

    • //rotateLeft。将i左移distance,如果distance为负,则右移-distance
    System.out.println("rotate left: " + Integer.rotateLeft(7, 2));//打印28
    
    System.out.println("rotate left: " + Integer.rotateLeft(28, -2));//实际为向右移2,打印7
    
    • //rotateRight。将i无符号右移distance,如果distance为负,则左移-distance。负的肯定会移成正的。

    System.out.println("rotate left: " + Integer.rotateRight(-7, 2));//打印28

  • 相关阅读:
    找出水王
    第九周进度表
    [设计模式]组合模式
    [设计模式]外观模式
    [设计模式]策略模式
    [设计模式] 6个设计遵循基本原则
    [OSGI]Eclipse4.2 OSGI依赖Bundle
    [xfire]使用xfire开发webservice的简单示例
    [HTML5 Canvas学习]使用颜色和透明度
    [HTML5 Canvas学习]绘制矩形
  • 原文地址:https://www.cnblogs.com/laphi/p/6507583.html
Copyright © 2011-2022 走看看