深入理解 Java 中的字符串、包装类与日期处理
在 Java 编程中,字符串操作、基本类型包装以及日期时间处理是日常开发中高频出现的场景。掌握这些知识点不仅能提升代码效率,更能避免一些隐藏的坑。本文将系统讲解 String 字符串、可变字符串、八大包装类及日期处理的核心知识与实践技巧。
一、字符串:不可变的字符序列
String 类是 Java 中最常用的类之一,它的不可变性是理解其特性的关键。
1. 核心特性解析
- 不可继承:String 被
final
修饰,无法被其他类继承。 - 不可变对象:字符串一旦创建,其内部字符序列就不可修改。任何修改操作(如拼接、替换)都会生成新的 String 对象。
- 两种创建方式:
// 字面量方式:创建于字符串常量池,相同内容会复用 String s1 = "hello"; // new方式:创建于堆内存,每次new都会生成新对象 String s2 = new String("hello"); System.out.println(s1 == s2); // 输出false(地址不同) System.out.println(s1.equals(s2)); // 输出true(内容相同)
2. 常用方法实战
String 类提供了丰富的方法用于字符串操作,以下是开发中最常用的 API:
方法分类 | 关键方法 | 功能说明 |
---|---|---|
基础信息 | length() | 返回字符串长度 |
charAt(int index) | 获取指定索引的字符(索引从 0 开始) | |
查找操作 | indexOf(String str) | 查找子串首次出现的索引,不存在返回 - 1 |
lastIndexOf(String str) | 查找子串最后出现的索引 | |
截取拼接 | substring(int begin) | 从 begin 索引截取到末尾 |
substring(int begin, int end) | 截取 [begin, end) 区间的子串 | |
concat(String str) | 拼接字符串(等价于+ ,但效率更高) | |
修改转换 | replace(CharSequence old, CharSequence new) | 替换子串 |
split(String regex) | 按正则分割为字符串数组 | |
toUpperCase() /toLowerCase() | 转换大小写 | |
toCharArray() | 转为字符数组 | |
类型转换 | valueOf(任意类型) | 静态方法,将其他类型转为字符串 |
示例代码:
public class StringDemo {public static void main(String[] args) {String str = "Java Programming";// 基础操作System.out.println("长度:" + str.length()); // 18System.out.println("第5个字符:" + str.charAt(4)); // 空格// 查找与截取int index = str.indexOf("Program");System.out.println("Program起始索引:" + index); // 5System.out.println("截取子串:" + str.substring(5, 12)); // Program// 替换与分割String replaced = str.replace("Programming", "Coding");System.out.println("替换后:" + replaced); // Java CodingString[] parts = str.split(" ");System.out.println("分割结果:" + parts[0] + "|" + parts[1]); // Java|Programming}
}
-
toCharArray()
是String
类的方法,作用是将字符串转换为字符数组(char[]
)。例如:String str = "hello"; char[] arr = str.toCharArray(); // arr 结果为 {'h','e','l','l','o'}
-
String.valueOf(char[])
是String
类的静态方法,作用是将字符数组(char[]
)转换为字符串(String
),正好与toCharArray()
相反。例如:char[] arr = {'h','i'}; String str = String.valueOf(arr); // str 结果为 "hi"
二、可变字符串:StringBuilder 与 StringBuffer
当需要频繁修改字符串时,使用 String 会产生大量临时对象,导致性能问题。此时应选择可变字符串类。
1. 两者的异同点
- 共同点:
- 继承自
AbstractStringBuilder
,内部通过动态数组存储字符 - 提供
append()
、insert()
、delete()
等修改方法,支持原地修改
- 继承自
- 核心区别:
StringBuffer
:线程安全(方法加了synchronized
锁),性能较低StringBuilder
:非线程安全,性能更高(单线程首选)
2. 常用方法与性能对比
核心方法:
append(任意类型)
:末尾追加内容(最常用)insert(int offset, 任意类型)
:指定位置插入delete(int start, int end)
:删除 [start, end) 区间内容reverse()
:反转字符串toString()
:转为 String 类型
性能测试:
public class StringPerformance {public static void main(String[] args) {int loop = 100000; // 十万次拼接// String拼接(效率极低)long start1 = System.currentTimeMillis();String s = "";for (int i = 0; i < loop; i++) {s += i;}long end1 = System.currentTimeMillis();System.out.println("String耗时:" + (end1 - start1) + "ms"); // 约3000ms// StringBuilder拼接(效率极高)long start2 = System.currentTimeMillis();StringBuilder sb = new StringBuilder();for (int i = 0; i < loop; i++) {sb.append(i);}long end2 = System.currentTimeMillis();System.out.println("StringBuilder耗时:" + (end2 - start2) + "ms"); // 约5ms}
}
结论:字符串修改频繁时,优先使用StringBuilder
(单线程)或StringBuffer
(多线程)。
三、包装类:基本类型的对象化封装
Java 是面向对象语言,但 8 种基本数据类型不是对象。包装类的出现正是为了将基本类型封装为对象,满足泛型、集合等场景的需求。
1. 八大包装类对应关系
基本数据类型 | 包装类 | 父类 | 特殊说明 |
---|---|---|---|
byte | Byte | Number | - |
short | Short | Number | - |
int | Integer | Number | 最常用,缓存 - 128~127 区间对象 |
long | Long | Number | - |
float | Float | Number | - |
double | Double | Number | - |
char | Character | Object | 无父类 Number |
boolean | Boolean | Object | 无父类 Number |
2. 核心功能与自动装箱拆箱
-
类型转换:
- 字符串转基本类型:
Integer.parseInt("123")
、Double.parseDouble("3.14")
- 基本类型转包装类:
Integer.valueOf(123)
(推荐,利用缓存) - 包装类转基本类型:
Integer.intValue()
- 字符串转基本类型:
-
自动装箱与拆箱(JDK5+):
// 自动装箱:int -> Integer Integer a = 100; // 等价于 Integer a = Integer.valueOf(100);// 自动拆箱:Integer -> int int b = a; // 等价于 int b = a.intValue();
-
缓存机制:Integer 对
-128~127
区间的值做了缓存,多次创建该区间的对象会复用:Integer x = 127; Integer y = 127; System.out.println(x == y); // true(复用缓存对象)Integer m = 128; Integer n = 128; System.out.println(m == n); // false(超出缓存范围,新建对象)
四、日期时间处理:从传统 API 到新特性
日期时间处理是开发中的难点,Java 提供了多套 API,其中 Java 8 的新 API 是目前的最佳选择。
1. 传统日期类(不推荐)
java.util.Date
:表示特定瞬间,精度到毫秒,但多数方法已过时。SimpleDateFormat
:用于日期格式化与解析,但线程不安全。
2. Java 8 新日期 API(推荐)
Java 8 引入的java.time
包解决了传统 API 的线程安全、设计混乱等问题,核心类包括:
LocalDate
:日期(年 / 月 / 日)LocalTime
:时间(时 / 分 / 秒)LocalDateTime
:日期 + 时间DateTimeFormatter
:线程安全的格式化工具
核心优势:
- 不可变对象,线程安全
- 方法链编程,操作简洁
- 清晰区分日期、时间、带时区的时间
示例代码:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public class NewDateDemo {public static void main(String[] args) {// 获取当前日期时间LocalDateTime now = LocalDateTime.now();System.out.println("当前时间:" + now); // 2025-10-21T15:30:00.123// 格式化(线程安全)DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");String formatted = now.format(formatter);System.out.println("格式化后:" + formatted); // 2025年10月21日 15:30// 日期计算LocalDateTime tomorrow = now.plusDays(1); // 明天LocalDateTime lastMonth = now.minusMonths(1); // 上个月System.out.println("明天同一时间:" + tomorrow.format(formatter));// 解析字符串LocalDateTime parsed = LocalDateTime.parse("2023年01月01日 08:00", formatter);System.out.println("解析后的月份:" + parsed.getMonthValue()); // 1}
}
总结
- 字符串选择:少量操作选
String
,频繁修改选StringBuilder
(单线程)或StringBuffer
(多线程)。 - 包装类注意:利用自动装箱拆箱简化代码,但需注意
null
拆箱的空指针风险和缓存机制的影响。 - 日期处理:彻底抛弃
Date
和SimpleDateFormat
,全面使用 Java 8 的java.time
包,兼顾安全性与易用性。
掌握这些基础组件的特性与最佳实践,能显著提升代码质量与开发效率,避免常见的性能问题和 bug。