基于 Hutool 和 OpenCSV 的日历记事本系统开发

基于 Hutool 和 OpenCSV 的日历记事本系统开发实践

一、项目概述

本文记录一个基于 Java 的日历记事本系统开发过程,主要解决阳历与阴历日期管理、事件存储与查询、异常处理等问题,涉及 Hutool 工具库、OpenCSV 文件解析及设计模式的应用。

  • 核心库版本
    • Hutool:5.8.38(日期处理与工具类)
    • OpenCSV:5.7.0(CSV 文件读写)
    • Apache Commons Lang3:3.17.0(通用工具类,OpenCSV 依赖)

二、采用的设计模式

1. 单例模式(Singleton)

  • 场景NotepadManager类使用单例模式确保全局唯一实例。

  • 实现

    1
    2
    3
    4
    5
    6
    7
    8
    private static NotepadManager instance;
    private NotepadManager() {} // 私有化构造方法
    public static synchronized NotepadManager getInstance() {
    if (instance == null) {
    instance = new NotepadManager();
    }
    return instance;
    }

2. 策略模式(Strategy)

  • 场景:定义StorageStrategy接口,实现类CsvStorageStrategy负责 CSV 文件存储逻辑。

  • 接口定义

    1
    2
    3
    4
    public interface StorageStrategy {
    List<Event> loadEvents();
    void saveEvents(List<Event> events);
    }
  • 优势:方便扩展其他存储方式(如 JSON、数据库)。

3. 观察者模式(Observer)

  • 场景EventNotifier类管理观察者,事件变更时(添加 / 修改 / 删除)触发通知。

  • 核心代码

    1
    2
    3
    4
    5
    6
    7
    private List<Observer> observers = new ArrayList<>();
    public void registerObserver(Observer observer) {
    observers.add(observer);
    }
    public void notifyObservers(String message) {
    observers.forEach(o -> o.update(message));
    }

三、项目目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
EventNotepad
├─ JRE System Library [JavaSE-22] # Java 22运行时环境
├─ src
│ └─ com.calendar.notepad
│ ├─ Main.java # 主程序入口(控制台交互)
│ ├─ lunar
│ │ └─ LunarUtil.java # 阴历工具类(阳历转阴历、农历键生成)
│ ├─ model
│ │ └─ Event.java # 事件实体(标题、日期、内容)
│ ├─ observer
│ │ ├─ EventNotifier.java # 事件通知器(观察者模式,管理事件变更通知)
│ │ └─ Observer.java # 观察者接口(定义更新方法)
│ ├─ service
│ │ └─ NotepadManager.java # 核心管理类(单例,事件增删改查、存储策略、阴历映射)
│ └─ strategy
│ ├─ CsvStorageStrategy.java # CSV存储实现(读写events.csv)
│ └─ StorageStrategy.java # 存储策略接口(可扩展JSON、数据库等存储)
├─ Referenced Libraries # IDE自动管理的依赖(如JRE库)
├─ lib # 第三方库(手动导入)
│ ├─ commons-lang3-3.17.0.jar # Apache Commons Lang3(OpenCSV依赖)
│ ├─ hutool-all-5.8.38.jar # Hutool(农历计算、日期工具)
│ └─ opencsv-5.7.0.jar # OpenCSV(CSV文件读写)
└─ events.csv # 事件数据存储文件(CSV格式)

四、阳历转阴历功能实现

1. 依赖配置(Hutool 库)

注:本次代码编程时,考虑到客户可能不会maven,使用的是本地导入的Hutool。

1
2
3
4
5
6
<!-- Maven依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.38</version>
</dependency>

2. 工具类LunarUtil实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.calendar.notepad.lunar;

import cn.hutool.core.date.ChineseDate;
import java.util.Date;

public class LunarUtil {
/**
* 阳历转阴历字符串(如:2025年四月初八)
*/
public static String solarToLunarString(Date solarDate) {
if (solarDate == null) return "无效日期";
try {
ChineseDate lunar = new ChineseDate(solarDate);
return lunar.getChineseYear() + "年"
+ lunar.getChineseMonthName()
+ lunar.getChineseDay();
} catch (Exception e) {
System.err.println("阴历转换失败: " + e.getMessage());
return "转换失败";
}
}

/**
* 生成阴历查询键(年-月-日)
*/
public static String getLunarKey(Date solarDate) {
ChineseDate lunar = new ChineseDate(solarDate);
return lunar.getChineseYear() + "-"
+ lunar.getMonth() + "-"
+ lunar.getChineseDay();
}
}

