JAVA深化篇_03——包装类与自动拆装箱及缓存问题

本文介绍了Java中的包装类如何解决基本数据类型与对象间的转化问题,包括Integer等包装类的用法、自动装箱/拆箱机制以及-128~127范围内的缓存优化。
摘要由CSDN通过智能技术生成

基本数据类型的包装类

我们前面学习的八种基本数据类型并不是对象,为了将基本类型数据和对象之间实现互相转化,Java为每一个基本数据类型提供了相应的包装类。

包装类基本知识

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

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

包装类位于java.lang包,八种包装类和基本数据类型的对应关系:

基本数据类型对应包装类
基本数据类型包装类
byteByte
booleanBoolean
shortShort
charCharacter
int                    Integer
longLong
floatFloat
doubleDouble

在这八个类名中,除了IntegerCharacter类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写而已。

Number类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number类提供了抽象方法:intValue()longValue()floatValue()doubleValue(),意味着所有的“数字型”包装类都可以互相转型。

下面我们通过一个简单的示例认识一下包装类。

【示例】初识包装类

 public class WrapperClassTest {
     public static void main(String[ ] args) {
         Integer i = new Integer(10);  //从java9开始被废弃
         Integer j = Integer.valueOf(50); //官方推荐
     }
 }
 ​

包装类的用途

对于包装类来说,这些类的用途主要包含两种:

  1. 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[ ]、集合等的操作。

  2. 包含每种基本数据类型的相关属性如最大值、最小值等以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)。

【示例】包装类的使用

 public class Test {
     /** 测试Integer的用法,其他包装类与Integer类似 */
     void testInteger() {
         // 基本类型转化成Integer对象
         Integer int1 = new Integer(10);  //已废弃,不推荐使用
         Integer int2 = Integer.valueOf(20); // 官方推荐这种写法
         // Integer对象转化成int
         int a = int1.intValue();
         // 字符串转化成Integer对象
         Integer int3 = Integer.parseInt("334");
         Integer int4 = new Integer("999");
         // Integer对象转化成字符串
         String str1 = int3.toString();
     // 一些常见int类型相关的常量
         System.out.println("int能表示的最大整数:" + Integer.MAX_VALUE); 
     }
     public static void main(String[ ] args) {
         Test test = new Test();
         test.testInteger();
     }
 }

 ​执行结果如图所示:

自动装箱和拆箱

自动装箱(autoboxing)和拆箱(unboxing):将基本数据类型和包装类自动转换。

自动装箱:

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

我们以Integer为例:

 Integer i = 5

编译器会自动转成:Integer i = Integer.valueOf(5),这就是Java的自动装箱。

自动拆箱:

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

Integer i = Integer.valueOf(5);
 int j = i;

编译器会自动转成:int j = i.intValue();

这样的过程就是自动拆箱。

自动装箱/拆箱的本质是:

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

【示例】自动装箱

 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) {
         /*示例8-5的代码在编译时期是合法的,但是在运行时期会有错误因为其相当于下面两行代码*/
         Integer i = null; 
         int j = i.intValue();        
     }
 }
 ​

包装类的缓存问题

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

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

下面我们以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解,如示例所示。

【示例】Integer类相关源码

 public static Integer valueOf(int i) {
   if (i >= IntegerCache.low && i <= IntegerCache.high)
     return IntegerCache.cache[i + (-IntegerCache.low)];
   return new Integer(i);
 }
  1. 为Integer类的一个静态内部类,仅供Integer类使用。

  2. 一般情况下IntegerCache.low为-128IntegerCache.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() {}
 }

 ​由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会在类加载时完成。

下面我们做一下代码测试,如示例所示。

【示例8-9】包装类的缓存测试

 public class Test3 {
     public static void main(String[ ] args) {
         Integer in1 = -123;
         Integer in2 = -123;
         System.out.println(in1 == in2);//true 因为123在缓存范围内
         System.out.println(in1.equals(in2));//true
         Integer in3 = 1234;
         Integer in4 = 1234;
         System.out.println(in3 == in4);//false 因为1234不在缓存范围内
         System.out.println(in3.equals(in4));//true
     }
 }

总结

1️⃣ 自动装箱调用的是valueOf()方法,而不是new Integer()方法。

2️⃣ 自动拆箱调用的xxxValue()方法。

3️⃣ 包装类在自动装箱时为了提高效率,对于-128~127之间的值会进行缓存处理。超过范围后,对象之间不能再使用==进行数值的比较,而是使用equals方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值