java快速学习速查(4)

尽快吧,这个毕竟得快点做完,本章是初级部分最后一篇了
主要内容为数组,日期时间,正则表达式,方法,构造方法,Stream和File和IO,scanner类,异常处理

Java 数组全面解析

数组是 Java 中存储固定大小同类型元素的数据结构。下面我将系统地讲解 Java 数组的各种功能和使用场景。

一、数组基础

1. 声明和初始化数组

1
2
3
4
5
6
7
8
// 声明数组的两种方式
int[] numbers1; // 推荐方式
int numbers2[]; // C风格,不推荐

// 创建数组的三种方式
int[] arr1 = new int[5]; // 长度为5的数组,元素初始为0
int[] arr2 = {1, 2, 3, 4, 5}; // 直接初始化
int[] arr3 = new int[]{1, 2, 3}; // 匿名数组初始化

2. 访问数组元素

1
2
3
4
5
6
7
8
9
10
11
int[] numbers = {10, 20, 30, 40, 50};

// 获取数组长度
int length = numbers.length; // 5

// 访问元素
int first = numbers[0]; // 10
int last = numbers[numbers.length - 1]; // 50

// 修改元素
numbers[1] = 25; // 数组变为 {10, 25, 30, 40, 50}

二、数组操作

1. 遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int[] numbers = {1, 2, 3, 4, 5};

// 1. 基本for循环
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}

// 2. 增强for循环 (for-each)
for (int num : numbers) {
System.out.println(num);
}

// 3. 使用Arrays.toString()
System.out.println(Arrays.toString(numbers));

2. 数组复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int[] source = {1, 2, 3, 4, 5};

// 1. 使用System.arraycopy()
int[] dest1 = new int[5];
System.arraycopy(source, 0, dest1, 0, source.length);

// 2. 使用Arrays.copyOf()
int[] dest2 = Arrays.copyOf(source, source.length);

// 3. 使用clone()
int[] dest3 = source.clone();

// 4. 手动复制
int[] dest4 = new int[source.length];
for (int i = 0; i < source.length; i++) {
dest4[i] = source[i];
}

3. 数组排序和搜索

1
2
3
4
5
6
7
8
9
10
int[] numbers = {5, 3, 9, 1, 7};

// 排序数组
Arrays.sort(numbers); // {1, 3, 5, 7, 9}

// 二分查找 (数组必须已排序)
int index = Arrays.binarySearch(numbers, 5); // 2

// 填充数组
Arrays.fill(numbers, 0); // 所有元素变为0

三、多维数组

1. 二维数组

1
2
3
4
5
6
7
8
9
10
11
12
// 声明和初始化二维数组
int[][] matrix1 = new int[3][4]; // 3行4列
int[][] matrix2 = {{1, 2}, {3, 4}, {5, 6}};

// 不规则数组
int[][] irregular = new int[3][];
irregular[0] = new int[2];
irregular[1] = new int[3];
irregular[2] = new int[1];

// 访问二维数组元素
int value = matrix2[1][1]; // 4

2. 多维数组遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

// 嵌套for循环遍历
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}

// 增强for循环遍历
for (int[] row : matrix) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}

四、Arrays 工具类

1. 常用方法

方法 描述 示例
sort() 数组排序 Arrays.sort(arr)
binarySearch() 二分查找 Arrays.binarySearch(arr, key)
equals() 比较数组 Arrays.equals(arr1, arr2)
fill() 填充数组 Arrays.fill(arr, value)
copyOf() 复制数组 Arrays.copyOf(arr, newLength)
toString() 数组转字符串 Arrays.toString(arr)
asList() 数组转List Arrays.asList(arr)

2. 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Arrays;

public class ArrayExample {
public static void main(String[] args) {
int[] numbers = {5, 3, 9, 1, 7};

// 排序
Arrays.sort(numbers); // [1, 3, 5, 7, 9]

// 搜索
int index = Arrays.binarySearch(numbers, 5); // 2

// 比较
int[] copy = Arrays.copyOf(numbers, numbers.length);
boolean isEqual = Arrays.equals(numbers, copy); // true

// 填充
Arrays.fill(copy, 0); // [0, 0, 0, 0, 0]

// 转字符串
System.out.println(Arrays.toString(numbers));
}
}

五、实际应用示例

1. 查找数组中的最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ArrayMax {
public static int findMax(int[] arr) {
if (arr == null || arr.length == 0) {
throw new IllegalArgumentException("数组不能为空");
}

int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}

public static void main(String[] args) {
int[] numbers = {12, 45, 67, 89, 34, 23};
System.out.println("最大值: " + findMax(numbers));
}
}

2. 数组反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ArrayReverse {
public static void reverse(int[] arr) {
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("原始数组: " + Arrays.toString(numbers));
reverse(numbers);
System.out.println("反转后: " + Arrays.toString(numbers));
}
}

3. 矩阵转置

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
public class MatrixTranspose {
public static int[][] transpose(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;

int[][] result = new int[cols][rows];

for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result[j][i] = matrix[i][j];
}
}

return result;
}

