基本数据类型与包装类

  • 前言

   正如所有接触过Java 的程序员所知,Java 是一种面向对象的高级编程语言。但Java 中却存在8种数据类型,它们不但不支持面向对象的编程机制,也不具备面向对象的特性(不包含任何field和方法)。这8种数据类型分别为 byte, short, int, long, char, double, float, boolean, 它们被称之为Java 的8种基本数据类型。Java 提供这8种基本数据类型主要是为了照顾C++等程序员的编程习惯。

   Java 兼容这8种基本数据类型固然带来了一定的便利,例如可采用8种数据类型进行常规的数据处理。但在某些场景下,采用基本数据类型却带来了一定的约束,例如:当某个方法的形参只接受引用数据类型,但方法调用者只能提供基本数据类型的实参时,基本数据类型就显得无能为力了。为解决这一矛盾,Java 在1.5之后为每个基本数据类型引入了对应的包装类。具体如表1所示:

表1 8种基本数据类型与其包装类对比
基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character

   通过观察上表可以看出,除了charint 类型外,其他6种基本数据类型对应的包装类名称均为其首字母大写后的名称。

  • 自动拆装箱

   jdk1.5提供的包装类不仅弥补了基本数据类型在面向对象上的不足,而且还可与其对应的基本数据类型之间进行相互转换,这一功能称之为自动拆装箱。即可以将包装类直接赋值给其对应的基本数据类型,也可将一个基本数据类型的值直接赋值给其对应的包装类。如下两个例子在编译时均不会报错:

代码块1
Integer a=1; // 1
int b=a;// 2

   采用反编译工具JD-GUI获取代码块1对应的class码如下:

public class Test
{
  public static void main(String[] args) {
    int a = 1;
    Integer b = Integer.valueOf(a);
    int c = b.intValue();
  }
}

   基于此特性,可将基本数据类型与其对应的包装类直接进行关系比较。其原理为:首先将通过自动拆箱功能获取包装类封装的基本数据类型数据,然后再将两个基本数据类型数据进行比较。当然也可采用相同原理将两个包装类进行关系比较。具体实例如代码块2所示:

代码块2
public class Test
{
  public static void main(String[] args) {
    Integer a = Integer.valueOf(12);
    Integer c=Integer.valueof(14)
    boolean b = (a.intValue() > 13);
    boolean d=(a.intValue()>b.intValue())
  }
}

下面以int为例介绍自动拆装箱的原理:

  1. 自动装箱

   当将一个int类型的数据赋值给一个Integer类型的数据时(如代码块1中的第1行代码),会调用Integer类的静态方法valeueOf(int a)获取相应的包装类。源码如下代码块3所示:

代码块3
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

   注:关于代码块3中的cache将在Integer的特殊性中讲解.

  1. 自动拆箱

   当将一个Integer类型的数据赋值给一个int类型的变量时(如代码块1中的第2行代码),会调用Integer的实例方法intValue()来获取其封装了数据。源码如代码块4所示:

代码块4
 public int intValue() {
      return value;
 }
  • Integer的特殊性

   Java中对象的创建与回收是一个十分为消耗性能的过程。故为提高性能,Integer在内部采用缓存的方式保存了部分数据,从而避免了对象的创建重复创建。也正因为缓存的存在,导致将Integer对象与基本数据类型之间进行关系比较时会出现一些非常规现象,具体如下:

代码块5
Integer a=129;
int a1=129;
System.out.println(a==a1);// 1  输入结果:false
Integer b=12;
int b1=12;
System.out.println(b==b1);//输出结果为:true

   通过观察代码块3中的代码发现,当自动装箱的基本数据类型大小属于某个特定的范围时,虚拟机将直接从缓存中获取对应的包装类对象返回;当装箱对象超出该范围,虚拟机每次自动装箱时都会创建一个新的对象返回。故,代码块5中,语句1输出false说明129超出了该范围。下面通过观察缓存源码,分析具体的范围大小:

代码块6
public class Integer{
    ...
     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() {}
    }
    ....
}    
   

   通过观察上述源码可以发现,IntegerCacheInteger的静态内部类,其静态数组cache 就是用来存储一定范围内int类型数据对应的包装类。static 静态代码块中的代码则是对缓存数组cache 的存储的初始化过程。为便于理解,将其转化为流程图如图1所示:

图1 Integer内部缓存初始化图

  由图1可知,在Integer包装类的内部,保存有[-128,127]范围内int对应的包装类。其下限由代码种low=-128决定,上限由变量high=127决定。基本数数据类型a在缓存中的索引位置k如下:

k=a-(low)     if a∈[-128,127]
  • 总结

   本文首先分析了Java 保存8中基本数据类型的的原因及其存在的问题,并在此基础上引出包装类的概念。然后以intInteger之间的转换为例,介绍了自动拆箱和自动装箱的概念,并从源码层面分析了自动拆装箱的实现原理;最后从源码层面上分析了8个包装类中较为特殊的一个包装类:Integer,得出Integer缓存的具体范围,从而在实际应用中能够灵活应对代诸如码块4中遇到的问题。

猜你喜欢

转载自www.cnblogs.com/accumulating/p/11707919.html