Java 常用工具类与异常处理详解
·
1. 引言
在 Java 开发中,熟练使用标准库提供的工具类以及正确处理异常,是编写健壮、可维护代码的基础。本文将结合代码示例,详细讲解 Java 中最常用的工具类(如集合框架、日期时间、字符串处理等)以及异常处理机制的核心概念与实践。
2. 常用工具类详解
2.1 集合框架工具类 (java.util.Collections)
Collections 类提供了大量静态方法,用于操作或返回集合(如 List、Set、Map)。
import java.util.*;
public class CollectionsDemo {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5, 9));
// 排序
Collections.sort(numbers);
System.out.println("排序后: " + numbers); // [1, 1, 3, 4, 5, 9]
// 反转
Collections.reverse(numbers);
System.out.println("反转后: " + numbers); // [9, 5, 4, 3, 1, 1]
// 查找最大/最小值
System.out.println("最大值: " + Collections.max(numbers)); // 9
System.out.println("最小值: " + Collections.min(numbers)); // 1
// 创建不可修改的集合视图(安全地返回给外部)
List<Integer> unmodifiableList = Collections.unmodifiableList(numbers);
// unmodifiableList.add(10); // 抛出 UnsupportedOperationException
// 创建同步(线程安全)的集合包装
List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());
}
}
2.2 数组工具类 (java.util.Arrays)
Arrays 类包含用于操作数组(如排序、搜索、比较、填充)的各种静态方法。
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int[] arr = {5, 2, 8, 1, 9};
// 排序
Arrays.sort(arr);
System.out.println("排序后数组: " + Arrays.toString(arr)); // [1, 2, 5, 8, 9]
// 二分查找(数组必须已排序)
int index = Arrays.binarySearch(arr, 5);
System.out.println("元素5的索引: " + index); // 2
// 填充
int[] filledArr = new int[5];
Arrays.fill(filledArr, 7);
System.out.println("填充后: " + Arrays.toString(filledArr)); // [7, 7, 7, 7, 7]
// 比较两个数组是否相等(深度比较)
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
System.out.println("数组相等: " + Arrays.equals(arr1, arr2)); // true
// 多维数组的深度字符串表示和比较
int[][] deepArr1 = {{1, 2}, {3, 4}};
int[][] deepArr2 = {{1, 2}, {3, 4}};
System.out.println("深度字符串: " + Arrays.deepToString(deepArr1));
System.out.println("深度相等: " + Arrays.deepEquals(deepArr1, deepArr2)); // true
}
}
2.3 字符串工具 (java.lang.String, java.util.StringJoiner, org.apache.commons.lang3.StringUtils)
Java 内置的 String 类提供了丰富的方法,StringJoiner 用于高效拼接,第三方库如 Apache Commons Lang 的 StringUtils 功能更强大。
import java.util.StringJoiner;
public class StringToolsDemo {
public static void main(String[] args) {
// 1. String 类常用方法
String str = " Hello, Java! ";
System.out.println("去除首尾空格: '" + str.trim() + "'"); // 'Hello, Java!'
System.out.println("转大写: " + str.toUpperCase()); // " HELLO, JAVA! "
System.out.println("是否包含'Java': " + str.contains("Java")); // true
System.out.println("替换: " + str.replace("Java", "World")); // " Hello, World! "
System.out.println("分割: " + Arrays.toString(str.split(","))); // [" Hello", " Java! "]
// 2. StringJoiner (Java 8+) 用于高效拼接
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("Apple").add("Banana").add("Orange");
System.out.println("拼接结果: " + joiner.toString()); // [Apple, Banana, Orange]
// 3. 使用 StringBuilder 进行大量字符串修改(非线程安全,但更快)
StringBuilder sb = new StringBuilder("Start");
sb.append(" Middle").append(" End");
sb.insert(6, "Inserted ");
System.out.println("StringBuilder 结果: " + sb.toString()); // Start Inserted Middle End
}
}
// 假设已引入 Apache Commons Lang3 依赖
// import org.apache.commons.lang3.StringUtils;
// System.out.println("是否空白: " + StringUtils.isBlank(" ")); // true
// System.out.println("截取: " + StringUtils.substring("HelloWorld", 5, 10)); // "World"
// System.out.println("重复: " + StringUtils.repeat("Ab", 3)); // "AbAbAb"
2.4 日期时间工具 (java.time.*)
Java 8 引入了全新的日期时间 API (java.time 包),解决了旧 Date 和 Calendar 的诸多问题。
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class DateTimeDemo {
public static void main(String[] args) {
// 当前时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间: " + now);
// 创建特定日期
LocalDate birthday = LocalDate.of(1995, Month.MAY, 23);
System.out.println("生日: " + birthday);
// 格式化与解析
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = now.format(formatter);
System.out.println("格式化后: " + formatted);
LocalDateTime parsed = LocalDateTime.parse("2023-10-01 12:30:00", formatter);
System.out.println("解析后: " + parsed);
// 日期计算
LocalDate nextWeek = LocalDate.now().plusDays(7);
System.out.println("一周后: " + nextWeek);
// 时间间隔
LocalDate start = LocalDate.of(2023, 1, 1);
LocalDate end = LocalDate.of(2023, 12, 31);
long daysBetween = ChronoUnit.DAYS.between(start, end);
System.out.println("2023年总天数: " + daysBetween); // 364 (因为 between 是 exclusive end)
// 时区处理
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("上海当前时间: " + zonedNow);
}
}
2.5 数学工具 (java.lang.Math, java.util.Random)
Math 类提供基本的数学运算,Random 用于生成随机数。
import java.util.Random;
public class MathDemo {
public static void main(String[] args) {
// Math 类
System.out.println("绝对值: " + Math.abs(-10.5)); // 10.5
System.out.println("向上取整: " + Math.ceil(4.3)); // 5.0
System.out.println("向下取整: " + Math.floor(4.7)); // 4.0
System.out.println("四舍五入: " + Math.round(4.5)); // 5
System.out.println("最大值: " + Math.max(10, 20)); // 20
System.out.println("平方根: " + Math.sqrt(25)); // 5.0
System.out.println("幂运算: " + Math.pow(2, 3)); // 8.0
System.out.println("随机数 [0,1): " + Math.random()); // 0.0 <= x < 1.0
// Random 类 (更灵活的随机数生成)
Random random = new Random();
System.out.println("随机整数: " + random.nextInt()); // 任意 int
System.out.println("0-99随机整数: " + random.nextInt(100)); // [0, 100)
System.out.println("随机布尔值: " + random.nextBoolean());
System.out.println("随机高斯分布: " + random.nextGaussian());
}
}
3. 异常处理详解
3.1 异常体系结构
Java 异常都是 Throwable 类的子类,主要分为两大类:
- Error: 系统级错误,程序通常无法处理(如
OutOfMemoryError)。 - Exception: 程序可处理的异常。又分为:
- Checked Exception (受检异常): 编译时检查,必须捕获或声明抛出(如
IOException,SQLException)。 - Unchecked Exception (运行时异常): 编译时不检查,通常由程序逻辑错误引起(如
NullPointerException,IllegalArgumentException)。
- Checked Exception (受检异常): 编译时检查,必须捕获或声明抛出(如
3.2 异常处理关键字:try-catch-finally
public class TryCatchFinallyDemo {
public static void main(String[] args) {
try {
// 可能抛出异常的代码
int result = divide(10, 0);
System.out.println("结果: " + result);
} catch (ArithmeticException e) {
// 捕获特定异常
System.err.println("捕获到算术异常: " + e.getMessage());
// 打印异常堆栈跟踪(调试用)
e.printStackTrace();
} catch (Exception e) {
// 捕获更通用的异常(应放在更具体的 catch 后面)
System.err.println("捕获到其他异常: " + e);
} finally {
// 无论是否发生异常,finally 块都会执行(常用于释放资源)
System.out.println("finally 块始终执行");
}
System.out.println("程序继续运行...");
}
static int divide(int a, int b) {
return a / b; // 当 b 为 0 时抛出 ArithmeticException
}
}
3.3 try-with-resources (Java 7+)
自动管理资源(实现了 AutoCloseable 接口的对象),确保资源被正确关闭。
import java.io.*;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 传统方式(容易忘记关闭)
// BufferedReader br = null;
// try {
// br = new BufferedReader(new FileReader("file.txt"));
// String line = br.readLine();
// } catch (IOException e) {
// e.printStackTrace();
// } finally {
// if (br != null) {
// try { br.close(); } catch (IOException e) { /* 忽略 */ }
// }
// }
// try-with-resources (推荐)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line = br.readLine();
System.out.println("第一行: " + line);
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("读取文件时发生IO异常: " + e.getMessage());
}
// 无需显式关闭,br 会自动调用 close()
}
}
3.4 抛出异常 (throw) 与声明异常 (throws)
import java.io.*;
public class ThrowThrowsDemo {
// 方法声明可能抛出的受检异常
public static void readFile(String filename) throws FileNotFoundException, IOException {
if (filename == null || filename.isEmpty()) {
// 主动抛出异常
throw new IllegalArgumentException("文件名不能为空");
}
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
System.out.println(br.readLine());
}
// FileNotFoundException 是 IOException 的子类,这里也可以只声明 throws IOException
}
public static void main(String[] args) {
try {
readFile("test.txt");
readFile(""); // 会抛出 IllegalArgumentException
} catch (IllegalArgumentException e) {
System.err.println("参数错误: " + e.getMessage());
} catch (FileNotFoundException e) {
System.err.println("文件不存在: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO错误: " + e.getMessage());
}
}
}
3.5 自定义异常
通过继承 Exception(受检)或 RuntimeException(非受检)来创建自定义异常。
// 自定义受检异常
class InsufficientBalanceException extends Exception {
public InsufficientBalanceException(String message) {
super(message);
}
}
// 自定义非受检异常
class InvalidAccountException extends RuntimeException {
public InvalidAccountException(String message) {
super(message);
}
}
class BankAccount {
private double balance;
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
throw new InsufficientBalanceException("余额不足。当前余额: " + balance);
}
balance -= amount;
}
public void setAccountId(String id) {
if (id == null || id.length() != 10) {
throw new InvalidAccountException("账户ID必须为10位字符: " + id);
}
// 设置账户ID...
}
}
public class CustomExceptionDemo {
public static void main(String[] args) {
BankAccount account = new BankAccount();
try {
account.withdraw(100); // 可能抛出 InsufficientBalanceException
} catch (InsufficientBalanceException e) {
System.err.println("取款失败: " + e.getMessage());
}
// 非受检异常,可以不捕获,但程序会终止
account.setAccountId("123"); // 抛出 InvalidAccountException
}
}
3.6 异常处理最佳实践
- 具体异常优先: 捕获最具体的异常类型,而不是笼统的
Exception。 - 不要吞掉异常: 空的
catch块会隐藏错误,至少应记录日志。 - 使用 finally 或 try-with-resources 释放资源。
- 异常应提供有意义的上下文信息,便于调试。
- 避免在 finally 块中使用 return,它会吞掉 try 或 catch 块中的异常。
- 受检异常用于可恢复情况,运行时异常用于编程错误。
- 考虑异常转换:将底层异常包装为更贴近业务含义的异常再抛出。
// 不好的做法:吞掉异常
try {
// ...
} catch (Exception e) {
// 什么都没做!
}
// 改进做法:至少记录日志
try {
// ...
} catch (SpecificException e) {
logger.error("操作失败,原因: ", e);
// 或者根据情况决定是重试、返回默认值还是向上抛出
throw new BusinessException("业务操作失败", e); // 异常链
}
4. 综合示例:一个简单的文件处理器
下面是一个结合了工具类和异常处理的综合示例,演示如何安全地读取文件、处理数据并写入结果。
import java.io.*;
import java.nio.file.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class FileProcessor {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 处理文件:读取内容,添加时间戳,并写入新文件。
* @param inputPath 输入文件路径
* @param outputPath 输出文件路径
* @throws IOException 如果文件读写失败
* @throws IllegalArgumentException 如果参数无效
*/
public static void processFile(String inputPath, String outputPath) throws IOException {
// 参数校验(使用 Objects 工具类)
Objects.requireNonNull(inputPath, "输入文件路径不能为null");
Objects.requireNonNull(outputPath, "输出文件路径不能为null");
if (!Files.exists(Paths.get(inputPath))) {
throw new FileNotFoundException("输入文件不存在: " + inputPath);
}
List<String> lines = new ArrayList<>();
// 使用 try-with-resources 确保资源关闭
try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputPath))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} // 自动关闭 reader
if (lines.isEmpty()) {
System.out.println("文件为空,无需处理。");
return;
}
// 使用 Collections 工具类
Collections.reverse(lines); // 反转顺序作为示例处理
// 添加处理时间戳
String timestamp = LocalDateTime.now().format(DATE_FORMATTER);
lines.add(0, "// 处理时间: " + timestamp);
lines.add("// 处理完成");
// 写入输出文件
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputPath))) {
for (String line : lines) {
writer.write(line);
writer.newLine();
}
} // 自动关闭 writer
System.out.println("文件处理完成,输出至: " + outputPath);
}
public static void main(String[] args) {
String inputFile = "input.txt";
String outputFile = "output_" + System.currentTimeMillis() + ".txt";
try {
更多推荐
所有评论(0)