public static void main(String[] args) {
int[][] matrix = {{1, 2, 3}, {4, 5, 6}};
System.out.println("原始矩阵:");
printMatrix(matrix);

int[][] transposed = transpose(matrix);
System.out.println("转置矩阵:");
printMatrix(transposed);
}

private static void printMatrix(int[][] matrix) {
for (int[] row : matrix) {
System.out.println(Arrays.toString(row));
}
}
}

六、注意事项与最佳实践

  1. 数组边界检查

    1
    2
    int[] arr = new int[5];
    // arr[5] = 10; // 会抛出ArrayIndexOutOfBoundsException
  2. 数组长度固定

    1
    2
    3
    // 数组创建后长度不可变
    int[] arr = new int[5];
    // arr.length = 10; // 编译错误
  3. 默认初始化值

    • 数值类型:0
    • boolean:false
    • 对象引用:null
  4. 数组与集合的选择

    1
    2
    // 固定大小、性能要求高 → 使用数组
    // 需要动态大小、丰富操作 → 使用ArrayList等集合
  5. 多维数组内存布局

    1
    2
    3
    4
    5
    // Java中的多维数组实际上是数组的数组
    int[][] arr = new int[3][];
    arr[0] = new int[2];
    arr[1] = new int[3];
    arr[2] = new int[4];
  6. 数组性能考虑

    1
    2
    3
    // 访问数组元素是O(1)时间复杂度
    // 连续内存分配,缓存友好
    // 适合数值计算等高性能场景

通过合理使用数组和Arrays工具类,可以高效地处理各种数据集合操作。对于固定大小的同类型数据集合,数组是最佳选择;对于需要动态调整大小的场景,应考虑使用Java集合框架中的类。

Java 日期时间全面解析

Java 提供了多种处理日期和时间的类,包括传统的 DateCalendar 和现代的 java.time 包(Java 8+)。下面我将系统地讲解 Java 日期时间处理的各种功能和使用场景。

一、传统日期时间类

1. Date 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Date;

public class DateExample {
public static void main(String[] args) {
// 创建当前日期时间的Date对象
Date now = new Date();
System.out.println("当前时间: " + now);

// 创建指定时间戳的Date对象
Date specificDate = new Date(1620000000000L);
System.out.println("特定时间: " + specificDate);

// 比较日期
System.out.println("now after specificDate? " + now.after(specificDate));
System.out.println("now before specificDate? " + now.before(specificDate));
System.out.println("now equals specificDate? " + now.equals(specificDate));

// 获取时间戳
long timestamp = now.getTime();
System.out.println("时间戳: " + timestamp);
}
}

2. SimpleDateFormat 类

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
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatExample {
public static void main(String[] args) {
Date now = new Date();

// 创建格式化对象
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 EEE a hh:mm:ss");

// 格式化日期
System.out.println("格式1: " + sdf1.format(now));
System.out.println("格式2: " + sdf2.format(now));

// 解析字符串为日期
try {
String dateStr = "2023-05-15 14:30:00";
Date parsedDate = sdf1.parse(dateStr);
System.out.println("解析后的日期: " + parsedDate);
} catch (Exception e) {
e.printStackTrace();
}
}
}

3. Calendar 类

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
import java.util.Calendar;
import java.util.Date;

public class CalendarExample {
public static void main(String[] args) {
// 获取Calendar实例
Calendar calendar = Calendar.getInstance();

// 设置特定日期
calendar.set(2023, Calendar.MAY, 15, 14, 30, 0);
Date date = calendar.getTime();
System.out.println("设置的日期: " + date);

// 获取日期各部分
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
System.out.printf("%d年%d月%d日 %d:%d\n", year, month, day, hour, minute);

// 日期计算
calendar.add(Calendar.DAY_OF_MONTH, 7); // 加7天
calendar.add(Calendar.MONTH, -1); // 减1个月
System.out.println("计算后的日期: " + calendar.getTime());
}
}

二、Java 8 日期时间 API

Java 8 引入了全新的日期时间 API (java.time 包),解决了传统类的各种问题。

1. 主要类介绍

类名 描述
LocalDate 只包含日期,不包含时间
LocalTime 只包含时间,不包含日期
LocalDateTime 包含日期和时间
ZonedDateTime 包含时区的日期和时间
Instant 时间戳(精确到纳秒)
Duration 时间段,以秒和纳秒为单位
Period 时间段,以年、月、日为单位
DateTimeFormatter 日期时间格式化类

