重走Java路-基本数据类型的包装类

    五一小长假过的有点腐,一直都没有更新博客,本来计划五一之前就应该总结的基本数据包装类一直拖到了现在。言归正传开始介绍八种基本数据类型的包装类。

    八种基本数据类型及其包装类如下:

基本数据类型包装类缓存区间
byteByte-128~127
shortShort-128~127
intInteger-128~127
longLong-128~127
floatFloat
doubleDouble
charCharacter0~127
booleanBoolean

 

 

 

 

 

 

 

 

 

    上面既然提到了缓存区间,这里我们以int的包装类Integer说一下缓存区间的含义。

    打开Integer源码,找到valueOf()方法

/**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

//---------------------------------------------------------------------------------------
 /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    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) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }




    通过注释以及阅读源代码,我们可以发现只有当 i >= IntegerCache.low && i <= IntegerCache.high不成立即 i<-128或者 i>127时,该方法才会从新去new一个新的Integer()对象。当i属于[-128~127]区间内时,是直接返回的IntegerCache类中cache[]中的值即缓存区间中的值。至于这里为什么提到缓存区间,我们后面慢慢看。

    话说呢那是三年前,一个工作两年的小子自以为自己已经会了很多东西,也做了几个项目能熟练的使用spring、springmvc、mybatis等框架。完了之后呢,他就出去面试,结果一个面试官也算是不按常理出牌上来就是一个忒简单的问题-“int的取值范围”尴尬的是那小子没答出来。面试的结果很显然没有通过,不过面试官说了一句干了一年两年要有点变化,不能一味的只是会用有时候我们要去看看,人家的代码是怎么实现的,去读一读源码。

    被鄙视完了之后回来看了看相关资料,读了读源码,同时也看看了相关面试题,发现其实之前那个面试官提的不按套路出牌的问题其实是一个非常经典的面试题,而且这个问题可以进一步深挖最大值最小值是怎么得到的以及int和integer的比较等等。

    下面我们来看一个经典的面试题:请问如下代码会输出什么结果

public static void main(String[] args) {
		//基本数据类型与包装类
		int a = 100;
		Integer b = 100;
		System.out.println(a == b);
		
		//包装类与包装类赋值属于[-128~127]
		Integer c = 100;
		Integer d = 100;
		System.out.println(c == d);
		
		//报专类赋值超过127
		c = 200;
		d = 200;
		System.out.println(c == d);
		
		
	}

    我一贯的原则就是喜欢先揭晓答案,输出如下:

true
true
false

    看到这个输出结果你一定会想what?怎么会这样呢,这个时候如果你认真阅读了上文我想你一定会猜测到是不是缓存区间捣的鬼,这里我可以负责任的告诉你就是缓存区间捣的鬼。那么这个环节区间到底是怎么捣鬼的呢,还有integer与int进行比较的时候为什么也是true呢明明是两种不同的类型啊?如果你还是有这样的疑问,那么请你耐心点继续往下读,我们一起慢慢品。

    首先呢,我们来说一说,int a = 100与Integer b = 100的比较。这个故事要从很久很久以前说起,话说呢Java从诞生之初就以面向对象的编程风格受到了广大程序员的追捧,以至于有了广泛流传于Java帝国的那句谚语-“万事万物且对象”(C语言-你们Java真会玩,整的这么玄虚又这么这么哲学)。可是呢这样又出现了一个问题如果真的“万事万物且对象”了,那以Java这种栈中存引用堆中存对象的内存模型,怎样的去高效存取1、2、3、、、这样的数字呢。Java之父-詹姆斯·高斯林 (James Gosling)一开始就想到了这个问题于是他定义了byte、short、int、long、float、double、char、boolean这八种基本数据类型,将他们的值直接存储于栈中以便于高效存取。

    What?你上面叨叨了这么多到底想表达个啥,我们的问题是integer与int的比较。还有你要记住我们Java是面向对象的语言你这个基本数据类型int将值直接存在栈中也许效率是高一些,可是他也就能做的数字存取,加减乘除啥的这功能未免也太少了吧。好吧既然你这样说了、既然你非要面向对象,那么我们就把int包装一下包装成我们想要的对象,我们姑且就称呼这个动作叫做“装箱”吧。

    “装箱”这个词好熟悉啊,我记得上学那会我们老师给我们讲集合、讲泛型的是时候说过,泛型的对象不能是基本数据类型,泛型的对象必须是原始类型,啥、啥玩意、“原始类型”。简单点说方向的的对象必须是object类及其子类,咱们说的基本数据类型是个特殊的存在,它不能被泛型。至于泛型是啥,这里不详细说明了,简单点说就是为了规范我们码农编程便于提取暴露问题,让问题在编译期间就得以暴露,不至于出现猫的集合里面蹦出了狗狗。

    上面既然说到了“装箱”相信你一定也想到了“拆箱”,那么“拆箱”是啥呢,很简单就是吧包装类转换为基本数据类型的过程,在Java5之前“装箱”、“拆箱”的过程都需要我们手动去实现,在Java之后我们不必再去理会这个过程我们只需编程如下的代码便能实现自动“装箱”与“拆箱”。

// 手动 
// 装箱
Integer e = Integer.valueOf(1);
// 拆箱
int f = e.intValue();
		
// 自动
// 装箱
Integer g = 1;
// 拆箱
int h = g;

    相信读到这里机智的你应该明白了int a = 100与Integer b = 100做==比较为什么为true了,因为Integer与int进行比较时为提高运算效率会自动拆箱成基本数据类型之后再进行比较,所以结果为true。你又说基本数据类型效率高到底是不是啊,这里我可以很明确的告诉你基本数据类型效率就行高。不信请看如下实验。

public static void main(String[] args) {

	long time1 = new Date().getTime();
	for (Integer a = 0; a < Integer.MAX_VALUE; a++) {
			
	}
	long time2 = new Date().getTime();
	System.out.println("Integer所用时间:" + (time2 - time1));
		
	
	long time3 = new Date().getTime();
	for (int b = 0; b < Integer.MAX_VALUE; b++) {
			
	}
	long time4 = new Date().getTime();
	System.out.println("int所用时间:" + (time4 - time3));
		
		
}

    输出结果如下:

Integer所用时间:5880
int所用时间:3

    通过上述实验我们会相信两者的速度更不就不是一个量级,因为Integer自动拆箱的过程其实是非常耗时的,所以呢我们在编程过程中,应该尽可能的去避免用Integer进行++、--等操作特别是在循环中使用时。

    下面我们接着分析Integer c = 100与Integer d = 100的比较,我们对源代码生产的class文件进行反编译得到如下代码:

public static void main(String[] args)
  {
    int a = 100;
    Integer b = Integer.valueOf(100);
    System.out.println(a == b.intValue());
    
    Integer c = Integer.valueOf(100);
    Integer d = Integer.valueOf(100);
    System.out.println(c == d);
    
    c = Integer.valueOf(200);
    d = Integer.valueOf(200);
    System.out.println(c == d);
  }

    我们会发现我们对c、d进行赋值时其实都调用调用了Integer.valueOf()这个自动装箱的方法,上面我们已经提到过Integer.valueOf()这个装箱方法有缓存区间的存在,而100属于[-128~127]这个区间范围内,调用Integer.valueOf()返回的是同一个对象所以c==d为true。

    同理因为200已经不属于Integer的缓存区间范围,所以后面的c==d返回false。

    基本数据类型及其包装类的回顾差不多就到此为止了,其实剩下的byte、short等都与之类似,我们只要看看源码敲一敲试一试都会明白这些问题。故事未完后文请听下回分解-“重走Java路之String你真的了解吗?”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值