Skip to content

Java异常处理与常用API完全指南

目录


一、异常处理体系

1.1 异常概述

异常:代码出现的不正常现象,在Java中以类和对象的形式存在。

异常继承体系

Throwable
├── Error(错误)
│   └── 相当于绝症,需要大规模重构代码
└── Exception(异常)
    ├── 编译时期异常
    │   └── Exception及其子类(RuntimeException除外)
    └── 运行时期异常
        └── RuntimeException及其子类

1.2 编译时期异常 vs 运行时期异常

特性编译时期异常运行时期异常
语法检查语法正确,但调用方法时爆红语法正确,编写时不报错
运行时机必须处理后才能编译通过编译通过,运行时报错
典型例子FileNotFoundException, IOExceptionNullPointerException, ArrayIndexOutOfBoundsException
处理方式必须处理(throws或try-catch)一般不处理,修改代码逻辑

判断方法

  • 按住Ctrl点击异常类,查看继承体系
  • 如果继承体系中有RuntimeException → 运行时期异常
  • 如果继承体系中没有RuntimeException → 编译时期异常

1.3 异常处理方式

方式一:throws抛出异常

格式

java
修饰符 返回值类型 方法名(参数列表) throws 异常类名 {
    // 方法体
}

特点

  • 将异常向上抛出,由调用者处理
  • 可以抛出多个异常:throws 异常1, 异常2
  • 如果多个异常有继承关系,只需抛出父类异常

示例

java
public static void insert(String s) throws FileNotFoundException {
    if (!s.endsWith(".txt")) {
        throw new FileNotFoundException("文件找不到");
    }
    System.out.println("文件处理成功");
}

弊端:如果一直往上抛,可能导致一个功能的异常影响其他功能执行。

方式二:try-catch捕获异常

格式

java
try {
    // 可能出现异常的代码
} catch (异常类型 变量名) {
    // 异常处理方案
    e.printStackTrace(); // 打印详细异常信息
}

特点

  • 捕获并处理异常,不影响后续代码执行
  • 可以有多个catch块捕获不同异常
  • 如果catch的异常有继承关系,可以直接catch父类异常

示例

