1. Java时间与日期处理:从Date到新时代API
在Java开发中,时间日期处理是每个程序员都无法回避的课题。记得我刚入行时,就曾被SimpleDateFormat的线程安全问题坑过——线上日志突然出现大量乱码时间戳,排查半天才发现是多线程共用了同一个格式化实例。这个经历让我深刻认识到,时间处理远没有表面看起来那么简单。
1.1 传统Date类的局限性
Java最初的日期时间API(java.util.Date)设计存在明显缺陷:
java复制Date date = new Date();
System.out.println(date); // 默认输出格式包含时区信息
主要问题包括:
- 可变性:Date对象创建后仍可被修改,违反不可变原则
- 时区处理混乱:toString()方法使用默认时区,但比较时又忽略时区
- 月份从0开始:与人类直觉相悖,极易导致off-by-one错误
关键教训:永远不要在多线程环境下共享SimpleDateFormat实例,要么每次创建新实例,要么使用ThreadLocal封装。
1.2 Calendar类的改进与不足
为解决Date的问题,Java 1.1引入了Calendar抽象类:
java复制Calendar cal = Calendar.getInstance();
cal.set(2023, Calendar.JUNE, 15); // 月份仍然从0开始
虽然解决了部分问题,但仍有痛点:
- 月份索引问题依旧存在
- 类型不安全:set()方法可以传入任意整数值
- 性能开销:由于需要处理多种日历系统,实例化较慢
1.3 Java 8时间API革命
Java 8引入的java.time包彻底改变了游戏规则。核心类包括:
- Instant:时间戳(精确到纳秒)
- LocalDate/LocalTime/LocalDateTime:不带时区的日期时间
- ZonedDateTime:带时区的完整日期时间
- Period/Duration:时间段计算
典型用法示例:
java复制// 获取当前时间(无时区)
LocalDateTime now = LocalDateTime.now();
// 创建特定日期
LocalDate birthday = LocalDate.of(1990, Month.JUNE, 15);
// 计算两个日期差值
Period period = Period.between(birthday, LocalDate.now());
System.out.println(period.getYears()); // 输出年龄
// 时区转换
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
1.4 日期格式化与解析
DateTimeFormatter替代了SimpleDateFormat:
java复制DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
String formatted = formatter.format(Instant.now());
Instant parsed = Instant.from(formatter.parse("2023-06-15 14:30:00"));
优势包括:
- 线程安全:所有实例都是不可变的
- 严格模式:默认会严格校验日期有效性
- 内置多种ISO标准格式
1.5 实战经验分享
-
数据库交互:JDBC 4.2+支持直接映射java.time类型:
java复制preparedStatement.setObject(1, LocalDateTime.now()); ResultSet rs = statement.executeQuery(); LocalDateTime time = rs.getObject("create_time", LocalDateTime.class); -
JSON序列化:使用Jackson时需要注册JavaTimeModule:
java复制ObjectMapper mapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); -
时区处理黄金法则:
- 存储:统一使用UTC时间戳
- 显示:根据用户偏好转换时区
- 计算:始终使用时区感知类型(ZonedDateTime)
2. 包装类:基本类型的对象外衣
2.1 包装类存在意义
Java的8种基本类型都有对应的包装类:
- byte → Byte
- short → Short
- int → Integer
- long → Long
- float → Float
- double → Double
- char → Character
- boolean → Boolean
主要应用场景:
- 泛型类型参数(如List
) - 需要null值的场合
- 反射操作字段时
- 与对象型API交互(如集合框架)
2.2 自动装箱与拆箱
Java 5引入的语法糖:
java复制Integer a = 100; // 自动装箱(实际调用Integer.valueOf(100))
int b = a; // 自动拆箱(实际调用a.intValue())
但要注意陷阱:
java复制Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true(缓存范围内)
Integer m = 128;
Integer n = 128;
System.out.println(m == n); // false(超出缓存范围)
2.3 数值缓存机制
Java对部分包装类做了缓存优化:
- Integer:-128~127(可通过-XX:AutoBoxCacheMax调整上限)
- Byte/Short/Long:-128~127
- Character:0~127
- Boolean:TRUE/FALSE(全部缓存)
最佳实践:比较包装类对象时,始终使用equals()而非==
2.4 类型转换与工具方法
各包装类提供了丰富的转换方法:
java复制// 字符串转数值
int num = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
// 进制转换
String binary = Integer.toBinaryString(255); // "11111111"
String hex = Integer.toHexString(255); // "ff"
// 无符号转换
long unsigned = Integer.toUnsignedLong(0xFFFFFFFF);
2.5 常见问题排查
-
NPE风险:
java复制Integer count = null; int total = count + 1; // 抛出NullPointerException -
性能考量:
- 高频计算场景避免过度装箱
- 批量操作优先使用基本类型数组
-
特殊值处理:
java复制Double.NaN // 非数字 Double.POSITIVE_INFINITY // 正无穷
3. 正则表达式:文本处理的瑞士军刀
3.1 正则基础语法
Java通过java.util.regex包提供正则支持,核心类:
- Pattern:编译后的正则表达式
- Matcher:匹配操作引擎
基础元字符:
.任意字符(除换行外)\d数字[0-9]\w单词字符[a-zA-Z0-9_]\s空白字符^行首,$行尾
量词:
*0次或多次+1次或多次?0次或1次{n}恰好n次{n,}至少n次
3.2 Java中的正则API
典型使用流程:
java复制Pattern pattern = Pattern.compile("\\d{3}-\\d{4}");
Matcher matcher = pattern.matcher("123-4567");
boolean matches = matcher.matches(); // true
分组捕获示例:
java复制Pattern p = Pattern.compile("(\\d+)-(\\w+)");
Matcher m = p.matcher("123-abc");
if (m.find()) {
System.out.println(m.group(1)); // "123"
System.out.println(m.group(2)); // "abc"
}
3.3 常用正则模式
-
手机号验证(中国):
java复制String regex = "^1[3-9]\\d{9}$"; Pattern.matches(regex, "13800138000"); // true -
邮箱验证:
java复制String emailRegex = "^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"; -
密码强度校验(至少8位,含大小写和数字):
java复制String passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$";
3.4 性能优化技巧
-
预编译Pattern:
java复制// 类静态字段 private static final Pattern PHONE_PATTERN = Pattern.compile("\\d{3}-\\d{4}"); -
避免贪婪匹配:
java复制// 贪婪模式(默认) Pattern.compile("<.*>").matcher("<a><b>").find(); // 匹配整个"<a><b>" // 非贪婪模式 Pattern.compile("<.*?>").matcher("<a><b>").find(); // 只匹配"<a>" -
合理使用边界匹配:
java复制// 查找独立单词"java" Pattern.compile("\\bjava\\b").matcher("javascript is not java").find();
3.5 常见问题排查
-
特殊字符转义:
java复制// 错误示范(未转义点号) Pattern.compile("example.com"); // 正确做法 Pattern.compile("example\\.com"); -
Unicode处理:
java复制// 匹配中文字符 Pattern.compile("[\\u4e00-\\u9fa5]"); -
多行模式:
java复制Pattern.compile("^pattern$", Pattern.MULTILINE);
4. 综合应用案例
4.1 日志时间戳解析
处理混合格式的日志时间:
java复制String log = "[2023-06-15T14:30:00Z] ERROR: System failure";
Pattern logPattern = Pattern.compile("\\[(.*?)\\]");
Matcher m = logPattern.matcher(log);
if (m.find()) {
Instant timestamp = Instant.parse(m.group(1));
System.out.println(timestamp.atZone(ZoneId.of("Asia/Shanghai")));
}
4.2 数据清洗管道
处理CSV中的不规范数据:
java复制String dirtyData = "ID123, John Doe , 42 , 1,500.00";
String[] parts = dirtyData.split("\\s*,\\s*");
// 清洗每个字段
parts[0] = parts[0].replaceAll("[^A-Z0-9]", "");
parts[1] = parts[1].trim();
parts[2] = parts[2].replaceAll("\\D", "");
parts[3] = parts[3].replace(",", "");
4.3 参数校验工具类
封装常用校验逻辑:
java复制public class Validators {
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static boolean isValidPhone(String phone) {
return phone != null && PHONE_PATTERN.matcher(phone).matches();
}
public static boolean isValidDate(String dateStr) {
try {
LocalDate.parse(dateStr, DATE_FORMAT);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
}
4.4 性能敏感场景优化
当处理大量文本时:
java复制// 重用Matcher对象
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("");
List<Integer> numbers = new ArrayList<>();
for (String text : hugeTextList) {
matcher.reset(text);
while (matcher.find()) {
numbers.add(Integer.parseInt(matcher.group()));
}
}
5. 面试常见问题解析
5.1 时间日期相关问题
-
Instant与LocalDateTime区别:
- Instant是时间线上的瞬时点(UTC时区)
- LocalDateTime不含时区信息,只是日期时间的组合
-
如何计算两个日期之间的工作日:
java复制public static long countWorkDays(LocalDate start, LocalDate end) { return start.datesUntil(end.plusDays(1)) .filter(d -> d.getDayOfWeek().getValue() < 6) .count(); }
5.2 包装类相关问题
-
Integer缓存机制原理:
- 使用IntegerCache内部类实现
- 默认范围-128~127,可通过JVM参数调整
- valueOf()会优先返回缓存对象
-
自动装箱的性能影响:
- 每次装箱都会创建新对象(缓存范围内除外)
- 循环体内频繁装箱会导致大量对象创建
5.3 正则表达式问题
-
贪婪模式与懒惰模式区别:
- 贪婪量词:
*,+,{n,} - 懒惰量词:
*?,+?,{n,}? - 贪婪模式会尽可能匹配更多字符
- 贪婪量词:
-
Pattern.compile()的性能消耗:
- 编译正则表达式开销较大
- 应该重用Pattern实例而非重复编译
6. 开发中的经验之谈
6.1 时间处理最佳实践
-
系统边界的时间处理:
- 对外接口:统一使用ISO8601格式字符串
- 数据库存储:根据数据库类型选择TIMESTAMP或DATETIME
- 内部计算:优先使用Instant或LocalDateTime
-
处理用户输入的时间:
java复制public static LocalDate parseUserDate(String input) { List<DateTimeFormatter> formats = Arrays.asList( DateTimeFormatter.ofPattern("yyyy/MM/dd"), DateTimeFormatter.ofPattern("MM-dd-yyyy"), DateTimeFormatter.ISO_LOCAL_DATE ); for (DateTimeFormatter fmt : formats) { try { return LocalDate.parse(input, fmt); } catch (DateTimeParseException ignored) {} } throw new IllegalArgumentException("Invalid date format"); }
6.2 包装类使用建议
-
集合中的数值处理:
java复制// 避免 List<Integer> numbers = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { numbers.add(i); // 自动装箱产生大量对象 } // 推荐(原始类型集合) IntList numbers = new IntArrayList(); // 使用Eclipse Collections等库 -
Optional与包装类:
java复制// 不要这样用! Optional<Integer> optionalInt = Optional.of(42); // 应该直接使用OptionalInt OptionalInt optionalInt = OptionalInt.of(42);
6.3 正则表达式调试技巧
-
可视化调试工具:
- 使用Regex101等在线工具测试正则
- IntelliJ IDEA内置正则调试器
-
复杂正则的分解:
java复制// 验证复杂密码规则 String passwordRules = """ (?=.*[a-z]) # 至少一个小写字母 (?=.*[A-Z]) # 至少一个大写字母 (?=.*\\d) # 至少一个数字 (?=.*[@#$%]) # 至少一个特殊字符 .{8,20} # 长度8-20 """; Pattern.compile(passwordRules, Pattern.COMMENTS); -
性能监控:
- 警惕"灾难性回溯"(catastrophic backtracking)
- 对复杂正则进行性能测试
7. 现代Java中的新特性
7.1 时间API的增强
Java 9新增的方法:
java复制// 日期范围流
LocalDate start = LocalDate.of(2023, 1, 1);
start.datesUntil(LocalDate.of(2023, 1, 5))
.forEach(System.out::println);
// 更精确的时间获取
Instant instant = Instant.now().truncatedTo(ChronoUnit.MICROS);
7.2 模式匹配与switch表达式
Java 17+对包装类的增强处理:
java复制// 旧方式
if (obj instanceof Integer) {
int value = (Integer) obj;
// ...
}
// 新模式匹配
if (obj instanceof Integer value) {
System.out.println(value + 1);
}
// switch表达式
String type = switch (obj) {
case Integer i -> "Integer: " + i;
case Double d -> "Double: " + d;
default -> "Unknown";
};
7.3 文本块与正则表达式
Java 15引入的文本块(text blocks)让复杂正则更易读:
java复制String regex = """
(?x) # 启用注释模式
\\b # 单词边界
(0x[0-9a-fA-F]+) # 十六进制数
| # 或
(\\d+\\.?\\d*|\\.\\d+) # 十进制数
\\b # 单词边界
""";
8. 工具与库推荐
8.1 时间处理库
-
Joda-Time(历史选择):
- Java 8前的最佳替代方案
- 现在推荐直接使用java.time
-
ThreeTen-Extra:
- 扩展java.time的额外功能
- 提供Interval、YearQuarter等有用类
8.2 正则工具
-
RegexTester:
- IntelliJ IDEA内置插件
- 实时高亮匹配结果
-
jRegex:
- 高性能正则库
- 支持命名字符类等扩展语法
8.3 包装类工具
-
Guava Primitives:
java复制// 基本类型数组与集合转换 int[] array = Ints.toArray(List.of(1, 2, 3)); -
Apache Commons Lang:
java复制// 安全的数值转换 Integer num = NumberUtils.toInt("123", 0);
9. 性能对比与基准测试
9.1 时间API性能
JMH测试结果(纳秒/操作):
| 操作 | java.util.Date | java.time |
|---|---|---|
| 实例创建 | 15 | 12 |
| 格式化 | 120 | 85 |
| 解析 | 150 | 95 |
| 日期计算 | 22 | 18 |
9.2 包装类开销
内存占用对比:
| 类型 | 内存占用(字节) |
|---|---|
| int | 4 |
| Integer | 16 (对象头12+值4) |
9.3 正则表达式优化
不同写法的性能差异(匹配百万次耗时ms):
| 模式 | 预编译 | 未预编译 |
|---|---|---|
| 简单模式(\d+) | 120 | 450 |
| 复杂模式(带分组) | 380 | 2200 |
| 灾难性回溯模式 | 超时 | 超时 |
10. 异常处理与调试
10.1 时间日期异常
-
DateTimeParseException:
- 提供详细的错误位置信息
- 包含无法解析的文本值
-
DateTimeException:
- 非法日期操作时抛出
- 如2月30日这种无效日期
处理建议:
java复制try {
LocalDate date = LocalDate.parse(input);
} catch (DateTimeParseException e) {
System.err.println("Error at position " + e.getErrorIndex());
System.err.println("Invalid value: " + e.getParsedString());
}
10.2 包装类异常
-
NumberFormatException:
java复制try { int num = Integer.parseInt("123a"); } catch (NumberFormatException e) { System.err.println("Invalid number: " + e.getMessage()); } -
NullPointerException:
- 自动拆箱时可能发生
- 使用OptionalInt等避免
10.3 正则表达式异常
- PatternSyntaxException:
- 包含错误位置和描述
- 打印pattern有助于调试
错误处理示例:
java复制try {
Pattern.compile("invalid[pattern");
} catch (PatternSyntaxException e) {
System.err.println("Error at index " + e.getIndex());
System.err.println("Description: " + e.getDescription());
System.err.println("Pattern: " + e.getPattern());
}
11. 代码风格与可读性
11.1 时间处理代码规范
-
常量命名:
java复制// 好 private static final DateTimeFormatter ISO_DATE = DateTimeFormatter.ISO_LOCAL_DATE; // 不好 private static final DateTimeFormatter fmt1 = ...; -
方法链处理:
java复制ZonedDateTime nowInTokyo = Instant.now() .atZone(ZoneId.of("Asia/Tokyo")) .truncatedTo(ChronoUnit.HOURS) .plusHours(1);
11.2 包装类代码规范
-
显式转换优于隐式:
java复制// 好 int value = integerObj.intValue(); // 不够明确 int value = integerObj; -
null安全处理:
java复制// 使用Java 8 Optional Optional.ofNullable(integerObj).orElse(0);
11.3 正则表达式代码规范
-
复杂正则的注释:
java复制Pattern.compile(""" ^(?=.*[a-z]) # 至少一个小写字母 (?=.*[A-Z]) # 至少一个大写字母 (?=.*\\d) # 至少一个数字 .{8,}$ # 至少8个字符 """, Pattern.COMMENTS); -
合理拆分复杂模式:
java复制String usernameRegex = "[a-zA-Z]\\w{3,15}"; String domainRegex = "([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}"; Pattern emailPattern = Pattern.compile(usernameRegex + "@" + domainRegex);
12. 跨平台注意事项
12.1 时区数据库差异
不同JDK版本的时区数据可能不同:
java复制// 检查可用时区
Set<String> zones = ZoneId.getAvailableZoneIds();
// 建议明确指定时区数据版本
System.setProperty("java.time.zone.DefaultZoneRulesProvider", "TZDB2022a");
12.2 本地化差异
-
数字格式:
java复制// 德国使用逗号作为小数点 NumberFormat fmt = NumberFormat.getInstance(Locale.GERMANY); double d = fmt.parse("1,234").doubleValue(); // 1.234 -
日期格式:
java复制DateTimeFormatter fmt = DateTimeFormatter .ofLocalizedDate(FormatStyle.FULL) .withLocale(Locale.JAPANESE); String date = fmt.format(LocalDate.now()); // "2023年6月15日"
12.3 正则表达式引擎差异
注意不同语言间的正则语法差异:
- Java不支持递归匹配(?R)
- 部分特性需要开启特殊标志:
java复制Pattern.compile("pattern", Pattern.UNICODE_CHARACTER_CLASS);
13. 安全考量
13.1 时间处理安全
-
注入攻击防护:
java复制// 不安全 String sql = "SELECT * FROM logs WHERE time > '" + userInput + "'"; // 安全做法 Instant time = Instant.parse(userInput); // 先验证 PreparedStatement stmt = conn.prepareStatement( "SELECT * FROM logs WHERE time > ?"); stmt.setObject(1, time); -
时钟偏移问题:
java复制// 依赖系统时钟的代码应该可测试 public class OrderService { private Clock clock = Clock.systemDefaultZone(); public void setClock(Clock clock) { // 测试时注入固定时钟 this.clock = clock; } public Order createOrder() { Instant now = Instant.now(clock); // ... } }
13.2 数值处理安全
-
整数溢出防护:
java复制// 不安全 int result = a + b; // 安全做法 int result = Math.addExact(a, b); // 溢出时抛出ArithmeticException -
BigDecimal使用:
java复制// 金融计算必须使用BigDecimal BigDecimal total = new BigDecimal("0.1") .add(new BigDecimal("0.2")); // 0.3(正确)
13.3 正则表达式安全
-
ReDoS攻击防护:
- 避免使用用户输入作为正则模式
- 对复杂模式设置超时:
java复制Future<Boolean> future = executor.submit(() -> pattern.matcher(input).matches()); try { return future.get(1, TimeUnit.SECONDS); } catch (TimeoutException e) { future.cancel(true); throw new RuntimeException("Regex timeout"); }
-
敏感信息过滤:
java复制// 信用卡号脱敏 String cardNumber = "4111111111111111"; String masked = cardNumber.replaceAll("\\b(\\d{4})(\\d{8})(\\d{4})\\b", "$1********$3");
14. 测试策略
14.1 时间处理测试
-
固定时钟测试:
java复制@Test void testExpiration() { Clock fixedClock = Clock.fixed( Instant.parse("2023-01-01T00:00:00Z"), ZoneId.of("UTC")); LicenseService service = new LicenseService(fixedClock); assertFalse(service.isExpired()); } -
时区边界测试:
java复制@ParameterizedTest @ValueSource(strings = {"UTC", "Asia/Shanghai", "America/New_York"}) void testTimezoneConversion(String zoneId) { ZoneId zone = ZoneId.of(zoneId); // 测试代码 }
14.2 包装类测试
-
null值处理测试:
java复制@Test void testNullInteger() { assertThrows(NullPointerException.class, () -> { Integer num = null; int value = num; // 自动拆箱 }); } -
数值边界测试:
java复制@Test void testIntegerCache() { assertSame(Integer.valueOf(127), Integer.valueOf(127)); assertNotSame(Integer.valueOf(128), Integer.valueOf(128)); }
14.3 正则表达式测试
-
模式覆盖测试:
java复制@ParameterizedTest @CsvSource({ "abc@example.com, true", "invalid_email, false" }) void testEmailPattern(String input, boolean expected) { assertEquals(expected, EMAIL_PATTERN.matcher(input).matches()); } -
性能测试:
java复制@Test void testRegexPerformance() { String input = "a".repeat(1000) + "b"; assertTimeout(Duration.ofMillis(100), () -> { Pattern.compile("(a+)+b").matcher(input).matches(); }); }
15. 调试技巧
15.1 时间调试
-
时区问题排查:
java复制System.out.println("Default zone: " + ZoneId.systemDefault()); System.out.println("All zones: " + ZoneId.getAvailableZoneIds()); -
时间戳转换:
java复制Instant now = Instant.now(); System.out.println("Epoch millis: " + now.toEpochMilli()); System.out.println("UTC: " + now.atZone(ZoneOffset.UTC));
15.2 包装类调试
-
查看自动装箱:
java复制// 编译后查看字节码:javap -c YourClass.class Integer num = 42; // 实际调用Integer.valueOf(42) -
内存分析:
- 使用JVisualVM等工具查看Integer对象数量
- 检查是否有不必要的装箱操作
15.3 正则表达式调试
-
详细匹配信息:
java复制Matcher matcher = pattern.matcher(input); while (matcher.find()) { System.out.println("Match: " + matcher.group()); for (int i = 0; i <= matcher.groupCount(); i++) { System.out.println("Group " + i + ": " + matcher.group(i)); } } -
模式分解:
- 将复杂正则拆分为多个简单模式
- 使用(?x)标志添加注释
16. 版本兼容性
16.1 时间API兼容
-
Java 8+迁移:
- 新项目直接使用java.time
- 旧项目逐步替换Date/Calendar
-
与旧API互操作:
java复制// Date转Instant Instant instant = new Date().toInstant(); // Instant转Date Date date = Date.from(Instant.now());
16.2 包装类变化
-
Java 5自动装箱:
- 旧代码可能需要显式转换
- 注意NPE风险增加
-
Java 8增强:
java复制// 无符号操作 Integer.compareUnsigned(a, b); Long.divideUnsigned(a, b);
16.3 正则表达式演进
-
Java 9新增:
java复制// 命名捕获组 Pattern.compile("(?<area>\\d{3})-(?<number>\\d{4})"); Matcher m = pattern.matcher("123-4567"); if (m.matches()) { String area = m.group("area"); // "123" } -
Java 11增强:
java复制// asMatchPredicate()方法 Pattern.compile("\\d+").asMatchPredicate().test("123"); // true
17. 内存与性能优化
17.1 时间对象复用
-
不可变对象池:
java复制// 常用日期可以缓存 private static final LocalDate START_DATE = LocalDate.of(2020, 1, 1); -
避免频繁创建格式化器:
java复制// 类静态字段 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
17.2 包装类优化
-
原始类型集合:
java复制// 使用Eclipse Collections或FastUtil IntList list = IntLists.mutable.empty(); list.add(1); -
数组替代集合:
java复制// 性能敏感场景 int[] values = new int[1000];
17.3 正则表达式优化
-
预编译模式:
java复制// 应用启动时初始化 public class Patterns { public static final Pattern EMAIL = Pattern.compile("..."); } -
简化复杂模式:
java复制// 使用多个简单正则替代单个复杂正则 boolean isValid = input != null && Patterns.USERNAME.matcher(input).matches() && input.length() >= 4;
18. 设计模式应用
18.1 时间处理模式
-
策略模式:
java复制public interface DateParser { LocalDate parse(String input); } public class IsoDateParser implements DateParser { ... } public class CustomDateParser implements DateParser { ... } -
工厂模式:
java复制public class DateTimeFormatterFactory { public static DateTimeFormatter forPattern(String pattern) { return DateTimeFormatter.ofPattern(pattern) .withZone(ZoneId.systemDefault()); } }
18.2 包装类模式
-
享元模式:
- Integer.valueOf()就是典型实现
- 缓存常用对象减少创建开销
-
适配器模式:
java复制public class IntAdapter { private final int value; public IntAdapter(int value) { this.value = value; } public Integer asInteger() { return value; } public String asString() { return String.valueOf(value); } }
18.3 正则表达式模式
-
解释器模式:
- 正则引擎本身就是解释器模式的实现
- 将模式字符串解释为匹配规则
-
构建器模式:
java复制public class RegexBuilder { private final StringBuilder pattern = new StringBuilder(); public RegexBuilder startsWith() { pattern.append("^"); return this; } public RegexBuilder digit() { pattern.append("\\d"); return this; } public Pattern build() { return Pattern.compile(pattern.toString()); } }
19. 领域特定应用
19.1 金融领域
-
精确计算:
java复制BigDecimal principal = new BigDecimal("10000.00"); BigDecimal rate = new BigDecimal("0.05"); BigDecimal interest = principal.multiply(rate) .setScale(2, RoundingMode.HALF_UP); -
工作日计算:
java复制public boolean isBusinessDay(LocalDate date) { return date.getDayOfWeek() != SATURDAY && date.getDayOfWeek() != SUNDAY && !holidays.contains(date); }
19.2 电商领域
-
促销时间校验:
java复制public boolean isPromotionActive(Instant now) { return !now.isBefore(promoStart) && !now.isAfter(promoEnd); } -
订单号生成:
java复制public String generateOrderId() { return DateTimeFormatter.ofPattern("yyyyMMddHHmmss") .format(LocalDateTime.now()) + RandomStringUtils.randomNumeric(6); }
19.3 日志分析
-
日志时间提取:
java复制Pattern logPattern = Pattern.compile( "\\[(?<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})\\]"); Matcher m = logPattern.matcher(logLine); if (m.find()) { LocalDateTime time = LocalDateTime.parse( m.group("timestamp"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } -
异常堆栈过滤:
java复制
