第16章-包装类
概述
在Java世界中,一切皆为对象
Java中的数据类型分类有
基本数据类型,共有8种,运算效率、存储效率非常高
引用数据类型,有Java体系中提供的,如String、Scanner等;也可以自定义,如Student
引入包装类的原因
- 8种基本数据类型并不符合Java语言面向对象的特性
- 包装类能提供更多的功能;如前面使用过的Integer包装类的输出二进制字符串方法、字符串转基本数据类型等
- 有些场景,属性的值不确定,基本数据类型就不合适了,因为都有默认值;此时,使用包装类,并赋值null,就能满足此类需要;如年龄属性,使用int,默认为0就不一定合适
- 有了包装类后,有些工具类,就不支持基本数据类型了,而只支持包装类;如集合类中的ArrayList等
基本数据类型与包装类的差异
- 存储位置不同,基本数据类型变量值直接存放在栈中,而包装类对象存放在堆中
- 默认值不同,基本数据类型的成员变量默认值为相应类型的默认值,包装类默认值为null
- 运算效率不同,基本数据类型的运算效率更高,包装类相对要低一些
- ==比较方式不同,基本数据类型直接比较,包装类,要使用其xxxValue方法转为基本数据类型再比较
基本数据类型与包装类对应关系,如下图:
基本数据类型与包装类对应关系 其他特点
所有的包装类都实现了Serializable接口,表示可序列化
所有的包装类都实现了Comparable接口,并覆盖了其中的comparTo方法,表示可用于两两比较,直接应用于排序工具中实现排序
Short、Byte、Integer、Long、Float、Double都继承了Number抽象类,该类主要提供一些转基本数据类型的抽象方法
装箱和拆箱
在实际的开发过程中,由于基本数据类型和包装类同时存在,会经常进行基本数据类型和包装类的相互转换,转换过程称之为装箱和拆箱
频繁的装箱和拆箱,会带来性能上的损失
从JDK1.5后,基本已经完成了自动装箱和自动拆箱,自动装箱是通过valueOf方法实现,自动拆箱是通过xxxValue方法实现
装箱:基本数据类型转包装类
//方式1、使用new创建包装类对象,自动装箱
Integer integerReference1 = new Integer(20);
//方式2、使用字面量创建包装类对象,自动装箱
Integer integerReference2 = 20;
//方式3、使用valueOf创建包装类对象,自动装箱
Integer integerReference3 = Integer.valueOf(20);
Integer integerReference4 = Integer.valueOf("20"); //方法同上,但字符串中必须为数值拆箱:包装类变成基本数据类型
//定义包装类
Integer integerReference1 = new Integer(20);
//方式1、使用包装类对象直接赋值,自动拆箱
int intVariable1 = integerReference1;
//方式2、使用包装类对象的xxxValue方法,自动拆箱
int intVariable2 = integerReference1.intValue();
//方式3、包装类与字面量、基本数据类型进行运算,也会进行自动拆箱后,进行计算
integerReference1 = integerReference1 + 30; //会先拆箱,再装箱
基本原则
- 基本数据类型和包装类进行比较运算,会先将包装类进行拆箱成基本数据类型,然后进行比较
- 两个包装类之间的运算,会被自动拆箱成基本类型进行计算
- 基本数据类型放入集合类中的时候,会进行自动装箱
- 三元运算符的使用过程中。两个表达式分别为基本数据类型和包装类时,会将包装类拆箱为基本数据类型,然后进行操作
如何选择?
问题:什么时候使用基本数据类型?什么时候使用包装类?
- 成员变量使用包装类
- 方法中形式参数、返回值推荐使用包装类
- 局部变量使用基本数据类型
常用静态常量、静态方法和普通方法(主要以Integer包装类为例)
静态常量
- Integer.MAX_VALUE,最大值
- Integer.MIN_VALUE,最小值
- Integer.SIZE,二进制位数
- Integer.BYTES,字节数
静态方法
- Integer.paseInt,字符串转int
- Integer.valueOf,字符串转Integer
- Integer.compare,比较两个int整数大小 , 返回1表示参数1大于参数2,0表示参数1等于参数2,1表示参数1小于参数2
- Integer.max,获取两个int整数的最大值
- Integer.min,获取两个int整数的最小值
- Integer.sum,获取两个int整数的汇总值
- Integer.toBinaryString,int整数转二进制字符串
- Integer.toOctalString,int整数转八进制字符串
- Integer.toHexString,int整数转十六进制字符串
普通方法
- compareTo,当前Integer对象跟另一个Integer对象比较
- xxxValue,包装类转指定基本数据类型
应用实例
package com.bjpowernode.demo;
/**
* 包装类示例
*/
public class WrapperDemo {
public static void main(String[] args) {
System.out.println("--------------Integer及其他包装类中的静态常量--------------");
System.out.println("-----Integer-----");
System.out.println(Integer.MIN_VALUE);
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.SIZE);
System.out.println(Integer.BYTES);
System.out.println("-----Long-----");
System.out.println(Long.MIN_VALUE);
System.out.println(Long.MAX_VALUE);
System.out.println(Long.SIZE);
System.out.println(Long.BYTES);
System.out.println("-----Float-----");
System.out.println(Float.MIN_VALUE);
System.out.println(Float.MAX_VALUE);
System.out.println(Float.SIZE);
System.out.println(Float.BYTES);
System.out.println("--------------字符串转int--------------");
String str1 = "10086";
int intVariable = Integer.parseInt(str1);
System.out.println(intVariable);
System.out.println("--------------字符串转包装类--------------");
String str2 = "10086";
Integer integerVariable = Integer.valueOf(str2);
System.out.println(integerVariable);
System.out.println("--------------比较两个int整数大小--------------");
System.out.println(Integer.compare(15, 20));
System.out.println(Integer.compare(20, 15));
System.out.println(Integer.compare(100, 100));
System.out.println("--------------获取两个int整数的最大值、最小值、汇总值--------------");
System.out.println(Integer.max(15,20));
System.out.println(Integer.min(15,20));
System.out.println(Integer.sum(15,20));
System.out.println("--------------int整数二进制、八进制、十六进制字符串--------------");
System.out.println(Integer.toBinaryString(31));
System.out.println(Integer.toOctalString(31));
System.out.println(Integer.toHexString(31));
System.out.println("--------------当前Integer对象跟另一个Integer对象比较,实现Comparable接口的compareTo方法--------------");
Integer first = Integer.valueOf(15);
Integer second = Integer.valueOf(20);
System.out.println(first.compareTo(second));
System.out.println(second.compareTo(first));
}
}
缓存机制
\==比较问题,包装类进行比较时,不要直接使用==比较,有可能会返回false,如下面两个代码段,会出现不同的返回
代码段1,返回true,由于比较的数据在-128~127之间,使用常量池中的值直接比较
//进行比较的两个变量定义,并赋初始值
Integer first = 127;
Integer second = 127;
//使用==比较
System.out.println(first == second); //返回true,由于比较的数据在-128~127之间,使用内部缓存代码段2,返回false,由于比较的数据不在-128~127之间,不使用常量池中的值进行比较,比较的是堆中地址
//进行比较的两个变量定义,并赋初始值
Integer first = 128;
Integer second = 128;
//使用==比较
System.out.println(first == second); //返回false,由于比较的数据不在-128~127之间,不使用内部缓存,比较的是堆中地址代码段3,此时,需要使用.xxxValue()方法,转换成基本数据类型进行比较,才能进行非-128~127之外的值的比较;上述代码段2修改成如下代码后,符合比较逻辑;故,建议所有的包装类比较都转换成基本数据类型进行
//进行比较的两个变量定义,并赋初始值
Integer first = 128;
Integer second = 128;
//使用==比较
System.out.println(first.intValue() == second.intValue());
问题根源
除了Float、Double类型以外,其他6种包装类都存在缓存机制,缓存一定范围的值(具体范围见下图“缓存范围”),以提高性能与效率
使用valueOf装箱构造包装类对象时,如果是缓存范围的值,会直接从缓存内部类IntegerCache的cache[]数组中获取包装类对象;注意,使用new生成包装类对象没有此效果
比较时,如果是缓存范围的值,都指向是缓存内部类IntegerCache的cache[]数组中相同的对象,会相等;如果不是缓存范围的值,则会在堆中创建一两个新的对象,地址不同,会不相等
【练习】
- 练习应用实例的内容,完成代码编写
- 编写程序,定义一个MyOperation类,里面包含多个operate重载方法,用于进行两个包装类型的操作,返回相同的包装类型,分别尝试任意3种包装类
BigInteger【扩展】
用于解决需要进行超过long的长度的变量定义或运算,将会产生的数据失真问题
定义方式
方式1,使用字符串初始化,字符串中,就可支持超过long最大值的数字字符串;注意字符串中不能有非数字的字符,否则会产生异常
BigInteger bigInteger = new BigInteger("9223372036854775808"); //long类型最大值为9223372036854775807
方式2,使用字面量初始化
BigInteger bigInteger = BigInteger.valueOf(9223372036854775807L); //long类型最大值为9223372036854775807
常用接口,由于数据太大,使用运算符进行运算会产生数据失真,BigInteger提供了一系列的方法进行运算来保证数据不失真,常见的方法有
- add:加
- subtract:减
- multiply:乘
- divide:除,整除
- remainder:取余
- pow:次方
- byteValue:返回byte基本数据类型值
- shortValue:返回short基本数据类型值
- intValue:返回int基本数据类型值
- longValue:返回long基本数据类型值
- ...
应用实例
实例1,常见方法使用
实例代码
package com.bjpowernode.demo;
import java.math.BigInteger;
/**
* BigInteger示例
*/
public class BigIntegerDemo {
public static void main(String[] args) {
//定义变量
BigInteger first = BigInteger.valueOf(9223372036854775807L); //long类型最大值为9223372036854775807
BigInteger second = BigInteger.valueOf(9223372036854775807L); //long类型最大值为9223372036854775807
BigInteger result;
//加法
result = first.add(second);
System.out.println("加法计算结果:" + result);
//减法
result = first.subtract(second);
System.out.println("减法计算结果:" + result);
//乘法
result = first.multiply(second);
System.out.println("乘法计算结果:" + result);
//除法
result = first.divide(second);
System.out.println("除法计算结果:" + result);
//取余
result = first.remainder(second);
System.out.println("取余计算结果:" + result);
//次方
result = first.pow(5);
System.out.println("次方计算结果:" + result);
}
}输出结果,运行查看结果
实例2,从控制台接收输入BigInteger数据
实例代码
package com.bjpowernode.demo;
import java.math.BigInteger;
import java.util.Scanner;
/**
* BigInteger示例
*/
public class BigIntegerDemo {
public static void main(String[] args) {
//定义变量
BigInteger bigInteger;
//接收输入
System.out.print("请输入一个很大的数:");
Scanner scanner = new Scanner(System.in);
bigInteger = scanner.nextBigInteger();
//输出
System.out.println("输入的BigInteger值为:" + bigInteger);
}
}输出结果,运行查看结果
【练习】
- 练习应用实例内容,完成代码编写
BigDecimal【扩展】
用于解决精度超过double类型的16位时,精度丢失的问题
定义方式,有多种,但推荐使用字符串作为构造参数方式,不会有精度丢失
BigDecimal bigDecimal = new BigDecimal("0.1234567890123456789"); //超过16位小时,依然保持完整精度
常用接口,由于精度要求更高,原有的运算符进行运算会产生精度丢失,BigDecimal提供了一系列的方法进行运算来保证精度不丢失,常见的方法有
- add:加
- subtract:减
- multiply:乘
- divide:除,整除
- remainder:取余
- pow:次方
- floatValue:返回float基本数据类型值
- doubleValue:返回double基本数据类型值
- ...
应用实例
应用实例1,常见方法使用;注意,类似0E-4这种科学计数法,表示0.0000的结果
实例代码
package com.bjpowernode.demo;
import java.math.BigDecimal;
/**
* BigDecimal示例
*/
public class BigDecimalDemo {
public static void main(String[] args) {
//定义变量
BigDecimal first = new BigDecimal("0.123456790123456789"); //19位小数
BigDecimal second = new BigDecimal("0.123456790123456789"); //19位小数,也可尝试普通小数0.1
BigDecimal result;
//加法
result = first.add(second);
System.out.println("加法计算结果:" + result);
//减法
result = first.subtract(second);
System.out.println("减法计算结果:" + result);
//乘法
result = first.multiply(second);
System.out.println("乘法计算结果:" + result);
//除法
result = first.divide(second);
System.out.println("除法计算结果:" + result);
//取余
result = first.remainder(second);
System.out.println("取余计算结果:" + result);
//次方
result = first.pow(5);
System.out.println("次方计算结果:" + result);
}
}输出结果,运行查看结果
应用实例2,从控制台接收输入BigDecimal数据
实例代码
package com.bjpowernode.demo;
import java.math.BigDecimal;
import java.util.Scanner;
/**
* BigDecimal示例
*/
public class BigDecimalDemo {
public static void main(String[] args) {
//定义变量
BigDecimal bigDecimal;
//接收输入
System.out.print("请输入超过16位小数位的数:");
Scanner scanner = new Scanner(System.in);
bigDecimal = scanner.nextBigDecimal();
//输出
System.out.println("输入的BigDecimal值为:" + bigDecimal);
}
}输出结果,运行查看结果
【练习】
- 练习应用实例内容,完成代码编写
实战和作业
【扩展】编写程序,要求如下
- 定义一个产品类(Product),要求如下
- 具备产品编号(每个产品对象的产品编号都不相同)、产品名称、产品单价、库存数量等属性,属性都使用包装类
- 具有入库方法,形参为入库数量,能够增加产品数量,返回是否入库成功
- 具有出库方法,形参为出库数量,能够出库不超过库存数量的产品,返回是否出库成功
- 定义一个仓库类(StoreHouse),要求如下
- 具备仓库编号、仓库名称、产品列表属性,即Product[]
- 具备指定产品入库方法,形参为产品编号、入库数量,能够针对该产品编号的产品进行库存数量增加,返回入库结果:0表示成功、1表示产品编号不存在、2表示入库数量不能小于0
- 具备指定产品出库方法,形参为产品编号、出库数量,能够针对该产品编号的产品进行库存数量减少,返回出库结果:0表示成功、1表示产品编号不存在、2表示库存数量不足
- 定义一个仓库测试类(StoreHouseTest),进行业务测试,要求如下
- 定义一个仓库类对象,并初始化产品列表
- 测试仓库类入库方法,根据产品编号、入库数量,进行入库
- 测试仓库类出库方法,根据产品编号、出库数量,进行出库
- 汇总库存总数、库存总价值
注意:合理使用包装类、基本数据类型
- 定义一个产品类(Product),要求如下