Skip to main content

第六课:常用工具

(一)日期 和 时间

1. Date 类

java.util.Date 是一个用于表示日期和时间的类。它提供了两个构造函数:

  • Date(): 使用当前时间和日期初始化一个实例。
  • Date(long millisec): 接收从 1970 年 1 月 1 日 00:00:00 GMT 起的毫秒数,设置时间和日期。

常用方法如下:

序号方法描述
1after(Date date)如果调用对象的时间在指定日期之后,返回 true,否则返回 false
2before(Date date)如果调用对象的时间在指定日期之前,返回 true,否则返回 false
3compareTo(Date date)比较当前对象与指定日期,返回值:0 表示相等,负值表示当前日期在前,正值表示当前日期在后。
4equals(Object date)当调用对象的时间与参数指定的时间相等时返回 true,否则返回 false
5getTime()返回距离 1970 年 1 月 1 日 00:00:00 GMT 的毫秒值。
6setTime(long time)设置日期对象为指定时间,时间以毫秒为单位。
7toString()返回格式化的字符串表示日期,例如:Mon May 04 09:51:52 CDT 2013

示例:打印当前时间:

import java.util.Date;

public class DateDemo {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.toString());
    }
}

输出示例:
Mon May 04 09:51:52 CDT 2013

2. 比较日期

  • 方法一:getTime() 比较两个日期的毫秒值:
Date date1 = new Date();
Date date2 = new Date(1245876867000L); 
//表示从 1970 年 1 月 1 日 00:00:00 GMT 开始起,经过了 1245876867000 毫秒 ,即 UTC 时间:2009/6/24 02:14:27 GMT


if (date1.getTime() > date2.getTime()) {
    System.out.println("date1 在 date2 之后");
}
  • 方法二:before()after()
Date date1 = new Date();
Date date2 = new Date(1245876867000L);

if (date1.after(date2)) {
    System.out.println("date1 在 date2 之后");
} else if (date1.before(date2)) {
    System.out.println("date1 在 date2 之前");
}
  • 方法三:compareTo()
Date date1 = new Date();
Date date2 = new Date(1245876867000L);

int result = date1.compareTo(date2);
if (result > 0) {
    System.out.println("date1 在 date2 之后");
} else if (result < 0) {
    System.out.println("date1 在 date2 之前");
} else {
    System.out.println("date1 和 date2 相等");
}

3. 自定义日期格式

SimpleDateFormat 是一个专门用于日期格式化和解析的类,可根据自定义的格式生成字符串。

常用格式化模式

字母说明示例
yyyy2023
MM月(两位,大写)03
dd日(两位)08
HH24 小时制的小时14
mm分钟30
ss59
E星期简写Wed

注意:

  • MM 表示月份,而 mm 表示分钟。
  • 使用 hh 表示 12 小时制,而 HH 表示 24 小时制。

示例

import java.text.SimpleDateFormat;
import java.util.Date;

public class FormatDate {
    public static void main(String[] args) {
        Date date = new Date();
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前时间为: " + ft.format(date));
    }
}

输出示例:
当前时间为: 2023-02-24 13:37:45

4. 解析字符串为日期

使用 SimpleDateFormatparse() 方法可以将字符串解析为 Date 对象:

import java.text.SimpleDateFormat;
import java.util.Date;

public class ParseDate {
    public static void main(String[] args) throws Exception {
        String dateStr = "2023-03-15";
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd");
        Date date = ft.parse(dateStr);
        System.out.println(date);
    }
}

5. Calendar 类

Calendar 是一个更加强大的类,支持操作时间的各个组成部分。

1. 创建 Calendar

Calendar calendar = Calendar.getInstance();

2. 设置日期

calendar.set(2023, Calendar.MARCH, 10); // 设置为 2023 年 3 月 10 日

3. 获取日期信息

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 注意:月份从 0 开始,需要加 1
int day = calendar.get(Calendar.DATE);

4. 修改日期