2. 基本使用示例

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
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Java8DateTime {
public static void main(String[] args) {
// 获取当前日期时间
LocalDate currentDate = LocalDate.now();
LocalTime currentTime = LocalTime.now();
LocalDateTime currentDateTime = LocalDateTime.now();

System.out.println("当前日期: " + currentDate);
System.out.println("当前时间: " + currentTime);
System.out.println("当前日期时间: " + currentDateTime);

// 创建特定日期时间
LocalDate birthDate = LocalDate.of(1990, Month.MAY, 15);
LocalTime meetingTime = LocalTime.of(14, 30);
LocalDateTime projectDeadline = LocalDateTime.of(2023, 12, 31, 23, 59);

// 日期时间计算
LocalDate nextWeek = currentDate.plus(1, ChronoUnit.WEEKS);
LocalTime twoHoursLater = currentTime.plusHours(2);
LocalDateTime yesterdaySameTime = currentDateTime.minusDays(1);

// 日期时间比较
System.out.println("今天在生日之后? " + currentDate.isAfter(birthDate));
System.out.println("现在在会议时间之前? " + currentTime.isBefore(meetingTime));

// 格式化输出
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("格式化日期时间: " + formattedDateTime);

// 解析字符串
LocalDateTime parsedDateTime = LocalDateTime.parse("2023/05/15 14:30:00", formatter);
System.out.println("解析后的日期时间: " + parsedDateTime);

// 计算时间差
Period age = Period.between(birthDate, currentDate);
System.out.printf("年龄: %d岁%d个月%d天\n", age.getYears(), age.getMonths(), age.getDays());

Duration duration = Duration.between(meetingTime, currentTime);
System.out.println("距离会议时间还有(秒): " + duration.getSeconds());
}
}

三、实际应用示例

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

1
2
3
4
5
6
7
8
9
10
11
12
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class DaysBetweenDates {
public static void main(String[] args) {
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2023, 12, 31);

long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("2023年有 " + daysBetween + " 天");
}
}

2. 判断闰年

1
2
3
4
5
6
7
8
9
10
11
12
import java.time.LocalDate;

public class LeapYearCheck {
public static void main(String[] args) {
int[] years = {2000, 2020, 2023, 2100};

for (int year : years) {
boolean isLeap = LocalDate.of(year, 1, 1).isLeapYear();
System.out.println(year + " 年是闰年吗? " + isLeap);
}
}
}

3. 工作时间计算器

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
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class WorkingHoursCalculator {
public static void main(String[] args) {
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");

LocalTime startTime = LocalTime.of(9, 0); // 上班时间 09:00
LocalTime endTime = LocalTime.of(18, 30); // 下班时间 18:30
LocalTime lunchStart = LocalTime.of(12, 0); // 午餐开始 12:00
LocalTime lunchEnd = LocalTime.of(13, 30); // 午餐结束 13:30

// 计算工作时间
Duration morningWork = Duration.between(startTime, lunchStart);
Duration afternoonWork = Duration.between(lunchEnd, endTime);
Duration totalWork = morningWork.plus(afternoonWork);

System.out.println("上午工作时间: " + morningWork.toHours() + "小时" +
morningWork.toMinutesPart() + "分钟");
System.out.println("下午工作时间: " + afternoonWork.toHours() + "小时" +
afternoonWork.toMinutesPart() + "分钟");
System.out.println("总工作时间: " + totalWork.toHours() + "小时" +
totalWork.toMinutesPart() + "分钟");
}
}

四、注意事项与最佳实践

  1. 时区处理

    1
    2
    3
    4
    5
    // 使用时区敏感的类处理跨时区应用
    ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
    ZonedDateTime newYorkTime = beijingTime.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println("北京时间: " + beijingTime);
    System.out.println("纽约时间: " + newYorkTime);
  2. 新旧API转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Date 转 Instant
    Date legacyDate = new Date();
    Instant instant = legacyDate.toInstant();

    // Instant 转 Date
    Date newDate = Date.from(instant);

    // Calendar 转 LocalDateTime
    Calendar calendar = Calendar.getInstance();
    LocalDateTime ldt = LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());
  3. 日期格式化线程安全

    1
    2
    3
    4
    5
    // DateTimeFormatter 是线程安全的,可以定义为常量
    public static final DateTimeFormatter DATE_FORMATTER =
    DateTimeFormatter.ofPattern("yyyy-MM-dd");

    // SimpleDateFormat 不是线程安全的,需要每次创建新实例或同步使用
  4. 性能考虑

    • 对于高频率的日期操作,使用 java.time 包性能更好
    • 避免在循环中重复创建格式化对象
  5. 日期验证

    1
    2
    3
    4
    5
    try {
    LocalDate.parse("2023-02-30"); // 无效日期会抛出DateTimeParseException
    } catch (Exception e) {
    System.out.println("无效日期");
    }
  6. 时间精度

    • Instant 精确到纳秒
    • 传统 Date 精确到毫秒
    • 根据需求选择合适的时间精度

通过合理使用 Java 的日期时间 API,可以准确、高效地处理各种日期时间相关的业务需求。对于新项目,建议直接使用 Java 8 的 java.time 包;对于维护旧系统,可能需要与传统 API 交互。

Java 正则表达式全面解析

正则表达式是处理字符串的强大工具,Java 通过 java.util.regex 包提供了完整的正则表达式支持。下面我将系统地讲解 Java 中正则表达式的各种功能和使用场景。

一、正则表达式基础

1. Pattern 和 Matcher 类

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
import java.util.regex.*;

public class RegexBasic {
public static void main(String[] args) {
String text = "Hello, my email is example@email.com and my phone is 123-456-7890";

// 1. 使用Pattern.matches()简单匹配
boolean isMatch = Pattern.matches(".*email.*", text);
System.out.println("Contains 'email'? " + isMatch);

// 2. 使用Pattern和Matcher
Pattern emailPattern = Pattern.compile("\\w+@\\w+\\.\\w+");
Matcher emailMatcher = emailPattern.matcher(text);

// 查找所有匹配
while (emailMatcher.find()) {
System.out.println("Found email: " + emailMatcher.group());
}

// 3. 电话号码匹配
Pattern phonePattern = Pattern.compile("\\d{3}-\\d{3}-\\d{4}");
Matcher phoneMatcher = phonePattern.matcher(text);
if (phoneMatcher.find()) {
System.out.println("Phone number: " + phoneMatcher.group());
}
}
}