3. 业务逻辑集成

  • 1
    NotepadManager

    中维护阴历事件映射:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private Map<String, List<Event>> lunarEventMap = new HashMap<>();

    private void updateLunarEventMap(Event event) {
    String key = LunarUtil.getLunarKey(event.getDate());
    lunarEventMap.computeIfAbsent(key, k -> new ArrayList<>()).add(event);
    }

    public List<Event> queryByLunarDate(Date solarDate) {
    String key = LunarUtil.getLunarKey(solarDate);
    return lunarEventMap.getOrDefault(key, new ArrayList<>());
    }

五、Eclipse 导入本地 JAR 包教程

1. 下载 JAR 包

  • OpenCSV:从Maven 中央仓库下载opencsv-5.7.0.jar
  • Apache Commons Lang3:下载commons-lang3-3.12.0.jar

2. 添加到项目

步骤 1:创建lib目录

在 Eclipse 项目根目录右键 → New Folder → 命名为lib

步骤 2:复制 JAR 包

将下载的 JAR 文件复制到lib目录中。

步骤 3:配置构建路径

  1. 右键项目 → Build PathConfigure Build Path
  2. Libraries选项卡中,点击Add JARs
  3. 选择lib目录下的所有 JAR 包,点击OK

验证

  • 导入语句生效(如import com.opencsv.CSVReader;)。
  • 项目无红叉错误。

六、常见问题及解决方案

1. 依赖缺失问题

错误:CSVReader cannot be resolved to a type

  • 原因:未添加 OpenCSV 依赖。
  • 解决:手动导入 JAR 包或添加 Maven 依赖。

错误:NoClassDefFoundError: org/apache/commons/lang3/ObjectUtils

  • 原因:OpenCSV 依赖 Apache Commons Lang3。
  • 解决:添加commons-lang3依赖。

2. 日期解析异常

错误:ArrayIndexOutOfBoundsException

  • 原因:Hutool 处理非法日期(如null或超出范围的日期)。
  • 解决
    • CsvStorageStrategy中验证日期格式(yyyy-MM-dd)。
    • LunarUtil中添加日期范围检查(如 1900-2100 年)。

3. IDE 缓存问题

  • 错误:类已导入但仍提示未找到(如Date cannot be resolved)。
  • 解决
    • Eclipse:Project → Clean
    • IntelliJ IDEA:File → Invalidate Caches / Restart

七、源码展示

Main.java # 主程序入口(控制台交互)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package com.calendar.notepad;

import com.calendar.notepad.lunar.LunarUtil;
import com.calendar.notepad.model.Event;
import com.calendar.notepad.observer.Observer;
import com.calendar.notepad.service.NotepadManager;
import com.calendar.notepad.strategy.CsvStorageStrategy;
import com.calendar.notepad.strategy.StorageStrategy;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

