面试题总结——包装类的自动拆装箱以及"缓存值"问题(常量池)

面试题总结——包装类的自动拆装箱以及"缓存值"问题(常量池)

  Java包装类的自动拆装箱可参考:深入理解Java中的包装类与自动拆装箱。

  再多强调一下,在使用包装类的==与equals()时需要注意:

   算术运算会触发自动拆箱过程

    ①若此时再使用==比较的是两个数值的大小,两个数值之间可以进行Java允许的类型转换。
    ②若此时在使用equals()比较又会触发自动装箱过程,但不会发生类型转换(已成包装类)

   看看下面这个例子:

		public static void main(String[] args) {
	            Integer a = 1;
	            Integer b = 2;
	            Integer c = 3;
	            Integer d = 3;
	            Integer e = 321;
	            Integer f = 321;
	            Long g = 3L;
	            Long h = 2L;
	
	            System.out.println(c==d);
	            System.out.println(e==f);
	            System.out.println(c==(a+b));
	            System.out.println(c.equals(a+b));
	            System.out.println(g==(a+b));
	            System.out.println(g.equals(a+b));
	            System.out.println(g.equals(a+h));
	        }
	
		// 打印结果
			true
			false
			true
			true
			true
			false
			true

   解析一下这个例子,第1个与第2个由于"常量池"的存在就不多说(下面会详细讲解),

   主要说一下3,4,5,6,7。

    第3个,算数运算会触发自动拆箱过程,因此两个数值比较后返回true
    第4个,算数运算会触发自动拆箱过程,equals()方法又会触发自动装箱过程,因此两个Integer包装类比较的就是其值。
    第5个,算数运算会触发自动拆箱过程,因此两个数值比较后返回true(虽然一个是int一个是long,但是Java会自动类型转换,1==1L的结果为true)
    第6个,算数运算会触发自动拆箱过程,自动拆箱后的运算结果是一个int类型,而equals()方法又会触发自动装箱过程。但两个包装类的类型明显不同并且不能相互转换(类型不同,一个是Integer,一个是Long)
    第7个,算数运算会触发自动拆箱过程,自动拆箱后的运算结果是一个long类型(int+long),此时equals()方法再触发自动装箱过程,此时两个Long包装类比较的就是其值(类型已经相同)

                华丽的分割线

  本篇文章主要探讨一下在Java的各包装类中出现的"缓存值"问题,按照Java语言的特性,没new一个对象会在堆空间中创建一块内存空间用于存储该对象的内容,而对于包装类来说,Java将经常出现的值包装到同一个对象中,也就是所谓的"常量池",下次再访问时直接将该引用指向"常量池"中已存在的常量(请参考String中的"常量池"相关概念)

  首先看一下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);
    }

   在其源码中可以看到,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则才会创建一个新的Integer对象。因此对于Integer来说,相当于其常量池中存储的数据为[-128,127]

   正是由于Integer包装类所使用的"缓存值"技术,才出现下面代码的执行结果

	public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 1000;
        Integer i4 = 1000;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }

	// 打印结果为:
		true
		false

  举一个相反的例子,看一下Double包装类valueOf()方法的源码:

	 public static Double valueOf(double d) {
        return new Double(d);
    }

   很明显,对于Double包装类来说,根本不存在"常量池"这一说法,无论创建多大数值的Double对象,Double包装类都会创建一个新的Double对象

  对所有数值类(Number类的子类<除Boolean外都是>)的源码进行分析,得到对包装类及其"缓存值"(常量池)的一个总结。

   ●Byte,Short,Integer,Long;这 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。

   ●Character默认创建数值[0,127]的相应类型的缓存数据。

   ●两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

面试题总结——包装类的自动拆装箱以及

你可能感兴趣的:(面试题总结)