2. 常用正则表达式元字符

元字符 描述 示例
. 匹配任意字符 a.c 匹配 “abc”, “a1c” 等
\d 数字 [0-9] \d\d 匹配 “12”, “34” 等
\D 非数字 [^0-9] \D\D 匹配 “ab”, “#$” 等
\w 单词字符 [a-zA-Z0-9_] \w+ 匹配单词
\W 非单词字符 \W 匹配 “@”, “!” 等
\s 空白字符 [ \t\n\x0B\f\r] \s+ 匹配空白
\S 非空白字符 \S+ 匹配非空白序列
^ 行开头 ^Java 匹配行开头的 “Java”
$ 行结尾 end$ 匹配行结尾的 “end”
* 0次或多次 a*b 匹配 “b”, “ab”, “aab” 等
+ 1次或多次 a+b 匹配 “ab”, “aab” 但不匹配 “b”
? 0次或1次 a?b 匹配 “b”, “ab”
{n} 恰好n次 a{3} 匹配 “aaa”
{n,} 至少n次 a{2,} 匹配 “aa”, “aaa” 等
{n,m} n到m次 a{2,4} 匹配 “aa”, “aaa”, “aaaa”

二、分组与捕获

1. 捕获组

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
import java.util.regex.*;

public class RegexGroups {
public static void main(String[] args) {
String dateStr = "2023-05-15, 1999-12-31";
Pattern datePattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher dateMatcher = datePattern.matcher(dateStr);

while (dateMatcher.find()) {
System.out.println("Full match: " + dateMatcher.group(0));
System.out.println("Year: " + dateMatcher.group(1));
System.out.println("Month: " + dateMatcher.group(2));
System.out.println("Day: " + dateMatcher.group(3));
}

// 命名捕获组 (Java 7+)
Pattern namedPattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher namedMatcher = namedPattern.matcher(dateStr);
while (namedMatcher.find()) {
System.out.println("\nNamed groups:");
System.out.println("Year: " + namedMatcher.group("year"));
System.out.println("Month: " + namedMatcher.group("month"));
System.out.println("Day: " + namedMatcher.group("day"));
}
}
}

2. 非捕获组

1
2
3
4
5
6
// (?:pattern) - 非捕获组
Pattern pattern = Pattern.compile("(?:Mr|Ms|Mrs)\\.\\s(\\w+)");
Matcher matcher = pattern.matcher("Mr. Smith and Ms. Doe");
while (matcher.find()) {
System.out.println("Name: " + matcher.group(1)); // group(0)是整个匹配,group(1)是名字
}

三、边界匹配与零宽断言

1. 边界匹配

1
2
3
4
5
6
7
8
9
10
11
// \b - 单词边界
Pattern wordBoundary = Pattern.compile("\\bcat\\b");
Matcher wbMatcher = wordBoundary.matcher("cat concatenate cat");
while (wbMatcher.find()) {
System.out.println("Found at: " + wbMatcher.start());
}

// ^ $ - 行开始和结束
Pattern linePattern = Pattern.compile("^\\d+$"); // 整行都是数字
System.out.println("123 matches: " + linePattern.matcher("123").matches());
System.out.println("123a matches: " + linePattern.matcher("123a").matches());

2. 零宽断言

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
// 正向先行断言 (?=pattern)
Pattern positiveLookahead = Pattern.compile("\\w+(?=\\=)");
Matcher plaMatcher = positiveLookahead.matcher("key=value");
if (plaMatcher.find()) {
System.out.println("Key before '=': " + plaMatcher.group());
}

// 负向先行断言 (?!pattern)
Pattern negativeLookahead = Pattern.compile("\\d{3}(?!-)");
Matcher nlaMatcher = negativeLookahead.matcher("123-456 789");
while (nlaMatcher.find()) {
System.out.println("3 digits not followed by '-': " + nlaMatcher.group());
}

// 正向后行断言 (?<=pattern)
Pattern positiveLookbehind = Pattern.compile("(?<=\\$)\\d+");
Matcher plbMatcher = positiveLookbehind.matcher("Price: $100");
if (plbMatcher.find()) {
System.out.println("Number after '$': " + plbMatcher.group());
}

// 负向后行断言 (?<!pattern)
Pattern negativeLookbehind = Pattern.compile("(?<!\\$)\\d+");
Matcher nlbMatcher = negativeLookbehind.matcher("Price: $100 200");
while (nlbMatcher.find()) {
System.out.println("Number not after '$': " + nlbMatcher.group());
}

四、常用正则表达式示例

1. 验证电子邮件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class EmailValidator {
private static final String EMAIL_REGEX =
"^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@" +
"(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";

public static boolean isValid(String email) {
return Pattern.compile(EMAIL_REGEX).matcher(email).matches();
}

public static void main(String[] args) {
String[] emails = {
"test@example.com",
"user.name@domain.co",
"invalid@.com",
"another@test"
};

for (String email : emails) {
System.out.println(email + ": " + isValid(email));
}
}
}

