java.lang包中的包装类源码分析

八个基本数据类型byte,char,short,int,long,double,float,boolean,对应的包装类位于java.lang包下面。只有对数据类型更好的了解,才能更高效的使用,更得心应手。本文通过整体分析来了解八个包装类和一个字符串类String,分析类设计共性,几个主要方法,并深入方法的源码,探索怎么实现的。

包装类设计共性

一、静态工厂方法 static valueof()

通过阅读源码或者jdk文档就可以知道,大部分包装类都以静态常量(static final)或者静态类(static class)的方式存放着本身的实例。并且每个包装类都有一个静态工厂方法valueof(),用来创建对象。 即使他们都有几个构造器,但我不建议使用,如果你知道valueof()的实现方式的话,你也会有同感。valueof()会判断要创建的实例是否已经存在,如果存在就直接使用。通过这个方法就可以避免创建出多余的对象造成内存和时间浪费。

例如boolean的包装类Boolean ,用两个常量TRUE,FALSE分别存放他的两个实例。

public final class Boolean implements java.io.Serializable,
                                      Comparable<Boolean>
{
    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);

valueof()的实现则在此的基础上,使用这个方法,都可以不用再创建任何对象了。(Boolean比较特殊,只有两个实例)

  public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

    public static Boolean valueOf(String s) {
        return parseBoolean(s) ? TRUE : FALSE;
    }

接下来,让我们看看byte类型的包装类,Byte,我们都知道 byte的范围[-128, 127], 所以Byte的所有实例总共有128+127+1种,如果直接用静态常量来接收,那整个类不就会显得臃肿,所以,Byte类采用静态类的方式缓存,并使用静态代码块的方式一次性加载,并只缓存一次。

public final class Byte extends Number implements Comparable<Byte> {

    /**
     * A constant holding the minimum value a {@code byte} can
     * have, -2<sup>7</sup>.  最小值
     */
    public static final byte   MIN_VALUE = -128;

    /**
     * A constant holding the maximum value a {@code byte} can
     * have, 2<sup>7</sup>-1.最大值
     */
    public static final byte   MAX_VALUE = 127;
    // 私有静态类,缓存所有Byte实例
    private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];
        // 当 i=0, cahe[0] = -128, cahe[1] = -127..... 以此类推
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }

如何发挥上面这块代码的作用呢? 如果你还在用这样的方式 new Byte((byte)120); 来创建对象的话,这静态类就没有存在的意义了。valueof()致力于创建对象时减少内存的消耗。下面Byte类的这个方法实现:这一看出,valueof()不会创建新的对象。完全从缓存类中返回。cache[] = {-128,-127-126,,... 0 ,1,2,... ,125,126,127}

 /**
     * @param  b a byte value.
     * @return a {@code Byte} instance representing {@code b}.
     * @since  1.5
     */
    public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }

其他的包装类 Character ,缓存示例的个数也是char的范围。

 private static class CharacterCache {
        private CharacterCache(){}

        static final Character cache[] = new Character[127 + 1];

        static {
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Character((char)i);
        }
    }

静态工厂方法,会判断,如果在实例范围内,则直接从缓存中拿,超过范围在创建新对象:

 public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

此外,另外五个包装类,也都是类似的设计,都是为了高效的创建对象,减少内存消耗。

------2018,-3-19, 补充:

稍微看了一下两个浮点数 double, float,对应的类。Double,Float 。这两个的设计和其他的不同,就算使用valueof方法,也是直接创建新对象,而并有事前已经创建好的对象。

    public static Double valueOf(String s) throws NumberFormatException {
        return new Double(parseDouble(s));
    }
  public static Float valueOf(String s) throws NumberFormatException {
        return new Float(parseFloat(s));
    }

二、作为数据类型,基本都重写了,equals(), hashCode(), 方法 ,另外,因为都实现了Comparable接口,所以都重写了他的比较方法 compareTo()方法。 但我感觉这里的这两个方法实现有点简单,如果作为集合元素,需要排序时,可能又要重写。

Byte类:

public static int hashCode(byte value) {
        return (int)value;
    }

    public boolean equals(Object obj) {
        if (obj instanceof Byte) {
            return value == ((Byte)obj).byteValue();
        }
        return false;
    }

Integer类

public boolean equals(Object obj) {
        if (obj instanceof Integer) {    // 一定要做这个判断 ,object是不是Integer类型的
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
 
 
public int compareTo(Integer anotherInteger) {
    return compare(this.value, anotherInteger.value);
}
// x 是当前对象, x.compareTo(y), x < y 返回 -1, 相等 返回0 , x>y 返回1 
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

这些方法的实现都大同小异,这里就不一一列举了。下面我会根据自己看的情况,详细分析几个方法的实现过程,这几个方法的实现,也都会使用到另外几个类的方法。

几个重要方法

1、 parseXXX(String s, int radix ) 方法, 

XXX表示基本数据类型(parseInt, parseByte, parseLong 等), 这个方法是用来: 根据radix 进制,把字符串转化为对应的数据类型。radix既然表示进制, 就说明他的2的幂数(2,8,10,16,32),通过观察,Integer类的这个方法比较有代表性,这里就使用Integet类的源码了。另外 两个浮点数类型的 这个方法的实现和长整数的不同,我后面再会补充。

首先看一下效果:

//使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
    public static void testParseInt(){
        int i1 = Integer.parseInt("-111",2);   // -7 
        int i2 = Integer.parseInt("111",4);   // 21
        int i3 = Integer.parseInt("111",8);   // 73
        int i4 = Integer.parseInt("111",10);  // 111
        System.out.println(i1+"--"+i2+"--"+i3+"--"+i4);

    }

源码分析:

下面源码用到了Character 类的一个方法,digit(char ch,  int radix), ,这个方法是用来检测字符窜中的字符是否为数字,并且是符合 radix 基数的数字。  如果基数不在 MIN_RADIX <= radix <= MAX_RADIX 范围之内,或者 ch 的值是一个使用指定基数的无效数字,则返回-1,否则返回本身。

 public static void testDigst(){
        String s = "-5";
        int i = Character.digit(s.charAt(1),2);
        System.out.println(i);      //  结果 -1

        // 基数(用进制比较好理解)如果字符串中的字符是非数字, 则返回-1, 
        // 如果字符串的字符不符合进制的表达式, 则返回-1,  例如: 上面,基数是2, 那么只能存在0
        // 或者 1 这两个数字,(完全可以理解为,字符串中需要合法的对应的进制表示方式)

        int i2 = Character.digit(s.charAt(1),8);
        System.out.println(i2); // 结果  5   ,  因为八进制可以用0 到7 之间的数来表示
        
        s= "-a";
        System.out.println(i); // 结果 -1,  因为是非数字
    }

方法的大体思路:

1. 判断String 是否为空。空就抛出异常。

2.判断进制是否合理(radix), 不合理抛出异常。

3.对String中的 单个字符进行处理,判断正负,  如果是非数字, 抛出异常。

4.主要的计算过程: result = result*radix - digit

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");
        }
        // radix 最小值是2, 不能小于2 Character.MIN_RADIX=2
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }
        // radix 最大值32,  
        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;        // 用来判断值的正负 , false 是正,  true是负
        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);    // digit方法,
                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;
    }

2、static XXX  XXXValue() , XXX 表示基本数据类型(double, int ,byte, char...),将某个类转化为对应的数据类型。

3、static xxx decode(String str),    将字符串转化为方法调用者类类型数据。

=====后面再补充2,3


猜你喜欢

转载自blog.csdn.net/qq_41694349/article/details/79580167
今日推荐