/**
* 主程序入口(使用Hutool优化)
*/
public class Main {
public static void main(String[] args) {
NotepadManager manager = NotepadManager.getInstance();
StorageStrategy csvStrategy = new CsvStorageStrategy();
manager.setStorageStrategy(csvStrategy);
manager.loadEvents();
manager.registerObserver(new ConsoleObserver());

Scanner scanner = new Scanner(System.in);

while (true) {
System.out.println("\n===== 日历记事本系统 =====");
System.out.println("1. 添加事件");
System.out.println("2. 修改事件");
System.out.println("3. 删除事件");
System.out.println("4. 查看事件列表");
System.out.println("5. 按阳历查询事件");
System.out.println("6. 按阴历查询事件");
System.out.println("7. 查看当月日期范围");
System.out.println("0. 退出");
System.out.print("请选择操作: ");

try {
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符

switch (choice) {
case 1:
System.out.print("请输入事件标题: ");
String title = scanner.nextLine();
System.out.print("请输入事件日期 (yyyy-MM-dd): ");
String dateStr = scanner.nextLine();
System.out.print("请输入事件内容: ");
String content = scanner.nextLine();

try {
Date date = DateUtil.parse(dateStr);
manager.addEvent(new Event(title, date, content));
System.out.println("事件添加成功!");
} catch (Exception e) {
System.out.println("日期格式错误! 请使用 yyyy-MM-dd 格式");
}
break;

case 2:
System.out.print("请输入要修改的事件标题: ");
String oldTitle = scanner.nextLine();
Event oldEvent = findEventByTitle(oldTitle, manager.getEventList());

if (oldEvent != null) {
System.out.print("请输入新的事件标题 (留空则不修改): ");
String newTitle = scanner.nextLine();
if (!newTitle.isEmpty()) oldEvent.setTitle(newTitle);

System.out.print("请输入新的事件日期 (yyyy-MM-dd, 留空则不修改): ");
String newDateStr = scanner.nextLine();
if (!newDateStr.isEmpty()) {
try {
Date newDate = DateUtil.parse(newDateStr);
oldEvent.setDate(newDate);
} catch (Exception e) {
System.out.println("日期格式错误!");
}
}

System.out.print("请输入新的事件内容 (留空则不修改): ");
String newContent = scanner.nextLine();
if (!newContent.isEmpty()) oldEvent.setContent(newContent);

manager.updateEvent(oldEvent, oldEvent);
System.out.println("事件修改成功!");
} else {
System.out.println("未找到该事件!");
}
break;

case 3:
System.out.print("请输入要删除的事件标题: ");
String deleteTitle = scanner.nextLine();
Event eventToDelete = findEventByTitle(deleteTitle, manager.getEventList());

if (eventToDelete != null) {
manager.deleteEvent(eventToDelete);
System.out.println("事件删除成功!");
} else {
System.out.println("未找到该事件!");
}
break;

case 4:
System.out.println("\n===== 事件列表 =====");
List<Event> allEvents = manager.getEventList();
if (allEvents.isEmpty()) {
System.out.println("暂无事件记录!");
} else {
for (Event event : allEvents) {
String lunarDate = LunarUtil.solarToLunarString(event.getDate());
System.out.println(event + " (阴历: " + lunarDate + ")");
}
}
break;

case 5:
System.out.print("请输入要查询的阳历日期 (yyyy-MM-dd): ");
String queryDateStr = scanner.nextLine();
try {
Date queryDate = DateUtil.parse(queryDateStr);
List<Event> eventsOnDate = manager.queryBySolarDate(queryDate);
printEvents("阳历 " + queryDateStr, eventsOnDate);
} catch (Exception e) {
System.out.println("日期格式错误!");
}
break;

case 6:
System.out.println("\n===== 按阴历查询事件 =====");
System.out.println("方式1: 输入阳历日期自动转换为阴历");
System.out.println("方式2: 直接输入阴历信息");
System.out.print("请选择查询方式 (1/2): ");
int queryType = scanner.nextInt();
scanner.nextLine(); // 消耗换行符

if (queryType == 1) {
System.out.print("请输入阳历日期 (yyyy-MM-dd): ");
String solarDateStr = scanner.nextLine();
try {
Date solarDate = DateUtil.parse(solarDateStr);
List<Event> events = manager.queryByLunarDate(solarDate);
String lunarDateStr = LunarUtil.solarToLunarString(solarDate);
printEvents("阴历 " + lunarDateStr, events);
} catch (Exception e) {
System.out.println("日期格式错误!");
}
} else if (queryType == 2) {
System.out.print("请输入阴历年份: ");
int lunarYear = scanner.nextInt();
System.out.print("请输入阴历月份: ");
int lunarMonth = scanner.nextInt();
System.out.print("请输入阴历日期 (如: 初一): ");
String lunarDay = scanner.next();
scanner.nextLine(); // 消耗换行符

List<Event> events = manager.queryByLunarDate(lunarYear, lunarMonth, lunarDay);
printEvents("阴历 " + lunarYear + "年" + lunarMonth + "月" + lunarDay, events);
} else {
System.out.println("无效的选择!");
}
break;

case 7:
Date today = new Date();
String lunarToday = LunarUtil.getFullLunarInfo(today);
System.out.println("\n===== 当月日期范围 =====");
System.out.println("今日阳历日期: " + DateUtil.format(today, "yyyy-MM-dd"));
System.out.println("今日阴历日期: " + lunarToday);
break;

case 0:
System.out.println("感谢使用日历记事本系统,再见!");
scanner.close();
System.exit(0);

default:
System.out.println("无效的选择,请重新输入!");
}
} catch (java.util.InputMismatchException e) {
System.out.println("输入无效,请输入数字!");
scanner.nextLine(); // 清除错误输入
}
}
}

private static Event findEventByTitle(String title, List<Event> events) {
for (Event event : events) {
if (event.getTitle().equals(title)) {
return event;
}
}
return null;
}

private static void printEvents(String dateDescription, List<Event> events) {
System.out.println("\n===== " + dateDescription + " 的事件 =====");
if (events.isEmpty()) {
System.out.println("该日期没有事件记录!");
} else {
events.forEach(System.out::println);
}
}

static class ConsoleObserver implements Observer {
@Override
public void update(String message) {
System.out.println("[通知] " + message);
}
}
}

LunarUtil.java # 阴历工具类(阳历转阴历、农历键生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.calendar.notepad.lunar;