java
public static void main(String[] args) {
    String s = "abc.txt1";
    try {
        insert(s);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    System.out.println("删除功能");
    System.out.println("修改功能");
    System.out.println("查询功能");
}

多个catch块

java
try {
    // 可能出现多种异常的代码
} catch (IOException e) {
    e.printStackTrace();
} catch (NullPointerException e) {
    e.printStackTrace();
}

1.4 finally关键字

作用:不管是否捕获到异常,finally块中的代码都会执行。

格式

java
try {
    // 可能出现异常的代码
} catch (异常类型 变量名) {
    // 异常处理
} finally {
    // 一定会执行的代码
}

使用场景

  • 释放资源(IO流、数据库连接、Socket等)
  • 清理工作

示例

java
public static int method() {
    try {
        String s = null;
        System.out.println(s.length());
        return 2;
    } catch (Exception e) {
        return 1;
    } finally {
        System.out.println("我一定要执行");
    }
}

注意:即使try或catch中有return语句,finally也会在return之前执行。

1.5 异常处理注意事项

子类重写方法时的异常抛出规则

  • 父类方法抛异常 → 子类重写后可抛可不抛
  • 父类方法没抛异常 → 子类重写后不能抛异常

throws和try-catch的使用时机

  • 如果处理异常后想让后续代码正常执行 → 使用try-catch
  • 如果方法之间是递进调用关系 → 可以先throws,最后统一try-catch处理

1.6 打印异常信息的三个方法

Throwable类的方法

方法说明示例输出
String toString()返回异常类型和异常信息java.io.FileNotFoundException: 文件找不到
String getMessage()获取异常信息文件找不到
void printStackTrace()获取最详细的异常信息(包括堆栈跟踪)完整的异常堆栈信息

推荐使用printStackTrace() - 信息最详细,便于调试。


二、BigInteger大整数处理

2.1 BigInteger概述

作用:处理超出long类型范围的超大整数。

可表示范围:理论上无限大(受内存限制)。

2.2 BigInteger使用

构造方法

java
BigInteger(String val)

字符串内容必须是数字格式。

常用方法

方法说明
BigInteger add(BigInteger val)加法
BigInteger subtract(BigInteger val)减法
BigInteger multiply(BigInteger val)乘法
BigInteger divide(BigInteger val)除法
int intValue()转换为int
long longValue()转换为long

示例

java
BigInteger b1 = new BigInteger("12121212121212121212121212121212121");
BigInteger b2 = new BigInteger("12121212121212121212121212121212121");

BigInteger sum = b1.add(b2);
BigInteger diff = b1.subtract(b2);
BigInteger product = b1.multiply(b2);
BigInteger quotient = b1.divide(b2);

System.out.println("加法结果: " + sum);
System.out.println("减法结果: " + diff);
System.out.println("乘法结果: " + product);
System.out.println("除法结果: " + quotient);

三、BigDecimal精确计算

3.1 BigDecimal概述

作用:解决float和double类型直接参与运算时出现的精度损失问题。

问题示例

java
float a = 3.55F;
float b = 2.12F;
float sum = a + b;
System.out.println(sum); // 输出: 5.6699998,而非预期的5.67

3.2 BigDecimal使用

构造方法

java
BigDecimal(String val)

推荐创建方式

java
static BigDecimal valueOf(double val)

常用方法

方法说明
BigDecimal add(BigDecimal val)加法
BigDecimal subtract(BigDecimal val)减法
BigDecimal multiply(BigDecimal val)乘法
BigDecimal divide(BigDecimal val)除法(除不尽会报错)
BigDecimal divide(BigDecimal divisor, int scale, RoundingMode mode)除法(指定精度和舍入方式)

RoundingMode舍入模式

  • UP:向上加1
  • DOWN:直接舍去
  • HALF_UP:四舍五入

示例

java
BigDecimal b1 = BigDecimal.valueOf(3.55);
BigDecimal b2 = BigDecimal.valueOf(2.12);

BigDecimal sum = b1.add(b2);
System.out.println("加法: " + sum); // 5.67

BigDecimal diff = b1.subtract(b2);
System.out.println("减法: " + diff); // 1.43

BigDecimal product = b1.multiply(b2);
System.out.println("乘法: " + product);

BigDecimal quotient = b1.divide(b2, 2, RoundingMode.HALF_UP);
System.out.println("除法(保留2位小数,四舍五入): " + quotient);

注意:除法如果除不尽且未指定精度和舍入模式,会抛出ArithmeticException


四、Date日期类

4.1 Date概述

作用:表示特定的瞬间,精确到毫秒。

时间常识

  • 1秒 = 1000毫秒
  • 北京时区:东八区(东经116.20,北纬39.56)
  • 时间原点:1970年1月1日 0时0分0秒(Unix纪元)

4.2 Date使用

构造方法

构造方法说明
Date()根据当前系统时间创建Date对象
Date(long time)根据指定毫秒值创建Date对象(从时间原点开始计算)

常用方法

方法说明
void setTime(long time)设置时间(毫秒值)
long getTime()获取时间对应的毫秒值

示例

java
Date date1 = new Date();
System.out.println("当前时间: " + date1);

Date date2 = new Date(1000L);
System.out.println("时间原点后1秒: " + date2);

date1.setTime(2000L);
System.out.println("设置后的时间: " + date1);

long millis = date2.getTime();
System.out.println("毫秒值: " + millis);

五、Calendar日历类

5.1 Calendar概述

特点:抽象类,用于操作日历字段。

获取对象

java
static Calendar getInstance()

5.2 Calendar使用

常用方法

方法说明
int get(int field)返回给定日历字段的值
void set(int field, int value)将给定日历字段设置为指定值
void add(int field, int amount)为给定日历字段添加或减去指定时间量
Date getTime()将Calendar转换为Date对象
void set(int year, int month, int date)设置年月日

重要提示:Calendar中的月份从0开始计算(0=一月,1=二月,...,11=十二月)。

日历字段常量

  • Calendar.YEAR - 年
  • Calendar.MONTH - 月
  • Calendar.DATE / Calendar.DAY_OF_MONTH - 日
  • Calendar.HOUR - 小时(12小时制)
  • Calendar.HOUR_OF_DAY - 小时(24小时制)
  • Calendar.MINUTE - 分钟
  • Calendar.SECOND - 秒

示例

java
Calendar calendar = Calendar.getInstance();

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 注意:需要+1
int day = calendar.get(Calendar.DATE);

System.out.println("当前日期: " + year + "-" + month + "-" + day);

calendar.set(Calendar.YEAR, 2000);
System.out.println("设置年份后: " + calendar.get(Calendar.YEAR));

calendar.add(Calendar.YEAR, -1);
System.out.println("减1年后: " + calendar.get(Calendar.YEAR));

Date date = calendar.getTime();
System.out.println("转换为Date: " + date);

5.3 实用案例:计算某年2月有多少天

java
public static int getDaysOfFebruary(int year) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, 2, 1); // 设置为3月1日(月份从0开始)
    calendar.add(Calendar.DATE, -1); // 减1天,得到2月最后一天
    return calendar.get(Calendar.DATE);
}