calendar.add(Calendar.DATE, 10); // 当前日期加 10 天
calendar.add(Calendar.MONTH, -1); // 当前月份减 1 月

6. GregorianCalendar 类

GregorianCalendar 是一个 Calendar 的具体实现类。语法和 Calendar 类类似:

import java.util.GregorianCalendar;

public class GregorianCalendarDemo {
    public static void main(String[] args) {
        GregorianCalendar calendar = new GregorianCalendar(2023, Calendar.MARCH, 15);

        System.out.println("日期: " + calendar.get(Calendar.YEAR) + "-"
                + (calendar.get(Calendar.MONTH) + 1) + "-" + calendar.get(Calendar.DATE));

        if (calendar.isLeapYear(2023)) {
            System.out.println("2023 是闰年");
        } else {
            System.out.println("2023 不是闰年");
        }
    }
}

7. 休眠线程:Thread.sleep()

通过 Thread.sleep() 方法可以让当前线程暂停一段时间:

import java.util.Date;

public class SleepDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("程序开始:" + new Date());
        Thread.sleep(3000); // 休眠 3 秒
        System.out.println("程序结束:" + new Date());
    }
}

输出示例:

程序开始:Fri Jan 08 09:48:47 CST 2023
程序结束:Fri Jan 08 09:48:50 CST 2023

throws InterruptedException 是一个抛出异常的声明。

为什么要抛出异常?

Thread.sleep() 方法 可能会抛出 InterruptedException 异常。

如果线程在睡眠状态时被其他线程打断,会抛出此异常。

所以为了编译通过,需要用 throws 定义,表示这个异常交由调用者处理。

注意 : 线程休眠期间仍然占用内存空间,但让出了 CPU 时间。

(二) 正则表达式

正则表达式是一种定义字符串模式的工具,广泛用于搜索、编辑或处理文本。

1. 核心类与功能

Java 提供了 java.util.regex 包,其中包括下面这些核心类:

  • Pattern: 表示编译后的正则表达式。
  • Matcher: 用于执行匹配操作的引擎。
  • PatternSyntaxException: 处理正则表达式中的语法错误。

示例

String content = "I am noob from runoob.com.";
String pattern = ".*runoob.*";
boolean isMatch = Pattern.matches(pattern, content);
System.out.println("字符串中是否包含 'runoob' 子字符串? " + isMatch);

输出:
字符串中是否包含 'runoob' 子字符串? true

2. 捕获组

捕获组通过()括号分组,可用于提取匹配内容。

示例: 正则表达式:(\D*)(\d+)(.*)
解读:

  • \D*: 匹配任意多个非数字字符。
  • \d+: 匹配一个或多个数字。
  • .*: 匹配任意字符。
import java.util.regex.*;

public class RegexMatches {
    public static void main(String[] args) {
        String line = "This order was placed for QT3000! OK?";
        String pattern = "(\\D*)(\\d+)(.*)";

        Pattern r = Pattern.compile(pattern);
        Matcher m = r.matcher(line);

        if (m.find()) {
            System.out.println("Found value: " + m.group(0)); // 整个匹配
            System.out.println("Found value: " + m.group(1)); // 非数字部分
            System.out.println("Found value: " + m.group(2)); // 数字部分
            System.out.println("Found value: " + m.group(3)); // 剩余部分
        } else {
            System.out.println("NO MATCH");
        }
    }
}

输出:

Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT
Found value: 3000
Found value: ! OK?

3. 常见正则表达式语法