import cn.hutool.core.date.ChineseDate;
import cn.hutool.core.date.DateUtil;
import java.util.Date;

public class LunarUtil {
/**
* 阳历转阴历字符串(如:2025年四月初八)
*/
public static String solarToLunarString(Date solarDate) {
ChineseDate lunar = new ChineseDate(solarDate);
return lunar.getChineseYear() + "年" +
lunar.getChineseMonthName() +
lunar.getChineseDay();
}

/**
* 获取阴历年份
*/
public static int getLunarYear(Date solarDate) {
return new ChineseDate(solarDate).getChineseYear();
}

/**
* 获取阴历月份(数字,1-12)
*/
public static int getLunarMonth(Date solarDate) {
// 注意:Hutool的getChineseMonth()返回的是中文月份名(如"正月")
// 这里需要使用getMonth()获取数字月份(1-12)
return new ChineseDate(solarDate).getMonth();
}

/**
* 获取阴历日期(1-30)
*/
public static String getLunarDay(Date solarDate) {
return new ChineseDate(solarDate).getChineseDay();
}

/**
* 获取阴历关键信息(用于查询)
*/
public static String getLunarKey(Date solarDate) {
ChineseDate lunar = new ChineseDate(solarDate);
return lunar.getChineseYear() + "-" +
lunar.getMonth() + "-" +
lunar.getChineseDay();
}

/**
* 获取完整阴历描述(带节日)
*/
public static String getFullLunarInfo(Date solarDate) {
ChineseDate lunar = new ChineseDate(solarDate);
String festivals = lunar.getFestivals();
return lunar.toString() +
(festivals.isEmpty() ? "" : " (" + festivals + ")");
}
}

Event.java # 事件实体(标题、日期、内容)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.calendar.notepad.model;

import com.calendar.notepad.lunar.LunarUtil;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
/**
* 事件实体类,包含事件的基本属性
*/
public class Event {
private String title; // 事件标题
private Date date; // 事件日期
private String content; // 事件内容

public Event(String title, Date date, String content) {
this.title = title;
this.date = date;
this.content = content;
}

// Getters and Setters
public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

@Override
public String toString() {
return "Event{title='" + title + "', date=" +
DateUtil.format(date, "yyyy-MM-dd") +
", lunarDate=" + LunarUtil.solarToLunarString(date) +
", content='" + content + "'}";
}
}

EventNotifier.java # 事件通知器(观察者模式,管理事件变更通知)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.calendar.notepad.observer;

import java.util.ArrayList;
import java.util.List;

/**
* 事件通知器(主题)
*/
public class EventNotifier {
private List<Observer> observers = new ArrayList<>();

public void registerObserver(Observer observer) {
observers.add(observer);
}

public void removeObserver(Observer observer) {
observers.remove(observer);
}

public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}

Observer.java # 观察者接口(定义更新方法)

1
2
3
4
5
6
7
8
package com.calendar.notepad.observer;

/**
* 观察者接口(观察者模式)
*/
public interface Observer {
void update(String message);
}

NotepadManager.java # 核心管理类(单例,事件增删改查、存储策略、阴历映射)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.calendar.notepad.service;

import com.calendar.notepad.lunar.LunarUtil;
import com.calendar.notepad.model.Event;
import com.calendar.notepad.observer.EventNotifier;
import com.calendar.notepad.strategy.StorageStrategy;
import java.util.*;