public static void main(String[] args) {
    System.out.println("2000年2月有" + getDaysOfFebruary(2000) + "天"); // 29天
    System.out.println("2001年2月有" + getDaysOfFebruary(2001) + "天"); // 28天
}

六、SimpleDateFormat日期格式化

6.1 SimpleDateFormat概述

作用

  • 将Date对象按照指定格式格式化为字符串
  • 将符合格式的字符串解析为Date对象

构造方法

java
SimpleDateFormat(String pattern)

6.2 格式化模式

字母含义示例
yyyyy → 2025
MMM → 01-12
ddd → 01-31
H时(24小时制)HH → 00-23
h时(12小时制)hh → 01-12
mmm → 00-59
sss → 00-59
E星期E → 星期五

常用模式

  • yyyy-MM-dd → 2025-01-24
  • yyyy-MM-dd HH:mm:ss → 2025-01-24 15:30:45
  • yyyy年MM月dd日 E → 2025年01月24日 星期五

6.3 SimpleDateFormat使用

常用方法

方法说明
String format(Date date)将Date格式化为字符串
Date parse(String source)将字符串解析为Date

示例

java
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Date now = new Date();
String dateStr = sdf.format(now);
System.out.println("格式化后的日期: " + dateStr);

String str = "2025-01-24 15:30:00";
Date parsedDate = sdf.parse(str);
System.out.println("解析后的Date: " + parsedDate);

七、常见面试题

⭐ 基础题

1. 编译时期异常和运行时期异常的区别?

答案

  • 编译时期异常:语法正确,但调用方法时编译器强制要求处理。必须使用throws或try-catch处理才能编译通过。例如:FileNotFoundException、IOException。
  • 运行时期异常:语法正确,编译时不报错,运行时才抛出。通常是代码逻辑错误导致,一般不需要显式处理,而是修改代码。例如:NullPointerException、ArrayIndexOutOfBoundsException。

2. throws和throw的区别?

答案

  • throws:用在方法声明处,表示该方法可能抛出的异常,将异常抛给调用者处理。
  • throw:用在方法体内,用于手动创建并抛出一个异常对象。
java
public void method() throws IOException {
    throw new IOException("手动抛出异常");
}

3. finally块什么时候会执行?

答案: finally块无论是否捕获到异常都会执行。即使try或catch块中有return语句,finally也会在return之前执行。

特殊情况:如果在try或catch块中执行了System.exit(0),finally不会执行。


4. try-catch-finally的执行顺序?

答案

  1. 执行try块
  2. 如果try块出现异常,执行对应的catch块
  3. 无论是否出现异常,都执行finally块
  4. 如果try或catch中有return,先执行finally,再执行return

⭐⭐ 进阶题

5. 如何选择使用throws还是try-catch?

