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 包),解决了旧 DateCalendar 的诸多问题。

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)。

Throwable

Error
如 OutOfMemoryError

Exception

Checked Exception
如 IOException

Unchecked Exception
RuntimeException

NullPointerException

IllegalArgumentException

ArrayIndexOutOfBoundsException

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 异常处理最佳实践

  1. 具体异常优先: 捕获最具体的异常类型,而不是笼统的 Exception
  2. 不要吞掉异常: 空的 catch 块会隐藏错误,至少应记录日志。
  3. 使用 finally 或 try-with-resources 释放资源
  4. 异常应提供有意义的上下文信息,便于调试。
  5. 避免在 finally 块中使用 return,它会吞掉 try 或 catch 块中的异常。
  6. 受检异常用于可恢复情况,运行时异常用于编程错误。
  7. 考虑异常转换:将底层异常包装为更贴近业务含义的异常再抛出。
// 不好的做法:吞掉异常
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 {

更多推荐