Java源码分析——包装类与Void类解析

    在Java中,有着8种基本类型,其对应着8种包装类型,分别为:Integer、Long、Short、Boolean、Float、Double、Byte以及Character类,这8种包装类型分别封装了对应类型的常用操作以及一些优化操作,比如包装类的缓存,自动装箱等。它们之间的关系如下:
Java源码分析——包装类与Void类解析_第1张图片
    在Number抽象类中定义了一些系列的基础类型的转换行为,所以关于数字操作的都继承自Number抽象类,而Boolean类与Character类则不是,其源代码如下:

public abstract class Number implements java.io.Serializable {
    public abstract int intValue();
    public abstract long longValue();
    public abstract float floatValue();
    public abstract double doubleValue();
    public byte byteValue() {
        return (byte)intValue();
    }
    public short shortValue() {
        return (short)intValue();
    }
    private static final long serialVersionUID = -8742448824652078965L;
}

    而阅读这8个包装类的时候会发现每个类中有这样一段代码:

public static final Class<包装类> TYPE = (Class<包装类>) Class.getPrimitiveClass("对应的包装类的基本类型");

    Class类的getPrimitiveClass方法是一个Native方法,该方法的作用是根据基本数据类型的名字来获取对应的Class类的对象的,也就是说不仅在Java中引用是有其Class类对象,基本的数据类型也具有,我们可以用该方法来直接得到该包装类的类型:

 System.out.println(Integer.TYPE);
 //输出:int

    为了在包装类与基本数据类型之间方便相互转换,Java实现了一个"语法糖",包装类的自动装箱与拆箱。什么是自动装箱,比如有下面一段代码:

 Byte a=12;
 Integer c=12;

    当把基本的数据类型直接赋值给对应的包装类的时候就会发生自动装箱机制,将对应的基本类型转化为对应的包装类对象。其中的原理是调用了每个包装类对应的的valueOf方法,也就是说上面的代码可以写成如下的形式:

Byte b=Byte.valueOf((byte) 12);
Integer c=Integer.valueOf(12);
int d=new Integer(10); 3

    而卸载则是刚好反了过来,如3式。以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);
    }

    当然,你会发现其中的IntegerCache类,这个类是用来干嘛的呢?是用来利用缓存来优化的,除了Boolean类、Float类、Double类外,其它的五个包装类都实现了缓存,其中Integer、Long、Short、Byte类实现了一个字节数的缓存,即-128~127之间是直接从缓存中获取,而不用直接创建的,而Character类是缓存了0到255的字符。以Byte类的缓存为例,源码如下:

private static class ByteCache {
        private ByteCache(){}
        static final Byte cache[] = new Byte[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }

    从代码中可以看出,缓存类是一个静态类,也就是说缓存实在类加载的时候就已经完成了,当调用valueOf方法创建包装类的时候,如果在其缓存的范围内,则直接从缓存中取出,否则直接创建一个新的包装类,这样就实现了局部的优化,知道了这个缓存机制的话,就可以在如下的代码中轻松的判断出结果:

Byte a=1;
Byte b=1;
Byte c=new Byte((byte)1);
System.out.println(a==b);//true
System.out.println(a==c);//false

    另外有趣的是,Float类与Double类定义了NaN,也就是表示无穷,表示一个不确定的数,这个含义是两个NaN做相等比较永远是false的:

//正无穷大
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
//负无穷大
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
//无穷大
public static final double NaN = 0.0d / 0.0;
System.out.println((0.0d / 0.0)==(0.0d / 0.0));//false

    关于Void类的源码如下:

public final
class Void {
    /**
     * The {@code Class} object representing the pseudo-type corresponding to
     * the keyword {@code void}.
     */
    @SuppressWarnings("unchecked")
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
 
    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

    官方对它的解释是Void类是一个不可实例化的占位符类,它持有对标识Java关键字void的Class对象的引用。它用于模拟void类型,是不可实例化的,它的值只有null值,至于好处貌似就是它的用途。

你可能感兴趣的:(JavaSE基础,Java源码分析与思考)