方法定义

LocalDate.with()java.time.LocalDate 类中用于创建修改后的新日期对象的一组方法。由于 LocalDate 是不可变对象,with 方法不会修改原对象,而是返回一个新的 LocalDate 实例。

主要重载方法

// 1. 使用 TemporalAdjuster(调整器)
public LocalDate with(TemporalAdjuster adjuster)

// 2. 修改指定字段
public LocalDate with(TemporalField field, long newValue)

功能说明

with() 方法的核心功能是 “基于当前日期,生成一个修改了某些字段的新日期”。它是函数式编程中“不可变性”原则的体现。

两大使用方向:

  1. 字段级修改:直接设置年、月、日等具体值。
  2. 逻辑级调整:使用预定义或自定义的“调整器”实现复杂日期计算(如“下一个周日”、“本月最后一天”)。

示例代码

方向一:字段级修改(with(TemporalField, long)

import java.time.LocalDate;
import java.time.temporal.ChronoField;

public class WithFieldExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2025, 8, 15); // 2025-08-15
        System.out.println("原始日期: " + date);

        // 修改年份
        LocalDate newYear = date.with(ChronoField.YEAR, 2030);
        System.out.println("修改年份: " + newYear); // 2030-08-15

        // 修改月份(数值)
        LocalDate newMonth = date.with(ChronoField.MONTH_OF_YEAR, 12);
        System.out.println("修改月份: " + newMonth); // 2025-12-15

        // 修改日期
        LocalDate newDay = date.with(ChronoField.DAY_OF_MONTH, 1);
        System.out.println("修改日期: " + newDay); // 2025-08-01
    }
}

💡 常用 ChronoField 常量

  • YEAR:年份
  • MONTH_OF_YEAR:月份(1-12)
  • DAY_OF_MONTH:月中的天(1-31)
  • DAY_OF_YEAR:年中的天(1-365/366)
  • ERA:纪元(如 AD/BC)

方向二:使用调整器(with(TemporalAdjuster)

1. 使用内置调整器(TemporalAdjusters

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class WithAdjusterExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2025, 8, 15); // 2025-08-15
        System.out.println("原始日期: " + date);

        // 本月第一天
        LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("本月第一天: " + firstDay); // 2025-08-01

        // 本月最后一天
        LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("本月最后一天: " + lastDay); // 2025-08-31

        // 下一个周日
        LocalDate nextSunday = date.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println("下一个周日: " + nextSunday); // 2025-08-17

        // 本月第一个周三
        LocalDate firstWednesday = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));
        System.out.println("本月第一个周三: " + firstWednesday); // 2025-08-06

        // 下一个工作日(自定义逻辑需自己实现)
    }
}

2. 自定义调整器

import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;

// 自定义:下一个工作日(跳过周六周日)
public class NextWorkingDay implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate date = LocalDate.from(temporal);
        do {
            date = date.plusDays(1);
        } while (date.getDayOfWeek() == DayOfWeek.SATURDAY || 
                 date.getDayOfWeek() == DayOfWeek.SUNDAY);
        return date;
    }
}

// 使用自定义调整器
LocalDate nextWorkDay = date.with(new NextWorkingDay());
System.out.println("下一个工作日: " + nextWorkDay);

使用技巧

技巧 1:链式调用

LocalDate result = date
    .withYear(2030)
    .withMonth(1)
    .withDayOfMonth(1);
// 结果: 2030-01-01

技巧 2:结合 plus()/minus() 使用

// 设置为下个月的第一天
LocalDate nextMonthFirst = date.plusMonths(1).withDayOfMonth(1);

技巧 3:使用 YearMonth 辅助

import java.time.YearMonth;

// 设置为某年某月的最后一天
YearMonth ym = YearMonth.of(2025, 2);
LocalDate febEnd = ym.atEndOfMonth(); // 2025-02-28

常见错误

错误 说明 修复
DateTimeException 设置无效日期,如 2025-02-30 确保日期合法(会自动处理闰年)
UnsupportedTemporalTypeException 使用不支持的 TemporalField 使用 ChronoField 中定义的字段
NullPointerException 调整器为 null 确保 TemporalAdjuster 不为 null
误解不可变性 以为 with() 修改了原对象 记住:必须接收返回值
// 错误示例
LocalDate date = LocalDate.now();
date.withYear(2030); // 忘记接收返回值!
System.out.println(date); // 仍然是当前日期!

注意事项

  1. ⚠️ 不可变性with() 不改变原对象,必须接收返回值。
  2. ⚠️ 自动校正:如果设置的日期无效,会自动调整(异常模式):
    LocalDate.of(2025, 1, 32).withDayOfMonth(32); // 抛异常
    // 但 LocalDate.of(2025, 1, 31).plusDays(1) 会变成 2025-02-01
    

    实际上,withDayOfMonth(32) 在 1月 会抛 DateTimeException,因为 1月没有32日。

  3. ⚠️ 闰年处理:设置 2月29日 时,非闰年会抛异常。
  4. ⚠️ 调整器可重用TemporalAdjusters 的静态方法返回的调整器是线程安全的,可共享。

最佳实践与性能优化

✅ 最佳实践

实践 说明
✅ 优先使用 withYear(), withMonth() 等便捷方法 with(ChronoField.XXX) 更直观
✅ 复杂逻辑使用 TemporalAdjusters 如“本月最后一个周五”
✅ 封装常用调整逻辑 提高代码复用性
✅ 链式调用提升可读性 适合连续修改多个字段

⚙️ 性能优化

  • with() 方法性能极高,为 O(1) 操作。
  • 内置调整器(如 firstDayOfMonth)经过优化,无需担心性能。
  • 自定义调整器避免复杂循环或 I/O 操作。
  • 在循环中可缓存常用的 TemporalAdjuster 实例。

总结

方法类型 示例 适用场景
with(field, value) date.with(ChronoField.YEAR, 2030) 直接设置具体字段值
with(adjuster) date.with(firstDayOfMonth()) 实现复杂日期逻辑
便捷方法 date.withYear(2030) 简单字段修改,代码更清晰

一句话总结LocalDate.with() 是修改日期的“标准方式”,通过不可变模式保证线程安全,结合字段设置和调整器机制,既能简单赋值,又能实现复杂日期运算。