从零开始学java(三十四)--包装类与包装类的缓存问题

包装类的基本知识

Java是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到Object[]数组或集合中的操作等等。

为了解决这个不足,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。

八个包装类的使用方法都十分相似,初学主要掌握其中一种。

包装类均位于java.lang包,八种包装类和基本数据类型的对应关系如下表所示:

在这里插入图片描述

package 常用类.Test;

import java.lang.Integer;


/**
 * 测试包装类
 * @author 与猫子
 *
 */

//以这一段程序为例,我们可以继续学习其他包装类的使用


public class TestWrappedClass {

	public static void main(String[] args) {
		//基本数据类型转换成包装类对象
		Integer a = new Integer(10);
		//System.out.println(a);
		
		Integer b = Integer.valueOf(30);//会出现缓存问题
		
		//把包装类对象转成基本数据类型
		int c = b.intValue();
		System.out.println(c);
		
		double d_double = b.doubleValue();
		System.out.println(d_double);
		
		//把字符串转成包装类对象
		//Integer eInteger = new Integer("9999dash");
		//"9999dash"出现报错.NumberFormatException
		Integer eInteger = new Integer("9999");
		System.out.println(eInteger);
	//	Integer fInteger = Integer.parseInt("99988sss");
		//"99988sss"同上
		Integer fInteger = Integer.parseInt("99988");
		System.out.println(fInteger);
		
//		Integer hello = Integer.parseInt("hello");//报错,不能出现字母
//		System.out.println(hello);
		
		//把包装类对象转成字符串
		String str = fInteger.toString();
		System.out.println(str);
		
		//常见的常量
		System.out.println("Int的最大值"+Integer.MAX_VALUE);
		System.out.println("Int的最小值"+Integer.MIN_VALUE);
	}
}

包装类自动装箱和拆箱

自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。JDK1.5后,Java引入了自动装箱(autoboxing)/拆箱(unboxing)。

自动装箱

基本类型的数据处于需要对象的环境中时,会自动转为“对象”。

我们以Integer为例:在JDK1.5以前,这样的代码 Integer i = 5 是错误的,必须要通过Integer i = new Integer(5) 这样的语句来实现基本数据类型转换成包装类的过程;而在JDK1.5以后,Java提供了自动装箱的功能,因此只需Integer i = 5这样的语句就能实现基本数据类型转换成包装类,这是因为JVM为我们执行了Integer i = Integer.valueOf(5)这样的操作,这就是Java的自动装箱。

自动拆箱

每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。

如 Integer i = 5;int j = i; 这样的过程就是自动拆箱。

我们可以用一句话总结自动装箱/拆箱:

自动装箱过程是通过调用包装类的valueOf()方法实现的,而自动拆箱过程是通过调用包装类的 xxxValue()方法实现的(xxx代表对应的基本数据类型,如intValue()、doubleValue()等)。

自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作,如下代码所示。

Integer i = 100;//自动装箱
//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);
//调用的是valueOf(100),而不是new Integer(100)
//=====================================================================
//=====================================================================
Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();

所以自动装箱与拆箱的功能是所谓的“编译器蜜糖(Compiler Sugar)”,虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。如以下代码:

public class Test1 {
    public static void main(String[] args) {
        Integer i = null;
        int j = i;
    }
}

执行结果如下:
在这里插入图片描述
会出现错误的原因是,以上代码相当于如下:

public class Test1 {
    public static void main(String[] args) {
        //上一个示例的代码在编译时期是合法的,
        //但是在运行时期会有错误,因为其相当于:
        Integer i = null; 
        int j = i.intValue();         
    }
}

null表示i没有指向任何对象的实体,但作为对象名称是合法的(不管这个对象名称存是否指向了某个对象的实体)。由于实际上i并没有指向任何对象的实体,所以也就不可能操作intValue()方法,这样上面的写法在运行时就会出现NullPointerException错误。

测试自动装箱与自动拆箱

public class Test2 {
    /**
     * 测试自动装箱和拆箱 结论:虽然很方便,但是如果不熟悉特殊情况,可能会出错!
     */
    public static void main(String[] args) {
        Integer b = 23; // 自动装箱
        int a = new Integer(20); //自动拆箱
        // 下面的问题我们需要注意:
        Integer c = null;
        int d = c; // 此处其实就是:c.intValue(),因此抛空指针异常。
    }
}

包装类的缓存问题

整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。

缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
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);
}**
  1. IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。

  2. 一般情况下 IntegerCache.low为-128,IntegerCache.high为127,IntegerCache.cache为内部类的一个静态属性。

IntegerCache类相关源码

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() {}
}

测试代码

public class Test3 {
    public static void main(String[] args) {

		//缓存[-128,127]实际就是 在Integer类初始化加载时就创建的数组,
		//保存在cache[]数组中
		//当我们调用valueOf()方法时,如果在[-128,127]之间,
		//就直接从缓存中,即cache[]中获取已经建好的对象
		//如果不在,则新建Integer对象

        Integer in1 = -128;//等价于Integr in1 = Integer.valueOf(-128)
        Integer in2 = -128;
        System.out.println(in1 == in2);//true 因为123在缓存范围内
        //-128保存在cache数组中,所以in1和in2指向的是cache[]缓存数组对象
        System.out.println(in1.equals(in2));//true
        Integer in3 = 1234;
        Integer in4 = 1234;
        System.out.println(in3 == in4);//false 因为1234不在缓存范围内
        //1234不在缓存,in3和in4的值相同,但是in3和in4是两个不同的对象
        System.out.println(in3.equals(in4));//true
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值