2. 提取URL

1
2
3
4
5
6
7
8
9
10
11
public class UrlExtractor {
public static void main(String[] args) {
String text = "Visit https://www.example.com or http://test.org for more info";
Pattern urlPattern = Pattern.compile("https?://(?:[\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?");

Matcher matcher = urlPattern.matcher(text);
while (matcher.find()) {
System.out.println("Found URL: " + matcher.group());
}
}
}

3. 密码强度验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PasswordValidator {
// 至少8字符,包含大小写字母、数字和特殊字符
private static final String PASSWORD_REGEX =
"^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$";

public static boolean isValid(String password) {
return Pattern.compile(PASSWORD_REGEX).matcher(password).matches();
}

public static void main(String[] args) {
String[] passwords = {
"Weakpass1",
"Strong@Pass123",
"noSpecialChar1",
"Short1@"
};

for (String pwd : passwords) {
System.out.println(pwd + ": " + isValid(pwd));
}
}
}

五、高级用法

1. 替换操作

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
public class RegexReplacement {
public static void main(String[] args) {
String text = "User: john, Age: 30; User: jane, Age: 25";

// 简单替换
String replaced = text.replaceAll("\\d+", "XX");
System.out.println("After age masking: " + replaced);

// 使用捕获组替换
String nameReplaced = text.replaceAll("User: (\\w+)", "Name: $1");
System.out.println("After name format: " + nameReplaced);

// 使用Matcher进行复杂替换
Pattern pattern = Pattern.compile("(\\w+): (\\w+)");
Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();

while (matcher.find()) {
String field = matcher.group(1);
String value = matcher.group(2);
String replacement = field.equals("Age") ? "**masked**" : value;
matcher.appendReplacement(sb, field + ": " + replacement);
}
matcher.appendTail(sb);
System.out.println("Selective masking: " + sb.toString());
}
}

2. 分割字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RegexSplit {
public static void main(String[] args) {
String text = "apple,orange,,banana;grape";

// 简单分割
String[] fruits1 = text.split("[,;]");
System.out.println("Simple split: " + Arrays.toString(fruits1));

// 去除空字符串
String[] fruits2 = text.split("[,;]+");
System.out.println("No empty strings: " + Arrays.toString(fruits2));

// 使用Pattern.split
Pattern pattern = Pattern.compile("[,;]");
String[] fruits3 = pattern.split(text);
System.out.println("Using Pattern.split: " + Arrays.toString(fruits3));

// 限制分割次数
String[] fruits4 = text.split("[,;]", 3);
System.out.println("Limited split: " + Arrays.toString(fruits4));
}
}

六、性能优化与最佳实践

  1. 预编译正则表达式

    1
    2
    // 对于频繁使用的正则表达式,预编译Pattern
    private static final Pattern EMAIL_PATTERN = Pattern.compile("your_email_regex");
  2. 避免贪婪匹配

    1
    2
    3
    4
    // 贪婪匹配
    Pattern greedy = Pattern.compile("<.*>"); // 匹配整个 <a><b></b></a>
    // 非贪婪匹配
    Pattern reluctant = Pattern.compile("<.*?>"); // 匹配单个标签 <a>, <b>, </b>, </a>
  3. 使用非捕获组提高性能

    1
    2
    // 当你不需要捕获组内容时使用非捕获组
    Pattern pattern = Pattern.compile("(?:Mr|Ms|Mrs)\\.\\s\\w+");
  4. 边界匹配优化

    1
    2
    // 使用^和$确保整行匹配,避免部分匹配
    Pattern exactMatch = Pattern.compile("^\\d{5}$"); // 只匹配5位数字
  5. 处理多行文本

    1
    2
    3
    4
    5
    6
    7
    // 使用Pattern.MULTILINE模式处理多行文本
    Pattern multiLine = Pattern.compile("^\\w+", Pattern.MULTILINE);
    String text = "first line\nsecond line\nthird line";
    Matcher m = multiLine.matcher(text);
    while (m.find()) {
    System.out.println("Line starts with: " + m.group());
    }
  6. 异常处理

    1
    2
    3
    4
    5
    6
    7
    8
    try {
    Pattern.compile("invalid[regex");
    } catch (PatternSyntaxException e) {
    System.out.println("Invalid regex: " + e.getMessage());
    System.out.println("Description: " + e.getDescription());
    System.out.println("Index: " + e.getIndex());
    System.out.println("Pattern: " + e.getPattern());
    }

通过合理使用 Java 的正则表达式功能,可以高效地处理各种复杂的字符串匹配、查找、替换和分割操作。

Java 方法全面解析

方法是 Java 编程中的基本构建块,用于封装可重用的代码逻辑。下面我将系统地讲解 Java 方法的各种功能和使用场景。

一、方法基础

