java 核心技术-12版 卷Ⅰ- 5.4 对象包装器与自动装箱

createh51个月前 (04-05)技术教程7

原文

5.4 对象包装器与自动装箱

有时,需要将 int 这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类,例如,Integer类对应基本类型 int。通常,这些类称为包装器 (wrapper)。这些包装器类有显而易见的名字: Integer、Long、Float、Double、Short、Byte、Character 和 Boolean (前6个类派生于公共超类 Number)。包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,包装器类还是 final,因此不能派生它们的子类。

假设想要定义一个整型数组列表。遗憾的是,尖括号中的类型参数不允许是基本类型也就是说,不允许写成ArrayList。这里就可以用到 Integer 包装器类。我们可以声明一个Integer 对象的数组列表。

var list = new ArrayList();

警告:由于每个值分别包装在一个对象中,所以ArrayList 的效率远远低于int[] 数组。因此,只有当程序员操作的方便性比执行效率更重要的时候,才会考虑对较小的集合使用这种构造。

幸运的是,有一个很有用的特性,从而可以很容易地向 ArrayList 添加 int 类型的元素。下面这个调用

list.add(3);

将自动地转换成

list.add(Integer.valueOf(3));

这种转换称为自动装箱(autoboxing)。

注释:你可能认为自动包装(autowrapping) 与包装器更一致,不过“装箱”(boxing)这个词源于 C#。

反过来,当将一个 Integer 对象赋给一个 int 值时,将会自动拆箱(unboxed)。也就是说,编译器将以下语句

int n = list.get(i);

转换成

int n = list.get(i).intValue();

自动装箱和自动拆箱甚至也适用于算术表达式。例如,可以将自增运算符应用于一个包装器引用:

Integer n = 3;

n++;

编译器将自动地插人指令对对象拆箱,然后将结果值增 1,最后再将其装箱。

大多数情况下容易有一种假象,认为基本类型与它们的对象包装器是一样的。但它们有一

点有很大不同:同一性。大家知道,== 运算符可以应用于包装器对象,不过检测的是对象是否有相同的内存位置,因此,下面的比较可能会失败:

Integer a = 1000;Integer b = 1000;if(a=b).

不过,Java 实现可以(如果选择这么做) 将经常出现的值包装到系统的对象中,这样一来,以上比较就可能成功。但这种不确定性并不是我们想要的。解决这个问题的办法是在比较两个包装器对象时调用equals 方法。

注释:自动装箱规范要求boolean、byte、char(≤ 127),介于-128和127之间的 short和int 包装到固定的对象中。例如,在前面的例子中,如果将a和b初始化为 109,那么它们的比较结果一定会成功。

提示:绝对不要依赖包装器对象的同一性。不要用== 比较包装器对象,也不要将包装器对象作为锁(参见第 12章)。

不要使用包装器类构造器,它们已被弃用,并将被完全删除。例如,可以使用 Integer.value0f(1000),而绝对不要使用 new Integer(1000)。或者,可以依赖自动装箱: Integer a=1000。

关于自动装箱还有几点需要说明。首先,由于包装器类引用可以为 null,所以自动装箱有可能会抛出一个NullPointerException 异常:

Integer n = null;

System.out.printIn(2 * n); // throws NullPointerException


另外,如果在一个条件表达式中混合使用 Integer 和 Double类型,则Integer 值就会拆箱提升为 double,再装箱为 Double:

Integer n = 1 ;

Double x = 2.0;

System.out.println(true ? n : x); // prints 1.0

最后强调一下,装箱和拆箱是编译器要做的工作,而不是虚拟机。编译器生成类的字节码时会插入必要的方法调用。虚拟机只是执行这些字节码。


注释: Java 将来的版本可能允许类似基本类型的用户自定义类型,其值并不存储在对象中。例如,基本类型Point 的值(包含 double字段x和y) 只是内存中一个16字节的块,并且有两个相邻的 double 值。可以复制这个值,但不能有它的引用。

如果需要一个引用,可以使用一个自动生成的伴随类(在当前提案中,这个类名为 Point.ref)。装箱和拆箱是自动的,这与当前的基本类型相同。