/**
* 记事本管理类(使用Hutool优化)
*/
public class NotepadManager {
private static NotepadManager instance;
private List<Event> eventList;
private Map<String, List<Event>> lunarEventMap;
private StorageStrategy storageStrategy;
private EventNotifier eventNotifier;

private NotepadManager() {
eventList = new ArrayList<>();
eventNotifier = new EventNotifier();
lunarEventMap = new HashMap<>();
}

public static synchronized NotepadManager getInstance() {
if (instance == null) {
instance = new NotepadManager();
}
return instance;
}

public void setStorageStrategy(StorageStrategy strategy) {
this.storageStrategy = strategy;
}

public void addEvent(Event event) {
eventList.add(event);
updateLunarEventMap(event);
eventNotifier.notifyObservers("添加了事件: " + event.getTitle());
if (storageStrategy != null) {
storageStrategy.saveEvents(eventList);
}
}

public void updateEvent(Event oldEvent, Event newEvent) {
int index = eventList.indexOf(oldEvent);
if (index != -1) {
removeFromLunarEventMap(oldEvent);
eventList.set(index, newEvent);
updateLunarEventMap(newEvent);
eventNotifier.notifyObservers("更新了事件: " + oldEvent.getTitle());
if (storageStrategy != null) {
storageStrategy.saveEvents(eventList);
}
}
}

public void deleteEvent(Event event) {
eventList.remove(event);
removeFromLunarEventMap(event);
eventNotifier.notifyObservers("删除了事件: " + event.getTitle());
if (storageStrategy != null) {
storageStrategy.saveEvents(eventList);
}
}

public List<Event> getEventList() {
return eventList;
}

public List<Event> queryBySolarDate(Date date) {
List<Event> result = new ArrayList<>();
for (Event event : eventList) {
if (event.getDate().equals(date)) {
result.add(event);
}
}
return result;
}

// 通过阳历日期转换为阴历查询
public List<Event> queryByLunarDate(Date solarDate) {
String key = LunarUtil.getLunarKey(solarDate);
return lunarEventMap.getOrDefault(key, new ArrayList<>());
}

// 直接通过阴历信息查询
public List<Event> queryByLunarDate(int lunarYear, int lunarMonth, String lunarDay) {
String key = lunarYear + "-" + lunarMonth + "-" + lunarDay;
return lunarEventMap.getOrDefault(key, new ArrayList<>());
}

public void registerObserver(com.calendar.notepad.observer.Observer observer) {
eventNotifier.registerObserver(observer);
}

public void removeObserver(com.calendar.notepad.observer.Observer observer) {
eventNotifier.removeObserver(observer);
}

public void loadEvents() {
if (storageStrategy != null) {
this.eventList = storageStrategy.loadEvents();
rebuildLunarEventMap();
}
}

private void updateLunarEventMap(Event event) {
String key = LunarUtil.getLunarKey(event.getDate());
lunarEventMap.computeIfAbsent(key, k -> new ArrayList<>()).add(event);
}

private void removeFromLunarEventMap(Event event) {
String key = LunarUtil.getLunarKey(event.getDate());
List<Event> events = lunarEventMap.get(key);
if (events != null) {
events.remove(event);
if (events.isEmpty()) {
lunarEventMap.remove(key);
}
}
}

private void rebuildLunarEventMap() {
lunarEventMap.clear();
for (Event event : eventList) {
updateLunarEventMap(event);
}
}
}

CsvStorageStrategy.java # CSV存储实现(读写events.csv)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.calendar.notepad.strategy;

import com.calendar.notepad.model.Event;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CsvStorageStrategy implements StorageStrategy {
private static final String FILE_PATH = "events.csv";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

@Override
public List<Event> loadEvents() {
List<Event> events = new ArrayList<>();
try (CSVReader reader = new CSVReader(new FileReader(FILE_PATH))) {
String[] nextLine;
while ((nextLine = reader.readNext()) != null) {
if (nextLine.length == 3) {
try {
Date date = DATE_FORMAT.parse(nextLine[1]);
events.add(new Event(nextLine[0], date, nextLine[2]));
} catch (ParseException e) {
System.err.println("无效日期格式: " + nextLine[1]);
}
}
}
} catch (FileNotFoundException e) {
// 文件不存在时返回空列表
} catch (CsvValidationException e) {
System.err.println("CSV格式验证失败: " + e.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
return events;
}

@Override
public void saveEvents(List<Event> events) {
try (CSVWriter writer = new CSVWriter(new FileWriter(FILE_PATH))) {
for (Event event : events) {
String[] line = {
event.getTitle(),
DATE_FORMAT.format(event.getDate()),
event.getContent()
};
writer.writeNext(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

StorageStrategy.java # 存储策略接口(可扩展JSON、数据库等存储)

1
2
3
4
5
6
7
8
9
10
11
12
package com.calendar.notepad.strategy;

import com.calendar.notepad.model.Event;
import java.util.List;

/**
* 存储策略接口(策略模式)
*/
public interface StorageStrategy {
void saveEvents(List<Event> events);
List<Event> loadEvents();
}

八、总结

本项目通过 Hutool 简化日期处理,OpenCSV 实现数据持久化,并结合设计模式提升代码可维护性。核心关键点包括:

  • 单例模式确保全局唯一实例。
  • 策略模式解耦存储逻辑。
  • Hutool 的ChineseDate实现阳历转阴历。
  • 多层防御机制处理非法日期(解析时验证、工具类过滤、加载后清理)。
    • 提供的代码并没有优化数据

通过合理使用工具库和设计模式,有效提升了开发效率和代码健壮性。


基于 Hutool 和 OpenCSV 的日历记事本系统开发
http://example.com/2025/05/25/基于-Hutool-和-OpenCSV-的日历记事本系统开发/
作者
John Doe
发布于
2025年5月25日
许可协议