Java基础之包装类及自动装/拆箱

众所周知,Java是一门面向对象的语言,其中有8中基本数据类型,但是这8中基本数据类型是不支持面向对象的编程思想的,基本数据类型也不具备"对象"的特征:没有成员变量、方法可以调用。

为了让基本数据类型也有对象的特征,就出现了包装类,可以理解为:把基本类型“包装”起来,使其具有对象的特征,即包装类。为其提供了相应属性和方法,丰富了基本类型的操作。

根据我的理解,通俗易懂的来说,包装类有三个作用:

  1.一个实现基本类型之间的转换

  2.便于函数传值

  3.在一些地方要用到Object的时候方便将基本数据类型装换。

包装类与原始类不同,在Java中,变量可以声明为数据类型(8种),而包装类则创建继承但隐藏原始数据类型的实例化对象和方法,而不是分配数据类型值的变量

同时包装类也不能理解为原始类,应该被理解为一个包装原始类型的类。包装类可以用来存储与原始类型变量相同的值,但是包装类本身的实例/对象本身是非原始的。我们不能说包装类本身是原始类型。他们只是包装原始类型。

为什么包装/封装原始类呢?

广义上,对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。

使用封装有如下好处:

1、良好的封装能够减少耦合。

2、类内部的结构可以自由修改。

3、可以对成员进行更精确的控制。

4、隐藏信息,实现细节。

狭义上举个栗子:

int 是基本数据类型(面向过程留下的痕迹,不过是对java的有益补充),Integer 是一个类,是int的扩展,定义了很多的转换方法。

类似的还有:float Float;double Double;string String等,而且还提供了处理 int 类型时非常有用的其他一些常量和方法

举个例子:当需要往ArrayList,HashMap中放东西时,像int,double这种内建类型是放不进去的,因为容器都是装Object的,这是就需要这些内建类型的外覆类/包装类了。

Java中每种内建类型都有相应的外覆类/包装类。

Java中int和Integer关系是比较微妙的。关系如下:

  1、int是基本的数据类型;

  2、Integer是int的封装类;

  3、int和Integer都可以表示某一个数值;

  4、int和Integer不能够互用,因为他们两种不同的数据类型;

举例说明

  ArrayList al=new ArrayList();

  int n=40;

  Integer nI=new Integer(n);

  al.add(n); //不可以

  al.add(nI); //可以

并且泛型定义时也不支持int: 如:

List<int> list = new ArrayList<int>(); //不可以

List<Integer> list = new ArrayList<Integer>(); //可以

总而言之:如果我们定义一个int类型的数,只是用来进行一些加减乘除的运算or作为参数进行传递,那么就可以直接声明为int基本数据类型,但如果要像对象一样来进行处理,那么就要用Integer来声明一个对象,因为java是面向对象的语言,因此当声明为对象时能够提供很多对象间转换的方式,与一些常用的方法。自认为java作为一们面向对象的语言,我们在声明一个变量时最好声明为对象格式,这样更有利于你对面向对象的理解。

具体数据类型及对应的包装类如下:

基本数据包装类
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean

除了int和char有点例外之外,其他基本数据类型对应的包装类都是将其首字母大写即可。

在JDK1.5以前,把基本数据类型变量变成包装类需要通过对应包装类的构造器来实现。

但是从JDK1.5之后这种繁琐就消除了,JDK1.5提供自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能

所谓自动装箱,就是可以把一个基本类型变量直接赋给对应包装类变量,或者赋给Object变量(Object是所有类的父类,子类可以直接赋给父类变量);自动拆箱则相反,允许把包装类对象直接赋给一个对应的仅类型变变量

那我们来分析Integer i = 5;的过程;

在jdk1.5以前,这样的代码是错误的,必须要通过Integer i = new Integer(5);这样的语句实现;而在jdk1.5以后,Java提供了自动装箱的功能,只需Integer i = 5;这样的语句就能实现基本数据类型传给其包装类,JVM为我们执行了Integer i = Integer.valueOf(5);这就是Java的自动装箱。

相对应的,把基本数据从对应包装类中取出的过程就是拆箱;如

Integer i = 5;

int j = i;//这样的过程就是自动拆箱

本人写的小示例:

以Integer为例:查看Java系统中java.lang.Integer类的源代码

系统把一个-128~127之间的整数自动装箱成Integer实例,并放入了一个名为cache的数组中缓存起来。如果以后把一个-128~127之间的整数自动装箱成一个Integer实例时,实例实际直接指向对应的数组元素,因此-128~127之间的同一个整数自动装箱成Integer实例时,永远是引用cache数组的同一个数组元素,所以它们全部都相等;但每次把一个不再-128~127范围内的整数自动装箱成Integer实例时,系统总是创建一个Integer实例,所以出现程序中的运行结果。

 更多的就不一一展示了,大家可以自己查看相关源码。

还有一个自己测试的小例子,大家可以先不看控制台,自己脑海里运行运行一下

 

需要注意的是:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。

解释: 第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

***********************

心得之谈:欢迎指正,一起学习。

***********************

原文地址:https://www.cnblogs.com/donsenChen/p/8886798.html