《Java 编程的逻辑》笔记——第7章 常用基础类(三)

本文深入探讨了Java中日期和时间处理的API,包括Date、Calendar、TimeZone、DateFormat和SimpleDateFormat的使用,强调了它们的局限性和问题。此外,还介绍了Math.random和Random类在生成随机数方面的应用,讨论了随机数的原理和一些实际场景,如随机密码生成、洗牌算法、抢红包算法和北京购车摇号算法。
摘要由CSDN通过智能技术生成

声明:

本博客是本人在学习《Java 编程的逻辑》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

7.5 剖析日期和时间

本节,我们讨论 Java 中日期和时间处理相关的 API。

日期和时间是一个比较复杂的概念,Java 8 之前的设计有一些不足,业界有一个广泛使用的第三方类库 Joda-Time,Java 8 受 Joda-Time 影响,重新设计了日期和时间 API,新增了一个包 java.time。

虽然 Java 8 之前的 API 有一些不足,但依然是被大量使用的,本节只介绍 Java 8 之前的 API。

下面,我们先来看一些基本概念,然后再介绍 Java 的日期和时间 API。

7.5.1 基本概念

7.5.1.1 时区

我们都知道,同一时刻,世界上各个地区的时间可能是不一样的,具体时间与时区有关,一共有 24 个时区,英国格林尼治是 0 时区,北京是东八区,也就是说格林尼治凌晨 1 点,北京是早上 9 点。0 时区的时间也称为 GMT+0 时间,GMT 是格林尼治标准时间,北京的时间就是 GMT+8:00。

7.5.1.2 时刻和纪元时

所有计算机系统内部都用一个整数表示时刻,这个整数是距离格林尼治标准时间 1970 年 1 月 1 日 0 时 0 分 0 秒的毫秒数。为什么要用这个时间呢?更多的是历史原因,本文就不介绍了。

格林尼治标准时间 1970 年 1 月 1 日 0 时 0 分 0 秒也被称为 Epoch Time (纪元时)。

这个整数表示的是一个时刻,与时区无关,世界上各个地方都是同一个时刻,但各个地区对这个时刻的解读,如年月日时分秒,可能是不一样的。

如何表示 1970 年以前的时间呢?使用负数。

7.5.1.3 年历

我们都知道,中国有公历和农历之分,公历和农历都是年历,不同的年历,一年有多少月,每月有多少天,甚至一天有多少小时,这些可能都是不一样的。

比如,公历有闰年,闰年 2 月是 29 天,而其他年份则是 28 天,其他月份,有的是 30 天,有的是 31 天。农历有闰月,比如闰 7 月,一年就会有两个 7 月,一共 13 个月。

公历是世界上广泛采用的年历,除了公历,还有其他一些年历,比如日本也有自己的年历。Java API 的设计思想是支持国际化的,支持多种年历,但实际中没有直接支持中国的农历,本文主要讨论公历

简单总结下,时刻是一个绝对时间,对时刻的解读,如年月日周时分秒等,则是相对的,与年历和时区相关

7.5.2 日期和时间 API

Java API 中关于日期和时间,有三个主要的类:

  • Date:表示时刻,即绝对时间,与年月日无关。
  • Calendar:表示年历,Calendar 是一个抽象类,其中表示公历的子类是 GregorianCalendar
  • DateFormat:表示格式化,能够将日期和时间与字符串进行相互转换,DateFormat 也是一个抽象类,其中最常用的子类是 SimpleDateFormat。

还有两个相关的类:

  • TimeZone: 表示时区
  • Locale: 表示国家和语言

下面,我们来看这些类。

7.5.2.1 Date

Date 是 Java API 中最早引入的关于日期的类,一开始,Date 也承载了关于年历的角色,但由于不能支持国际化,其中的很多方法都已经过时了,被标记为了 @Deprecated,不再建议使用。

Date 表示时刻,内部主要是一个 long 类型的值,如下所示:

private transient long fastTime;

fastTime 表示距离纪元时的毫秒数,此处,关于 transient 关键字,我们暂时忽略。

Date 有两个构造方法:

public Date(long date) {
   
    fastTime = date;
}

public Date() {
   
    this(System.currentTimeMillis());
}

第一个构造方法,就是根据传入的毫秒数进行初始化,第二个构造方法是默认构造方法,它根据 System.currentTimeMillis() 的返回值进行初始化。

System.currentTimeMillis() 是一个常用的方法,它返回当前时刻距离纪元时的毫秒数

Date 中的大部分方法都已经过时了,其中没有过时的主要方法有:

public long getTime() // 返回毫秒数
public boolean equals(Object obj) // 判断与其他Date是否相同
// 与其他Date进行比较,如果当前Date的毫秒数小于参数中的,返回-1,相同返回0,否则返回1。
public int compareTo(Date anotherDate)
public boolean before(Date when) // 断是否在给定日期之前
public boolean after(Date when) // 断是否在给定日期之后
public int hashCode() // 哈希值算法与Long类似

7.5.2.2 TimeZone

TimeZone 表示时区,它是一个抽象类,有静态方法用于获取其实例

获取当前的默认时区,代码为:

TimeZone tz = TimeZone.getDefault();
System.out.println(tz.getID());

获取默认时区,并输出其 ID,在我的电脑上,输出为:

Asia/Shanghai

默认时区是在哪里设置的呢,可以更改吗?Java 中有一个系统属性,user.timezone,保存的就是默认时区,系统属性可以通过 System.getProperty 获得,如下所示:

System.out.println(System.getProperty("user.timezone"));

在我的电脑上,输出为:

Asia/Shanghai