答案

  • 使用try-catch:如果处理异常后,希望后续代码继续执行。
  • 使用throws:如果方法之间是递进调用关系,可以先throws,在最后统一用try-catch处理。
  • 实际开发:通常在业务逻辑层使用throws,在控制层或界面层使用try-catch统一处理。

6. 为什么float和double运算会丢失精度?如何解决?

答案原因:float和double采用IEEE 754浮点数标准,某些十进制小数无法精确表示为二进制浮点数,导致精度损失。

解决方案:使用BigDecimal类进行精确计算。

java
float a = 0.1f;
float b = 0.2f;
System.out.println(a + b); // 0.30000001

BigDecimal b1 = BigDecimal.valueOf(0.1);
BigDecimal b2 = BigDecimal.valueOf(0.2);
System.out.println(b1.add(b2)); // 0.3

7. Calendar类中月份为什么要+1?

答案: Calendar类中月份从0开始计算(0=一月,1=二月,...,11=十二月),这是设计上的历史原因。使用时需要:

  • 获取月份时:calendar.get(Calendar.MONTH) + 1
  • 设置月份时:calendar.set(Calendar.MONTH, 实际月份 - 1)

8. SimpleDateFormat是线程安全的吗?如何解决?

答案: SimpleDateFormat不是线程安全的,因为它在格式化过程中使用了实例变量来存储中间结果。

解决方案

  1. 每次使用时创建新实例
  2. 使用ThreadLocal
  3. 使用JDK 8引入的DateTimeFormatter(线程安全)

⭐⭐⭐ 高级题

9. 子类重写父类方法时,异常抛出有什么限制?

答案

  • 父类方法抛出异常 → 子类重写方法可以抛出相同异常或其子类异常,也可以不抛出异常
  • 父类方法没有抛出异常 → 子类重写方法不能抛出编译时期异常(可以抛出运行时期异常)

原因:多态调用时,父类引用指向子类对象,编译器只看父类方法的异常声明。


10. 如何自定义异常?

答案

  1. 继承Exception或RuntimeException
  2. 提供构造方法
java
public class MyException extends Exception {
    public MyException() {
        super();
    }
    
    public MyException(String message) {
        super(message);
    }
}

public class MyRuntimeException extends RuntimeException {
    public MyRuntimeException() {
        super();
    }
    
    public MyRuntimeException(String message) {
        super(message);
    }
}

使用场景

  • 继承Exception → 编译时期异常,强制调用者处理
  • 继承RuntimeException → 运行时期异常,不强制处理

11. try-with-resources语法是什么?有什么优势?

答案: JDK 7引入的语法,用于自动关闭实现了AutoCloseable接口的资源。

java
try (FileInputStream fis = new FileInputStream("test.txt")) {
    // 使用资源
} catch (IOException e) {
    e.printStackTrace();
}
// 资源会自动关闭,无需finally

优势

  • 代码简洁,无需手动关闭资源
  • 自动处理资源关闭时的异常
  • 确保资源一定会被关闭

12. 异常链(异常转译)是什么?如何实现?

答案: 异常链是指捕获一个异常后,抛出另一个异常,但保留原始异常信息。

java
try {
    // 可能抛出IOException
} catch (IOException e) {
    throw new MyException("业务处理失败", e); // 保留原始异常
}

使用场景

  • 分层架构中,将底层异常转换为业务异常
  • 隐藏实现细节,提供更友好的异常信息

八、实用代码片段

8.1 异常处理最佳实践

