包装类Wrapper Classes
包装类是Java提供的一种类,用于将基本数据类型包装为对象。包装类提供了许多方法,可以方便地进行类型转换、比较、解析字符串等操作。Java 中的基本数据类型包括 int、boolean、char、byte、short、long、float 和 double,对应的包装类如下:
- Integer:对应 int
- Double:对应 double
- Float:对应 float
- Long:对应 long
- Short:对应 short
- Byte:对应 byte
- Character:对应 char
- Boolean:对应 boolean
常见方法如下:
//intValue():将Integer对象转换为int
Integer wrappedInt = 42;
int primitiveInt = wrappedInt.intValue(); // 42
//doubleValue():将Double对象转换为double
Double wrappedDouble = 3.14;
double primitiveDouble = wrappedDouble.doubleValue(); // 3.14
//compareTo():比较两个包装类对象的大小
Integer a = 10;
Integer b = 20;
int result = a.compareTo(b); // -1,表示 a < b
//equals():比较两个包装类对象的值是否相等
Integer x = 100;
Integer y = 100;
boolean isEqual = x.equals(y); // true
//parseInt(String s):将字符串解析为int
String str = "123";
int num = Integer.parseInt(str); // 123
//parseDouble(String s):将字符串解析为double
String strDouble = "45.67";
double dNum = Double.parseDouble(strDouble); // 45.67
//toString():将包装类对象转换为字符串表示
Integer num = 42;
String strNum = num.toString(); // "42"
//valueOf():将基本数据类型或字符串转换为对应的包装类对象
Integer wrappedFromString = Integer.valueOf("99"); // Integer对象,值为99
Double wrappedFromDouble = Double.valueOf(12.34); // Double对象,值为12.34
//MAX_VALUE和MIN_VALUE:常量,表示该类型的最大值和最小值
int maxInt = Integer.MAX_VALUE; // 2147483647
double minDouble = Double.MIN_VALUE; // 4.9E-324
为什么要用包装类?
虽然基本数据类型可以完成许多相同的操作,但使用包装类有一些显著的好处,尤其是在Java的面向对象特性和集合框架中。
- 面向对象特性
包装类是对象,可以作为方法参数或返回值传递,这使得它们能够利用Java的面向对象特性,例如多态和继承。 - 集合框架
Java的集合框架(如ArrayList, HashMap等)只能存储对象,而不能存储基本数据类型。使用包装类可以将基本数据类型作为对象存储在集合中。
List<Integer> list = new ArrayList<>();
list.add(10); // 自动装箱
- 不可变特性
包装类是不可变的,这意味着一旦创建,值无法更改。这有助于保证数据的一致性,特别是在多线程环境中。 - 处理空值
包装类可以为null,这在某些情况下是有用的。比如,可以用null来表示一个不存在的值,而基本数据类型无法表示null。
Integer nullableInt = null; // 合法
// int primitiveInt = null; // 不合法,会抛出编译错误
自动装箱Autoboxing
自动装箱是指Java编译器自动将基本数据类型转换为对应的包装类对象的过程。例如,当将int类型的变量赋值给Integer类型的对象时,编译器会自动进行装箱。
//int类型的num被自动转换为Integer类型的对象wrappedNum
int num = 5;
Integer wrappedNum = num; // 自动装箱
自动拆箱Unboxing
自动拆箱是指Java编译器自动将包装类对象转换为对应的基本数据类型的过程。例如,当将Integer类型的对象赋值给int类型的变量时,编译器会自动进行拆箱。
//wrappedNum被自动转换为int类型的变量num
Integer wrappedNum = 10; // 自动装箱
int num = wrappedNum; // 自动拆箱
包装类的不可变性Immutable
包装类在创建后,其值是不可变的。这意味着一旦创建了一个包装类对象,其内容(即所包装的基本数据类型的值)就无法改变。任何尝试修改包装类对象的操作都会生成一个新的对象。
//== 运算符用于比较两个对象的引用。如果两个引用指向同一个对象,== 返回 true;如果指向不同的对象,返回 false
//equals() 方法用于比较两个对象的内容是否相等。如果两个对象的值相同,则返回 true,否则返回 false
public class Main {
public static void main(String[] args) {
Integer num = 10; // 创建一个 Integer 对象,值为 10
// 记录 num 的引用
Integer referenceNum = num; // referenceNum 引用原始对象
// 自动装箱,num + 1 会创建一个新的 Integer 对象
num = num + 1; // num 现在指向一个新的 Integer 对象,值为 11
// 输出引用和内容的比较结果
System.out.println("referenceNum == num: " + (referenceNum == num)); // false
System.out.println("referenceNum.equals(num): " + referenceNum.equals(num)); // false
System.out.println("referenceNum: " + referenceNum); // 10
System.out.println("num: " + num); // 11
}
}
运行结果如下:
referenceNum == num: false
referenceNum.equals(num): false
referenceNum: 10
num: 11
String初始化Initialization
栈Stack
- 栈是用于存储基本数据类型(如int、char等)和对象引用的内存区域。
- 每当一个方法被调用时,Java会在栈上分配内存来存储局部变量和方法调用信息。
- 当方法执行结束时,栈上的内存会被自动释放。
堆Heap
- 堆是用于存储对象的内存区域,包括 String 对象和其他类的实例。
- 堆内存是动态分配的,大小不固定,可以在运行时分配和释放。
- 对象在堆上创建后,栈中会存储对该对象的引用。
初始化方式一:字面量初始化
当使用字符串字面量(例如 String str = “Hello”;)时,Java会首先检查字符串池(String Pool)中是否已存在该字符串:
如果字符串池中已存在 “Hello”,则 str 将引用该对象;
如果不存在,Java 会在堆中创建一个新的 String 对象,并将其添加到字符串池中。
String str1 = "Hello"; // 在字符串池中创建对象
String str2 = "Hello"; // 引用同一个对象
System.out.println(str1 == str2); // true,指向同一内存地址
初始化方式二:使用new关键字
如果你使用new关键字创建字符串(例如String str = new String(“Hello”);),Java会在堆中创建一个新的String对象,不管字符串池中是否已有相同内容的对象。
String str1 = new String("Hello"); // 在堆中创建新的对象
String str2 = new String("Hello"); // 又创建一个新的对象
System.out.println(str1 == str2); // false,指向不同的内存地址
==运算符及字符串内容比较
==运算符用于比较两个对象的引用,而不是它们的内容。这意味着:
对于基本数据类型,==比较的是实际的值。
对于对象,包括 String 对象,==比较的是它们在内存中的地址(引用)
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
System.out.println(str1 == str2); // true,因为它们指向字符串池中的同一个对象
System.out.println(str1 == str3); // false,因为 str3 是新创建的对象,指向不同的内存地址
要比较两个字符串的内容(即它们的实际字符序列是否相同),应该使用 equals() 方法。equals() 方法会逐字符比较字符串的内容。
System.out.println(str1.equals(str2)); // true,因为内容相同
System.out.println(str1.equals(str3)); // true,因为内容相同