系统属性可以在 Java 启动的时候传入参数进行更改,如

java -Duser.timezone=Asia/Shanghai xxxx

TimeZone 也有静态方法,可以获得任意给定时区的实例,比如:

获取美国东部时区

TimeZone tz = TimeZone.getTimeZone("US/Eastern");

ID 除了可以是名称外,还可以是 GMT 形式表示的时区,如:

TimeZone tz = TimeZone.getTimeZone("GMT+08:00");

7.5.2.3 国家和语言 Locale

Locale 表示国家和语言,它有两个主要参数,一个是国家,另一个是语言,每个参数都有一个代码,不过国家并不是必须的

比如说,中国的大陆代码是 CN,台湾地区的代码是 TW,美国的代码是 US,中文语言的代码是 zh,英文是 en。

Locale 类中定义了一些静态变量,表示常见的 Locale,比如:

  • Locale.US:表示美国英语
  • Locale.ENGLISH:表示所有英语
  • Locale.TAIWAN:表示台湾中文
  • Locale.CHINESE:表示所有中文
  • Locale.SIMPLIFIED_CHINESE:表示大陆中文

与 TimeZone 类似,Locale 也有静态方法获取默认值,如:

Locale locale = Locale.getDefault();
System.out.println(locale.toString());

在我的电脑上,输出为:

zh_CN

7.5.2.4 Calendar

Calendar 类是日期和时间操作中的主要类,它表示与 TimeZone 和 Locale 相关的日历信息,可以进行各种相关的运算

内部组成

我们先来看下它的内部组成。

与 Date 类似,Calendar 内部也有一个表示时刻的毫秒数,定义为:

protected long time;

除此之外,Calendar 内部还有一个数组,表示日历中各个字段的值,定义为:

protected int fields[];

这个数组的长度为 17,保存一个日期中各个字段的值,都有哪些字段呢?Calendar 类中定义了一些静态变量,表示这些字段,主要有:

  • Calendar.YEAR:表示年
  • Calendar.MONTH:表示月,一月份是 0,Calendar 同样定义了表示各个月份的静态变量,如 Calendar.JULY 表示 7 月。
  • Calendar.DAY_OF_MONTH:表示日,每月的第一天是 1。
  • Calendar.HOUR_OF_DAY:表示小时,从 0 到 23。
  • Calendar.MINUTE:表示分钟,0 到 59。
  • Calendar.SECOND:表示秒,0 到 59。
  • Calendar.MILLISECOND:表示毫秒,0 到 999。
  • Calendar.DAY_OF_WEEK:表示星期几,周日是 1,周一是 2,周六是 7,Calenar 同样定义了表示各个星期的静态变量,如 Calendar.SUNDAY 表示周日。

获取Calendar实例

Calendar 是抽象类,不能直接创建对象,它提供了四个静态方法,可以获取 Calendar 实例,分别为:

public static Calendar getInstance()
public static Calendar getInstance(Locale aLocale)
public static Calendar getInstance(TimeZone zone)
public static Calendar getInstance(TimeZone zone, Locale aLocale)

最终调用的方法都是需要 TimeZone 和 Locale 的,如果没有,则会使用上面介绍的默认值。getInstance 方法会根据 TimeZone 和 Locale 创建对应的 Calendar 子类对象,在中文系统中,子类一般是表示公历的 GregorianCalendar。

getInstance 方法封装了 Calendar 对象创建的细节,TimeZone 和 Locale 不同,具体的子类可能不同,但都是 Calendar,这种隐藏对象创建细节的方式,是计算机程序中一种常见的设计模式,它有一个名字,叫工厂方法,getInstance 就是一个工厂方法,它生产对象

获取日历信息

与 new Date() 类似,新创建的 Calendar 对象表示的也是当前时间,与 Date 不同的是,Calendar 对象可以方便的获取年月日等日历信息。

来看代码,输出当前时间的各种信息:

Calendar calendar = Calendar.getInstance();
System.out.println("year: "+calendar.get(Calendar.YEAR));
System.out.println("month: "+calendar.get(Calendar.MONTH));
System.out.println("day: "+calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("hour: "+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("minute: "+calendar.get(Calendar.MINUTE));
System.out.println("second: "+calendar.get(Calendar.SECOND));
System.out.println("millisecond: " +calendar.get(Calendar.MILLISECOND));
System.out.println("day_of_week: " + calendar.get(Calendar.DAY_OF_WEEK));

具体输出与执行时的时间和默认的 TimeZone 以及 Locale 有关,在写作时,我的电脑上的输出为:

year: 2016
month: 7
day: 14
hour: 13
minute: 55
second: 51
millisecond: 564
day_of_week: 2

内部,Calendar 会将表示时刻的毫秒数,按照 TimeZone 和 Locale 对应的年历,计算各个日历字段的值,存放在 fields 数组中,Calendar.get 方法获取的就是 fields 数组中对应字段的值。

设置和修改时间

Calendar 支持根据 Date 或毫秒数设置时间:

public final void setTime(Date date)
public void setTimeInMillis(long millis)

也支持根据年月日等日历字段设置时间:

public final void set(int year, int month, int date)
public final void set(int year, int month, int date, int hourOfDay, int minute)
public final void set(int year, int month, int date, int hourOfDay, int minute, int second)
public void set(int field, int value)

除了直接设置,Calendar 支持根据字段增加和减少时间:

public void add(int field, int amount)

amount 为正数表示增加,负数表示减少。

比如说,如果想设置 Calendar 为第二天的下午 2 点 15,代码可以为:

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 14);
calendar.set(Calendar.MINUTE, 15);
calendar
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值