java
public class ExceptionDemo {
    public void processFile(String filePath) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filePath);
            // 处理文件
        } catch (FileNotFoundException e) {
            System.err.println("文件未找到: " + e.getMessage());
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void processFileJava7(String filePath) {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            // 处理文件
        } catch (IOException e) {
            System.err.println("IO异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

8.2 BigDecimal工具类

java
public class BigDecimalUtil {
    private static final int DEF_DIV_SCALE = 10;
    
    public static double add(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.add(b2).doubleValue();
    }
    
    public static double subtract(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.subtract(b2).doubleValue();
    }
    
    public static double multiply(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.multiply(b2).doubleValue();
    }
    
    public static double divide(double v1, double v2) {
        return divide(v1, v2, DEF_DIV_SCALE);
    }
    
    public static double divide(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("精度必须大于等于0");
        }
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }
}

8.3 日期工具类

java
public class DateUtil {
    private static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    
    public static String format(Date date) {
        return format(date, DEFAULT_PATTERN);
    }
    
    public static String format(Date date, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.format(date);
    }
    
    public static Date parse(String dateStr) throws ParseException {
        return parse(dateStr, DEFAULT_PATTERN);
    }
    
    public static Date parse(String dateStr, String pattern) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.parse(dateStr);
    }
    
    public static int getDaysBetween(Date start, Date end) {
        long diff = end.getTime() - start.getTime();
        return (int) (diff / (1000 * 60 * 60 * 24));
    }
    
    public static Date addDays(Date date, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, days);
        return calendar.getTime();
    }
}

九、经典练习题

基础题

1. 异常捕获练习

编写一个程序,要求用户输入一个整数,如果输入的不是整数,捕获异常并提示重新输入。

java
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    while (true) {
        try {
            System.out.print("请输入一个整数: ");
            int num = Integer.parseInt(scanner.nextLine());
            System.out.println("你输入的整数是: " + num);
            break;
        } catch (NumberFormatException e) {
            System.out.println("输入格式错误,请重新输入!");
        }
    }
    scanner.close();
}

2. BigDecimal计算练习

编写程序计算圆的面积,要求精确到小数点后2位。

java
public static void main(String[] args) {
    double radius = 5.5;
    double area = calculateCircleArea(radius);
    System.out.println("半径为" + radius + "的圆面积是: " + area);
}

public static double calculateCircleArea(double radius) {
    BigDecimal r = BigDecimal.valueOf(radius);
    BigDecimal pi = BigDecimal.valueOf(Math.PI);
    BigDecimal area = r.multiply(r).multiply(pi);
    return area.setScale(2, RoundingMode.HALF_UP).doubleValue();
}

3. Date和SimpleDateFormat练习

编写程序,将当前日期格式化为"2025年01月24日 星期五"的格式。

java
public static void main(String[] args) {
    Date now = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E");
    String dateStr = sdf.format(now);
    System.out.println(dateStr);
}

进阶题

4. 计算两个日期之间的天数

编写程序,输入两个日期字符串(格式:yyyy-MM-dd),计算它们之间相差多少天。

java
public static void main(String[] args) throws ParseException {
    String date1 = "2025-01-01";
    String date2 = "2025-01-24";
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date d1 = sdf.parse(date1);
    Date d2 = sdf.parse(date2);
    
    long diff = Math.abs(d2.getTime() - d1.getTime());
    int days = (int) (diff / (1000 * 60 * 60 * 24));
    
    System.out.println("相差" + days + "天");
}

5. 判断某年是否为闰年

使用Calendar类判断某年是否为闰年。

java
public static boolean isLeapYear(int year) {
    return getDaysOfFebruary(year) == 29;
}

public static int getDaysOfFebruary(int year) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, 2, 1);
    calendar.add(Calendar.DATE, -1);
    return calendar.get(Calendar.DATE);
}

public static void main(String[] args) {
    System.out.println("2000年是闰年吗? " + isLeapYear(2000));
    System.out.println("2001年是闰年吗? " + isLeapYear(2001));
    System.out.println("2024年是闰年吗? " + isLeapYear(2024));
}

6. 自定义异常练习

编写一个用户注册程序,要求:

  • 用户名长度3-10位
  • 密码长度6-15位
  • 年龄18-100岁 如果不满足条件,抛出自定义异常。
java
class RegisterException extends Exception {
    public RegisterException(String message) {
        super(message);
    }
}