1. 方法定义与调用

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
public class MethodBasics {

// 1. 无参数无返回值方法
public static void greet() {
System.out.println("Hello, World!");
}

// 2. 带参数无返回值方法
public static void greetUser(String name) {
System.out.println("Hello, " + name + "!");
}

// 3. 带参数有返回值方法
public static int add(int a, int b) {
return a + b;
}

// 4. 可变参数方法
public static double average(double... numbers) {
if (numbers.length == 0) return 0;
double sum = 0;
for (double num : numbers) {
sum += num;
}
return sum / numbers.length;
}

public static void main(String[] args) {
// 方法调用示例
greet();
greetUser("Alice");

int sum = add(5, 3);
System.out.println("5 + 3 = " + sum);

double avg = average(1.5, 2.5, 3.5);
System.out.println("Average: " + avg);
}
}

2. 方法参数传递

Java 中只有值传递(传递的是值的副本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ParameterPassing {
public static void modifyPrimitive(int num) {
num = 100;
System.out.println("Inside method - primitive: " + num);
}

public static void modifyReference(StringBuilder sb) {
sb.append(" World");
System.out.println("Inside method - reference: " + sb);
}

public static void main(String[] args) {
// 基本类型参数
int x = 10;
modifyPrimitive(x);
System.out.println("After method - primitive: " + x);

// 引用类型参数
StringBuilder builder = new StringBuilder("Hello");
modifyReference(builder);
System.out.println("After method - reference: " + builder);
}
}

二、方法重载

方法重载允许一个类中有多个同名方法,只要参数列表不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MethodOverloading {

// 方法重载示例
public static int max(int a, int b) {
return a > b ? a : b;
}

public static double max(double a, double b) {
return a > b ? a : b;
}

public static int max(int a, int b, int c) {
return max(max(a, b), c);
}

public static void main(String[] args) {
System.out.println("Max of 5 and 3: " + max(5, 3));
System.out.println("Max of 5.5 and 3.3: " + max(5.5, 3.3));
System.out.println("Max of 5, 3 and 7: " + max(5, 3, 7));
}
}

三、递归方法

方法调用自身称为递归:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Recursion {

// 计算阶乘
public static int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

// 斐波那契数列
public static int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

public static void main(String[] args) {
System.out.println("5! = " + factorial(5));
System.out.println("Fibonacci(7) = " + fibonacci(7));
}
}

四、构造方法

构造方法用于初始化对象:

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
public class Student {
private String name;
private int age;

// 默认构造方法
public Student() {
this("Unknown", 18); // 调用另一个构造方法
}

// 带参数构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}

// 复制构造方法
public Student(Student other) {
this(other.name, other.age);
}

// Getter 和 Setter 方法
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}

public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("Alice", 20);
Student s3 = new Student(s2);

System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}

五、静态方法 vs 实例方法

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
public class MethodTypes {
private int instanceVar = 10;
private static int staticVar = 20;

// 实例方法
public void instanceMethod() {
System.out.println("实例方法可以访问实例变量: " + instanceVar);
System.out.println("实例方法可以访问静态变量: " + staticVar);
}

// 静态方法
public static void staticMethod() {
// System.out.println(instanceVar); // 错误:不能直接访问实例变量
System.out.println("静态方法可以访问静态变量: " + staticVar);
}

public static void main(String[] args) {
MethodTypes obj = new MethodTypes();

// 调用实例方法
obj.instanceMethod();

// 调用静态方法
MethodTypes.staticMethod();
staticMethod(); // 在同一个类中可以省略类名
}
}

六、方法最佳实践

1. 方法设计原则

  1. 单一职责原则:一个方法只做一件事
  2. 合理命名:方法名应准确描述其功能
  3. 适当长度:通常不超过20-30行代码
  4. 参数数量控制:最好不超过5个参数
  5. 避免副作用:除非必要,方法不应修改传入参数

2. 方法文档注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 计算两个数的最大公约数
*
* @param a 第一个正整数
* @param b 第二个正整数
* @return 两个数的最大公约数
* @throws IllegalArgumentException 如果参数小于等于0
*/
public static int gcd(int a, int b) throws IllegalArgumentException {
if (a <= 0 || b <= 0) {
throw new IllegalArgumentException("参数必须为正整数");
}
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}

3. 实用方法示例

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
import java.util.Arrays;

public class UtilityMethods {

// 判断是否为素数
public static boolean isPrime(int num) {
if (num <= 1) return false;
if (num == 2) return true;
if (num % 2 == 0) return false;
for (int i = 3; i * i <= num; i += 2) {
if (num % i == 0) return false;
}
return true;
}

// 反转数组
public static <T> void reverseArray(T[] array) {
for (int i = 0; i < array.length / 2; i++) {
T temp = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = temp;
}
}

// 二分查找
public static int binarySearch(int[] array, int key) {
int low = 0;
int high = array.length - 1;

while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = array[mid];

if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
return mid; // 找到key
}
}
return -(low + 1); // 未找到,返回插入点
}

public static void main(String[] args) {
System.out.println("Is 17 prime? " + isPrime(17));

Integer[] nums = {1, 2, 3, 4, 5};
reverseArray(nums);
System.out.println("Reversed array: " + Arrays.toString(nums));

int[] sorted = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91};
int index = binarySearch(sorted, 23);
System.out.println("Found 23 at index: " + index);
}
}

七、高级方法特性

