一. 包装类介绍
Java 语言是一个面向对象的编程语言,但是 Java 中的基本数据类型却不是面向对象的,但是我们在实际使用中经常需要将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象,所以就出现了包装类。
基本数据类型有 8 个,对应的包装类也是 8 个,如果下图所示
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
二. 包装类的继承关系
三. 包装类的基本操作
8 个包装类都带有自己对应的构造方法,java 中的包装类提供了将原始数据类型转换为对象,以及将对象转换为原始数据类型的机制。这里举例说明下这个过程
3.1 基本数据类型 —> 包装类
import org.junit.Test;
/**
* 基本数据类型 ---> 包装类:调用包装类的构造器
* 这个过程称之为装箱:将基本类型转换成包装类型的过程叫做装箱
*/
class Order{
boolean isMale;
Boolean isFemale;
}
public class WrapperTest {
@Test
public void test1(){
Integer in1 = new Integer(10);
System.out.println(in1.toString()); // 10
Integer in2 = new Integer("123");
System.out.println(in2.toString()); // 123
Float f1 = new Float(12.3f);
Float f2 = new Float("12.3");
System.out.println(f1); // 12.3
System.out.println(f2); // 12.3
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
Boolean b3 = new Boolean("true123");
System.out.println(b3); // false
Order order = new Order();
System.out.println(order.isMale); // false
System.out.println(order.isFemale); // null
}
}
3.2 包装类 —> 基本数据类型
import org.junit.Test;
/**
* 包装类 ---> 基本数据类型:调用包装类的 xxxValue()
* 这个过程称之为拆箱:将包装类型转换成基本类型的过程叫做拆箱
*/
public class WrapperTest {
@Test
public void test2(){
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1 + 1); // 13
Float f1 = new Float(12.3);
float f2 = f1.floatValue();
System.out.println(f2); // 12.3
}
}
3.3 自动装箱与自动拆箱
在 JDK1.5 版本之后新增了 ,自动装箱与自动拆箱功能
import org.junit.Test;
/**
* 自动装箱与自动拆箱功能
*/
public class WrapperTest {
@Test
public void test3(){
// 自动装箱:基本数据类型 ---> 包装类
Integer in2 = 10;
Boolean b2 = true;
// 自动拆箱:包装类 ---> 基本数据类型
int num3 = in2;
}
}
3.4 基本数据类型、包装类 —> String 类
import org.junit.Test;
/**
* 基本数据类型、包装类 ---> String 类
*/
public class WrapperTest {
@Test
public void test4(){
int num1 = 10;
// 方式1:链接运算
String str1 = num1 + "";
// 方式2:调用 String 的 valueOf() 方法
float f1 = 12.3f;
String str2 = String.valueOf(f1);
System.out.println(str2); // "12.3"
Double d1 = 12.4;
String str3 = String.valueOf(d1);
System.out.println(str3); // "12.4"
}
}
3.5 String 类型 —> 基本数据类型、包装类
import org.junit.Test;
/**
* String 类型 ---> 基本数据类型、包装类
*/
public class WrapperTest {
@Test
public void test5(){
// String 类型 ---> 基本数据类型、包装类
String str1 = "123";
int num1 = Integer.parseInt(str1);
System.out.println(num1 + 1); // 124
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1); // true
}
}
3.6 包装类的缓存机制
import org.junit.Test;
/**
* 包装类的缓存机制
*/
public class WrapperTest {
@Test
public void test6(){
// Integer 内部定义了 IntegerCache 结构,IntegerCache 中定义了 static final Integer cache[];
// 保存了从 -128 ~ 127 范围的整数。如果我们使用自动装箱的方式给 Integer 赋值的范围在 -128 ~ 127 范围内时
// 可以直接使用数组中的元素,不同再去 new 了,目的:提高效率
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // false、引用类型,== 比较地址值,不是一个对象,所以 false
Integer m = 1;
Integer n = 1;
System.out.println(m == n); // true、自动装箱,这里涉及到的包装类的缓存机制,下面可以通过源码查看
Integer x = 128; // 出了 cache[] 范围,相当于 new 了一个 Integer 对象
Integer y = 128;
System.out.println(x == y); // false
}
}
静态的内部类是在整个 Integer 加载的时候就已经加载完成了,以下代码初始化了一个 Integer 类型的叫 cache 的数组,取值范围是[-128, 127]。缓存机制的作用就是提前实例化相应范围数值的包装类对象,只要创建处于缓存范围的对象,就使用已实例好的对象。从而避免重复创建多个相同的包装类对象,提高了使用效率。如果我们用的对象范围在[-128, 127]之内,就直接去静态区找对应的对象,如果用的对象的范围超过了这个范围,会帮我们创建一个新的 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() {}
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过分析源码我们可以发现,只有 double 和 float 的自动装箱代码没有使用缓存,每次都是 new 新的对象,其它的 6 种基本类型都使用了缓存策略。 使用缓存策略是因为,缓存的这些对象都是经常使用到的(如字符、-128至127之间的数字),防止每次自动装箱都创建一次对象的实例。
3.7 包装类和基本数据类型的区别
- 默认值不同
包装类的默认值是 null,而基本数据类型是对应的默认值(比如整型默认值是0,浮点型默认值是0.0)
- 存储区域不同
基本数据类型是把值保存在栈内存里,包装类是把对象放在堆中,然后通过对象的引用来调用他们
- 传递方式不同
基本数据类型变量空间里面存储的是值,传递的也是值,一个改变,另外一个不变,而包装类属于引用数据类型,变量空间存储的是地址(引用),传递的也是引用,一个变,另外一个跟着变。
3.8 操作总结
四. 练习题
4.1 练习题1
import org.junit.Test;
/**
* 练习题:以下两种情况,输入结果相同吗,各是什么?
* 解释:三元运算符后面的表达式1和表达式2必须是同种类型、编译时自动类型提升
*/
public class WrapperTest {
@Test
public void test7(){
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); // 1.0 编译时自动类型提升
Object o2;
if (true) {
o2 = new Integer(1);
}else {
o2 = new Double(2.0);
}
System.out.println(o2); // 1
}
}
4.2 练习题2
package com.base.learn2;
import java.util.Scanner;
import java.util.Vector;
/**
* @Author myf15609
* @Date 2022/12/10
*/
public class ScoreTest {
public static void main(String[] args) {
// 1. 实例化 Scanner 用于从键盘获取学生成绩
Scanner scanner = new Scanner(System.in);
// 2. 常见 Vector 对象
Vector v = new Vector();
// 3. 通过 for(;;) 或者 while(true) 的方式,给 Vector 中添加数据
int maxScore = 0;
for (; ; ) {
System.out.println("请输入学生成绩(以负数代表输入结束):");
int score = scanner.nextInt();
if (score < 0) break;
if (score > 100) {
System.out.println("输入的成绩有误,请重新输入");
continue;
}
// jdk 5.0 之前需要手动装箱
// Integer integer = new Integer(score);
// v.addElement(integer);
// jdk 5.0 之后会自动装箱,直接写即可
v.addElement(score);
// 4. 获取学生成绩的最大值
if (maxScore < score) maxScore = score;
}
// 5. 遍历 Vector 得到每个学生的成绩,并与最大值比较,得到学生成绩的等级
char level = 0;
for (int i = 0; i < v.size(); i++) {
Object obj = v.elementAt(i);
// jdk 5.0 之前需要手动拆箱
// Integer intScore = (Integer) obj; // 强转为包装类
// int score1 = intScore.intValue(); // 包装类转为基本数据类型
// jdk 5.0 之后自动拆箱
int score1 = (int) obj;
if (maxScore - score1 < 10) {
level = 'A';
} else if (maxScore - score1 < 20) {
level = 'B';
} else if (maxScore - score1 < 30) {
level = 'C';
} else if (maxScore - score1 < 40) {
level = 'D';
}
System.out.println("student - " + i + " score is " + score1 + ", level is " + level);
}
}
}