public class RegisterDemo {
    public static void register(String username, String password, int age) 
            throws RegisterException {
        if (username.length() < 3 || username.length() > 10) {
            throw new RegisterException("用户名长度必须在3-10位之间");
        }
        if (password.length() < 6 || password.length() > 15) {
            throw new RegisterException("密码长度必须在6-15位之间");
        }
        if (age < 18 || age > 100) {
            throw new RegisterException("年龄必须在18-100岁之间");
        }
        System.out.println("注册成功!");
    }
    
    public static void main(String[] args) {
        try {
            register("张三", "123456", 25);
        } catch (RegisterException e) {
            System.out.println("注册失败: " + e.getMessage());
        }
    }
}

挑战题

7. 实现一个简单的日志记录器

要求:

  • 记录时间戳、日志级别、日志内容
  • 日志级别:INFO、WARN、ERROR
  • 将日志写入文件
  • 使用异常处理确保资源正确关闭
java
enum LogLevel {
    INFO, WARN, ERROR
}

class Logger {
    private String logFile;
    
    public Logger(String logFile) {
        this.logFile = logFile;
    }
    
    public void log(LogLevel level, String message) {
        String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        String logLine = String.format("[%s] [%s] %s%n", timestamp, level, message);
        
        try (FileWriter fw = new FileWriter(logFile, true);
             BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write(logLine);
        } catch (IOException e) {
            System.err.println("日志写入失败: " + e.getMessage());
        }
    }
}

public class LoggerDemo {
    public static void main(String[] args) {
        Logger logger = new Logger("app.log");
        logger.log(LogLevel.INFO, "应用程序启动");
        logger.log(LogLevel.WARN, "内存使用率较高");
        logger.log(LogLevel.ERROR, "数据库连接失败");
    }
}

8. 日期计算器

编写一个日期计算器,支持以下功能:

  • 计算两个日期之间的天数
  • 计算指定日期N天后的日期
  • 计算指定日期N天前的日期
  • 判断某天是星期几
java
public class DateCalculator {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    
    public static int daysBetween(String date1, String date2) throws ParseException {
        Date d1 = sdf.parse(date1);
        Date d2 = sdf.parse(date2);
        long diff = Math.abs(d2.getTime() - d1.getTime());
        return (int) (diff / (1000 * 60 * 60 * 24));
    }
    
    public static String addDays(String date, int days) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sdf.parse(date));
        calendar.add(Calendar.DATE, days);
        return sdf.format(calendar.getTime());
    }
    
    public static String getWeekDay(String date) throws ParseException {
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sdf.parse(date));
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
        return weekDays[dayOfWeek];
    }
    
    public static void main(String[] args) throws ParseException {
        String date1 = "2025-01-01";
        String date2 = "2025-12-31";
        
        System.out.println("相差天数: " + daysBetween(date1, date2));
        System.out.println("100天后: " + addDays(date1, 100));
        System.out.println("100天前: " + addDays(date1, -100));
        System.out.println("星期: " + getWeekDay(date1));
    }
}

学习建议

重点掌握

  1. ⭐⭐⭐ 异常处理机制(try-catch-finally)
  2. ⭐⭐⭐ throws和throw的区别
  3. ⭐⭐⭐ BigDecimal解决精度问题
  4. ⭐⭐ SimpleDateFormat日期格式化
  5. ⭐⭐ Calendar日历操作

学习路径

  1. 理解异常体系结构
  2. 掌握异常处理的两种方式
  3. 熟练使用BigDecimal进行精确计算
  4. 掌握Date、Calendar、SimpleDateFormat的使用
  5. 通过练习题巩固知识

常见误区

  1. ❌ 混淆throws和throw的作用
  2. ❌ 忘记Calendar月份从0开始
  3. ❌ 使用double直接进行金额计算
  4. ❌ 忘记关闭IO资源
  5. ❌ SimpleDateFormat线程安全问题

实践建议

  1. 多写异常处理的代码,理解执行流程
  2. 使用BigDecimal处理金额相关的计算
  3. 封装日期工具类,提高代码复用性
  4. 阅读JDK源码,理解异常体系设计
  5. 关注JDK 8新日期API(LocalDate、LocalDateTime)