1. 方法引用 (Java 8+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Arrays;
import java.util.List;

public class MethodReferences {
public static void print(String s) {
System.out.println(s);
}

public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 静态方法引用
names.forEach(MethodReferences::print);

// 实例方法引用
names.forEach(System.out::println);

// 构造方法引用
names.stream()
.map(String::new)
.forEach(System.out::println);
}
}

2. 默认方法 (Java 8+)

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
public interface Vehicle {
// 常规抽象方法
void start();

// 默认方法
default void stop() {
System.out.println("Vehicle stopped");
}

// 静态方法
static void honk() {
System.out.println("Honk honk!");
}
}

public class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car started");
}

public static void main(String[] args) {
Car car = new Car();
car.start();
car.stop(); // 调用默认方法
Vehicle.honk(); // 调用接口静态方法
}
}

Java I/O、Stream与异常处理全面解析

下面我将整合Java I/O流、文件操作和异常处理的核心知识,并提供实用的代码示例。

一、Java I/O 流体系

1. 流的基本分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.*;
import java.util.Scanner;

public class IOOverview {
public static void main(String[] args) {
// 1. 按方向分
// - 输入流: InputStream, Reader
// - 输出流: OutputStream, Writer

// 2. 按数据类型分
// - 字节流: InputStream, OutputStream
// - 字符流: Reader, Writer

// 3. 按功能分
// - 节点流: 直接操作数据源/目标的流
// - 处理流: 对现有流进行包装增强
}
}

2. 常用流类关系图

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
字节流:
InputStream
|-- FileInputStream
|-- FilterInputStream
|-- BufferedInputStream
|-- DataInputStream
|-- ObjectInputStream

OutputStream
|-- FileOutputStream
|-- FilterOutputStream
|-- BufferedOutputStream
|-- DataOutputStream
|-- ObjectOutputStream
|-- PrintStream

字符流:
Reader
|-- InputStreamReader
|-- FileReader
|-- BufferedReader

Writer
|-- OutputStreamWriter
|-- FileWriter
|-- BufferedWriter
|-- PrintWriter

二、文件操作与Scanner

1. 文件基本操作

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
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class FileOperations {
public static void main(String[] args) {
// 1. 使用File类
File file = new File("test.txt");

try {
if (file.createNewFile()) {
System.out.println("文件创建成功");
} else {
System.out.println("文件已存在");
}

System.out.println("文件路径: " + file.getAbsolutePath());
System.out.println("文件大小: " + file.length() + " bytes");

// 删除文件
// file.delete();
} catch (IOException e) {
e.printStackTrace();
}

// 2. 使用NIO的Files和Path (Java 7+)
Path path = Paths.get("test.txt");
try {
if (!Files.exists(path)) {
Files.createFile(path);
}
System.out.println("文件属性: " + Files.getAttribute(path, "basic:size"));
} catch (IOException e) {
e.printStackTrace();
}

// 3. 使用Scanner读取文件
try (Scanner scanner = new Scanner(path)) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

2. Scanner类详解

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
import java.util.Scanner;

public class ScannerDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.print("请输入整数: ");
if (scanner.hasNextInt()) {
int num = scanner.nextInt();
System.out.println("读取的整数: " + num);
} else {
System.out.println("输入的不是整数");
}

scanner.nextLine(); // 消耗换行符

System.out.print("请输入一行文本: ");
String line = scanner.nextLine();
System.out.println("读取的行: " + line);

System.out.print("请输入用逗号分隔的数据: ");
scanner.useDelimiter(",");
while (scanner.hasNext()) {
System.out.println(scanner.next().trim());
}

scanner.close();
}
}

三、字节流与字符流操作

1. 字节流文件复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.*;

public class ByteStreamCopy {
public static void copyFile(String src, String dest) {
try (InputStream in = new BufferedInputStream(new FileInputStream(src));
OutputStream out = new BufferedOutputStream(new FileOutputStream(dest))) {

byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("文件复制完成");
} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
copyFile("source.jpg", "destination.jpg");
}
}

2. 字符流读写文本

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
import java.io.*;

public class CharacterStreamDemo {
public static void writeToFile(String filename, String content) {
try (Writer writer = new BufferedWriter(new FileWriter(filename))) {
writer.write(content);
System.out.println("内容写入成功");
} catch (IOException e) {
e.printStackTrace();
}
}

public static String readFromFile(String filename) {
StringBuilder content = new StringBuilder();
try (Reader reader = new BufferedReader(new FileReader(filename))) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
content.append(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
}
return content.toString();
}

public static void main(String[] args) {
writeToFile("demo.txt", "这是使用字符流写入的文本内容\n第二行内容");
System.out.println("读取内容:\n" + readFromFile("demo.txt"));
}
}

四、异常处理机制

1. 异常处理基础

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
import java.io.*;

