Java包装类及自动装箱、拆箱

Java包装类

基本类型 大小 包装器类型
boolean / Boolean
char 16bit Character
byte 8bit Byte
short /16bit Short
int 32bit Integer
long 64bit Long
float 32bit Float
double 64bit Double
void / Void

Java 的包装类有两个主要的目的:

  • Java包装类将基本数据类型的值“包装”到对象中,对基本数据类型的操作变为了对对象进行操作,从而使基本值能够包含在为对象为保留的操作中。比如向Collections中添加元素(泛型的操作限制加入的只能是对象,比如List = new ArrayList() 的写法是错误的),或者从带对象返回值的方法中返回。
  • 更加方便类型的转换,如常见的Integer向字符的转换

装箱和拆箱

Java 在SE5之后提供了自动的装箱和拆箱机制。基本数据类型可以和与其对应的包装类之间自动进行转换
如:

Integer i = 10;
int index = i;

装箱就是自动将基本数据类型转换为包装器类型
拆箱就是自动将包装器类型装换为基本数据类型

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

其他的也类似,比如Double、Character,不相信的朋友可以自己手动尝试一下。

因此可以用一句话总结装箱和拆箱的实现过程:

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

面试问题

下面这段代码的输出结果是什么?

public class Main {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
      
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

注意 ==和equals的区别:

类型 == equals
基本数据类型 不可用
包装类 地址 内容

输出的结果为:

true
false

为什么会出现这样的结果?输出结果表明 i1 和 i2 指向的是同一个地址,而 i3 和 i4 指向的是不同的地址。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }

从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在 [-128,127] 之间,便返回指向IntegerCache.cache(常量池)中已经存在的引用;否则创建一个新的Integer对象。

上面的代码中 i1 和 i2 的数值为100,因此会直接从cache(常量池)中取已经存在的对象,所以 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不同的对象。

推荐阅读!:更多关于基本数据类型在内存中存储过程底层的知识请参考另一篇文章:
[Java基础]从Java的各种基本数据类型看Java内存区域划分

  • 下面这段代码的输出结果是什么?
public class Main {
    public static void main(String[] args) {
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
        
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

也许有的朋友会认为跟上面一道题目的输出结果相同,但是事实上却不是。实际输出结果为:

    false
    false

至于具体为什么,读者可以去查看Double类的valueOf的实现。

在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的,因为他们都实现了常量池技术,Double、Float的valueOf方法的实现是类似的,并没有实现常量池技术。
Q:那么为什么这里要将值存储在常量池中呢?
A:速度更快,常量池引入的目的是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。这是一种 享元模式 的实现。

你可能感兴趣的:(Java基础)