将来某个时候,基本包装器类将与那些类统一起来。例如,Double 将是 double.ref的一个别名。

使用数值包装器通常还有一个原因。Java 设计者发现,可以将某些基本方法放在包装器这会很方便,例如将一个数字字符串转换成数值

要想将字符串转换成整型,可以使用下面这条语句:

int x = Integer.parseInt(s);

这里与Integer 对象没有任何关系,parseInt 是一个静态方法。但 Integer 类是放置这个方法的一个好地方。API注释展示了 Integer 类中一些比较重要的方法。其他数值类也实现了相应的方法。

警告:有些人认为包装器类可以用来实现能修改数值参数的方法,不过这是错误的,在第4章中曾经讲到,由于 Java 方法的参数总是按值传递的,所以不可能编写一个能够让整型参数自增的 Java 方法。

public static void triple(int x) // won't work

{

x=3*x; // modifies local variable

}

将 int 替换成 Integer 能解决这个问题吗?

public static void triple(Integer x) // won't work

{...}

问题在于 Integer 对象是不可变的:包含在包装器中的信息不会改变。所以,不能使用这些包装器类来创建修改数值参数的方法。

API java.lang.Integer jdk1.0

  • int intValue() 将这个 Integer 对象的值作为一个int 返回(覆盖 Number 类中的 intValue 方法).
  • static String toString(int i) 返回一个新的 String 对象,表示指定数值 的十进制表示
  • static String toString(int i, int radix) 返回数值i的一个表示(采用 radix 参数指定的进制)。
  • static int parseInt(String s)
  • static int parseInt(String s, int radix) 返回一个整数,其数位包含在字符串 s 中。指定字符串必须表示一个十进制整数(第一种方法),或者采用 radix 参数指定的进制(第二种方法)。
  • static Integer value0f(String s)
  • static Integer value0f(String s, int radix) 返回一个新的 Integer 对象,初始化为一个整数,其数位包含在字符串 s 中。指定字符申必须表示一个十进制整数(第一种方法),或者采用 radix 参数指定的进制(第二种方法)

java.text.NumberFormat 1.1

  • Number parse(String s) 返回一个数值,假设给定的 String 表示一个数

相关文章

java 核心技术-12版 卷Ⅰ- 4.7.1 记录 record

原文4.7 记录有时,记录就只是数据,而面向对象程序设计提供的数据隐藏有些碍事。考虑一个类Point ,这个类描述平面上的一个点,有x和y 坐标。当然,可以如下创建一个类public class Po...

阿里大数据技术架构师整理分享java面试核心知识点框架篇文档

前言本文是对Java程序员面试中常见的微服务、网络编程、分布式存储和分布式计算等必备知识点的总结,包括Spring 原理及应用、Spring Cloud原理及应用、Netty网络编程原理及应用、Zoo...

java 核心技术-12版 卷Ⅰ- 5.10 继承的设计技巧

原文5.10 继承的设计技巧在本章的最后,我会给出使用继承时很有用的一些技巧。将公共操作和字段放在超类中正是因为这个原因,我们将姓名字段放在 Person 类中,而没有将它重复放在 Employee...

java 核心技术-12版 卷Ⅰ- 4.8.7 设置类路径

原文4.8.7 设置类路径最好使用 -classpath(或 -cp, 或者Java 9 中的 --calss-path) 选项指定类路径:java -classpath /home/user/cla...

Java核心知识 Zookeeper(二)角色

Zookeeper 集群是一个基于主从复制的高可用集群,每个服务器承担如下三种角色中的一种 Leader1. 一个 Zookeeper 集群同一时间只会有一个实际工作的 Leader,它会发起并维护与...

Java面试核心技能全景解析:架构设计与编码能力的深度碰撞

Java面试核心技能全景解析:架构设计与编码能力的深度碰撞——技术深水区的破局之道在2024年互联网技术迭代浪潮中,Java工程师的面试战场早已从基础语法跃迁至复杂系统设计维度。本文以实战视角拆解分布...