public class ExceptionHandling {
public static void checkedExceptionDemo() throws IOException {
// 受检异常必须处理或声明抛出
FileReader reader = new FileReader("nonexistent.txt");
reader.close();
}

public static void uncheckedExceptionDemo() {
// 非受检异常可以不处理
int[] arr = new int[3];
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
}

public static void main(String[] args) {
// 1. try-catch-finally
try {
checkedExceptionDemo();
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO错误: " + e.getMessage());
} finally {
System.out.println("finally块总是执行");
}

// 2. try-with-resources (Java 7+)
try (InputStream in = new FileInputStream("test.txt")) {
System.out.println("文件打开成功");
// 自动关闭资源
} catch (IOException e) {
System.err.println("处理文件时出错: " + e.getMessage());
}

// 3. 自定义异常
try {
validateAge(15);
} catch (InvalidAgeException e) {
System.err.println("年龄验证失败: " + e.getMessage());
}
}

// 自定义异常示例
static class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}

public static void validateAge(int age) throws InvalidAgeException {
if (age < 18) {
throw new InvalidAgeException("年龄必须大于18岁");
}
}
}

2. 异常处理最佳实践

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
import java.io.*;
import java.nio.file.*;

public class ExceptionBestPractices {
public static void readFile(String filename) {
try {
// 业务逻辑
String content = Files.readString(Path.of(filename));
System.out.println(content);
} catch (NoSuchFileException e) {
System.err.println("文件不存在: " + e.getFile());
} catch (AccessDeniedException e) {
System.err.println("没有访问权限: " + e.getFile());
} catch (IOException e) {
System.err.println("读取文件时发生错误: " + e.getMessage());
}
}

public static void processUserInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入数字: ");

try {
int num = Integer.parseInt(scanner.nextLine());
System.out.println("输入的数字是: " + num);
} catch (NumberFormatException e) {
System.err.println("输入的不是有效数字");
} finally {
scanner.close();
}
}

public static void main(String[] args) {
readFile("test.txt");
processUserInput();
}
}

五、综合应用示例

1. 文件加密解密工具

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
import java.io.*;
import java.util.Scanner;

public class FileEncryptor {
private static final int KEY = 0x55; // 简单异或加密密钥

public static void encryptFile(String inputFile, String outputFile) throws IOException {
try (InputStream in = new BufferedInputStream(new FileInputStream(inputFile));
OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile))) {

int data;
while ((data = in.read()) != -1) {
out.write(data ^ KEY);
}
}
}

public static void decryptFile(String inputFile, String outputFile) throws IOException {
encryptFile(inputFile, outputFile); // 异或加密解密是相同的操作
}

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("1. 加密文件");
System.out.println("2. 解密文件");
System.out.print("请选择操作: ");

try {
int choice = Integer.parseInt(scanner.nextLine());
System.out.print("输入文件路径: ");
String input = scanner.nextLine();
System.out.print("输出文件路径: ");
String output = scanner.nextLine();

if (choice == 1) {
encryptFile(input, output);
System.out.println("文件加密完成");
} else if (choice == 2) {
decryptFile(input, output);
System.out.println("文件解密完成");
} else {
System.out.println("无效选择");
}
} catch (NumberFormatException e) {
System.err.println("请输入有效数字");
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("处理文件时出错: " + e.getMessage());
} finally {
scanner.close();
}
}
}

2. 日志记录系统

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
import java.io.*;
import java.time.*;
import java.util.*;

public class LoggerSystem {
private static final String LOG_FILE = "app.log";

public enum LogLevel {
INFO, WARNING, ERROR
}

public static void log(LogLevel level, String message) {
String logEntry = String.format("[%s][%s] %s%n",
LocalDateTime.now(), level, message);

try (FileWriter fw = new FileWriter(LOG_FILE, true);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter out = new PrintWriter(bw)) {

out.print(logEntry);
} catch (IOException e) {
System.err.println("记录日志失败: " + e.getMessage());
}
}

public static List<String> readLogs() {
List<String> logs = new ArrayList<>();
try (Scanner scanner = new Scanner(new File(LOG_FILE))) {
while (scanner.hasNextLine()) {
logs.add(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.err.println("日志文件不存在");
}
return logs;
}

public static void main(String[] args) {
log(LogLevel.INFO, "应用程序启动");
log(LogLevel.WARNING, "内存使用量过高");
log(LogLevel.ERROR, "数据库连接失败");

System.out.println("日志内容:");
readLogs().forEach(System.out::println);
}
}

六、关键知识点总结

  1. I/O流选择原则

    • 文本数据:优先使用字符流(Reader/Writer)
    • 二进制数据:使用字节流(InputStream/OutputStream)
    • 需要缓冲:包装Buffered系列流
    • Java 7+:优先使用NIO的Files和Paths工具类
  2. 异常处理要点

    • 受检异常必须处理或声明抛出
    • 非受检异常通常表示编程错误
    • 使用try-with-resources自动管理资源
    • 捕获异常时应从具体到抽象
    • 不要忽略捕获的异常
  3. Scanner使用技巧

    • 读取不同类型数据使用hasNextXxx()/nextXxx()
    • 注意处理换行符问题(nextLine与其他方法混用时)
    • 使用useDelimiter()设置自定义分隔符
    • 读取完毕后调用close()释放资源
  4. 性能优化建议

    • 使用缓冲区减少I/O操作次数
    • 合理设置缓冲区大小(通常8KB)
    • 大文件处理使用流式而非全部加载到内存
    • 及时关闭资源防止内存泄漏