字符说明
\将下一个字符标记为特殊字符或转义符。例如,\d 表示数字。
^匹配字符串的开始位置。
$匹配字符串的结尾位置。
.匹配除 \r\n 以外的任何单个字符。
*匹配前面的子表达式 0 次或多次。
+匹配前面的子表达式 1 次或多次。
?匹配前面的子表达式 0 次或 1 次。
{n}恰好匹配 n 次。
{n,}至少匹配 n 次。
{n,m}匹配 n 次到 m 次(包括 m 次)。
[]匹配字符集合中的任一字符。例如,[abc] 匹配 "a"、"b" 或 "c"。
[^...]匹配不在括号中的任意字符。例如,[^abc] 匹配任何不是 "a"、"b" 或 "c" 的字符。
(pattern)捕获分组:匹配 pattern 并捕获该子表达式。
(?:pattern)非捕获分组,不存储匹配。
\d数字 [0-9]
\D非数字字符。
\w字母、数字或下划线 [A-Za-z0-9_]
\W非字母、非数字字符或非下划线。
\s匹配空白字符(包括空格、换行等)。
\S匹配非空白字符。
``
(?=pattern)正向预测先行匹配。
(?!pattern)负向预测先行匹配。

示例:数字验证

模式:^\d+(\.\d+)?$

  • ^: 起始位置。
  • \d+: 一个或多个数字。
  • (\.\d+)?: 可选的小数点和后续数字。
  • $: 字符串结束。

匹配:51.52.21

4. Matcher 方法详解

方法作用
find()查找下一个匹配项。
find(int start)从指定位置开始查找。
matches()尝试匹配整个字符串。
lookingAt()从字符串开头尝试匹配。
group()返回上一次匹配的结果。
group(int group)返回指定捕获组的内容。
groupCount()返回捕获组总数(不包括 group(0))。
start()返回匹配的起始索引。
end()返回匹配的结束索引。
replaceAll(String replacement)替换所有匹配的子字符串。
replaceFirst(String replacement)替换第一个匹配的子字符串。
appendReplacement(StringBuffer sb, String replacement)将当前匹配替换为新内容并追加到 StringBuffer
appendTail(StringBuffer sb)将输入字符串余下的部分追加到 StringBuffer 中。

5. replaceFirst & replaceAll 方法

import java.util.regex.*;

public class RegexMatches {
    public static void main(String[] args) {
        String regex = "dog";
        String input = "The dog says woof. All dogs say woof.";
        String replacement = "cat";

        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(input);

        System.out.println(m.replaceFirst(replacement)); // 替换第一个
        System.out.println(m.replaceAll(replacement));  // 替换所有
    }
}

输出

The cat says woof. All dogs say woof.
The cat says woof. All cats say woof.

6. appendReplacement & appendTail 方法

import java.util.regex.*;

public class RegexMatches {
    public static void main(String[] args) {
        String regex = "a*b";
        String input = "aabfooaabfooabfoobkkk";
        String replacement = "-";

        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(input);

        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, replacement);
        }
        m.appendTail(sb);

        System.out.println(sb.toString());
    }
}

输出

-foo-foo-foo-kkk

注意事项:

  1. 转义字符

    • 在 Java 中,为了正确描述 \,需要双反斜杠 \\。例如,\d 在 Java 中表示为 \\d
  2. 特殊操作

    • 贪婪与非贪婪匹配:
      • 贪婪(默认):.*
      • 非贪婪:.*?
  3. PatternSyntaxException: 如果正则表达式非法,将抛出 PatternSyntaxException

(三) Scanner 获取用户输入

1. 创建 Scanner 对象

要使用 Scanner 类,需要导入 java.util.Scanner,并创建 Scanner 对象。

import java.util.Scanner;

Scanner scan = new Scanner(System.in); // 从键盘接收数据

2. nextnextLine 方法

next() 用于读取键盘输入的下一个有效字符串,以空格、制表符或换行作为分隔。

只能读取到第一个有效字符串,遇到空格或换行即结束。

示例代码:

import java.util.Scanner;

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

        System.out.println("请输入字符串(next 方法):");

        // 判断是否有输入数据
        if (scan.hasNext()) {
            String str = scan.next(); // 使用 next() 方法获取输入
            System.out.println("输入的数据为:" + str);
        }

        scan.close(); // 关闭 Scanner
    }
}

运行结果:

输入:
runoob com

输出:
输入的数据为:runoob

nextLine() 用于读取输入的一整行字符串,以回车键(Enter)为结束符,即读取回车前的所有内容(包括空格)。可以读取带空格的字符串。

示例代码:

import java.util.Scanner;

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

        System.out.println("请输入字符串(nextLine 方法):");

        // 判断是否有输入数据
        if (scan.hasNextLine()) {
            String str = scan.nextLine(); // 使用 nextLine() 方法获取一整行数据
            System.out.println("输入的数据为:" + str);
        }

        scan.close(); // 关闭 Scanner
    }
}

运行结果:

输入:
runoob com

输出:
输入的数据为:runoob com

next()nextLine() 的区别

特性next()nextLine()
读取方式以空格、制表符、换行符为分隔回车键 为结束符
获取内容读取空格或回车前的单个有效字符串读取输入的整行,包括空格
能否读取空格不能获取空白(会跳过空白字符)可以获取带有空格的字符串
适合场景适合读取单一值(如单词、数字)适合读取整行输入内容

关闭 Scanner 对象

  • 用完 Scanner 后应当调用 close() 方法释放系统资源。

next()nextLine() 的交互问题

  • 如果在调用 next() 或其他类似方法后立即调用 nextLine(),可能会读取到空白。
  • 解决方案:在调用 next() 后,添加一个冗余的 nextLine() 以清除缓冲区。

3. 读取指定类型的数据

Scanner 类提供了多种方法用于读取特定类型的数据,例如 nextInt() 读取整数,nextFloat() 读取浮点数等。但在读取前,建议使用 hasNextXxx() 方法判断输入是否符合指定类型。

示例代码:

import java.util.Scanner;

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

        System.out.print("输入整数:");
        if (scan.hasNextInt()) { // 判断是否为整数
            int i = scan.nextInt(); // 读取整数
            System.out.println("整数数据:" + i);
        } else {
            System.out.println("输入的不是整数!");
        }

        System.out.print("输入小数:");
        if (scan.hasNextFloat()) { // 判断是否为小数
            float f = scan.nextFloat(); // 读取小数
            System.out.println("小数数据:" + f);
        } else {
            System.out.println("输入的不是小数!");
        }

        scan.close(); // 关闭 Scanner
    }
}

运行结果:

输入整数:12
整数数据:12
输入小数:3.14
小数数据:3.14

4. 连续接收多个输入值

可以使用 hasNextDouble() 来判断输入是否为数字,从而动态接收多个数字,直到输入非数字结束。

示例代码:

import java.util.Scanner;

public class ScannerDemo {
    public static void main(String[] args) {
        System.out.println("请输入数字:");

        Scanner scan = new Scanner(System.in);
        double sum = 0; // 总和
        int count = 0;  // 个数

        while (scan.hasNextDouble()) { // 判断是否为数字输入
            double num = scan.nextDouble(); // 读取数字
            sum += num;
            count++; // 计数
        }

        // 输入非数字后停止计算
        System.out.println("输入的数共有:" + count + " 个");
        System.out.println("总和为:" + sum);
        System.out.println("平均值为:" + (sum / count));

        scan.close(); // 关闭 Scanner
    }
}

运行结果:

请输入数字:
10
20
30
40
end

输入的数共有:4 个
总和为:100.0
平均值为:25.0

5. 常用 Scanner 方法列表

数据读取方法

方法功能
next()读取下一个字符串,以空格或换行结束。
nextLine()读取整行字符串,以回车键结束。
nextInt()读取整数。
nextDouble()读取双精度浮点数(double 类型)。
nextFloat()读取单精度浮点数(float 类型)。
nextLong()读取长整型(long 类型)。
nextShort()读取短整数(short 类型)。
nextByte()读取字节数据(byte 类型)。
nextBoolean()读取布尔值(truefalse)。

输入验证方法

方法功能
hasNext()判断是否还有以空格或换行分隔的输入。
hasNextLine()判断是否还有整行输入。
hasNextInt()判断下一个输入是否是整数。
hasNextDouble()判断下一个输入是否是双精度浮点数。
hasNextFloat()判断下一个输入是否是单精度浮点数。
hasNextBoolean()判断下一个输入是否是布尔值。