JDK8之前的日期时间API
System类中获取时间戳的方法
System类提供的public static long currentTimeMillis() 当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
此方法适于计算时间差
例如我想测试不同排序算法对10W个数据排序的时间
就可以在执行排序算发之前获取一下当前毫秒数
等排序算法执行完毕后,再获取一下当前毫秒数
二者只差即为排序算法执行时间
1 2 3 4 5
| @Test public void test1() { long time = System.currentTimeMillis(); System.out.println(time); }
|
Java中两个Date类的使用
java.util.Date类和java.sql.Date类
- 两个构造器的使用
- Date() 创建一个对应当前时间的Date对象
- Date(long time) 创建一个指定毫秒数的Date对象
- 两个方法的使用
- toString() 显示当前时间的 年,月,日,时,分,秒
- getTime() 获取当前Date对象对应的毫秒数(时间戳)
- java.sql.Date对应着数据库中的日期类型变量
- 如何实例化
- 如何将java.util.Date对象转化为java.sql.Date对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void test2() { Date date1 = new Date(); System.out.println(date1.toString()); System.out.println(date1.getTime());
Date date2 = new Date(1650045342397L); System.out.println(date2);
java.sql.Date date3 = new java.sql.Date(1650045342397L); System.out.println(date3);
Date date4 = new java.sql.Date(1650045342397L); java.sql.Date date5 = (java.sql.Date) date4;
Date date6 = new Date(); java.sql.Date date7 = new java.sql.Date(date6.getTime()); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Test public void test3() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat(); Date date = new Date(); System.out.println(date); System.out.println(sdf.format(date));
String str = "20-12-18 上午8:26"; Date date1 = sdf.parse(str); System.out.println(date1);
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); System.out.println(sdf1.format(date));
Date date2 = sdf1.parse("2077.08.26 23:12:13"); System.out.println(date2); }
|
注意大小写:大写Y表示weekYear 大写H表示24小时制 小写h表示12小时制
详情参阅API文档Class SimpleDateFormat
练习一: 字符串"2020-09-08"转换为java.sql.Date
1 2 3 4 5 6 7 8
| @Test public void test4() throws ParseException { String str = "2020-09-08"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf.parse(str); java.sql.Date date1 = new java.sql.Date(date.getTime()); System.out.println(date1); }
|
练习二: 三天打鱼两天晒网
从1990.01.01开始 三天打鱼两天晒网
那么在2022.04.14时 是在打鱼还是晒网
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void test5() throws ParseException { String str = "1990.01.01"; String str1 = "2022.04.14"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd"); Date startDate = sdf.parse(str); Date endDate = sdf.parse(str1); long a = (endDate.getTime() - startDate.getTime()) / (1000 * 24 * 60 * 60) + 1; long b = a % 5; if (b == 1 || b == 2 || b == 3) System.out.println("打鱼"); else System.out.println("晒网"); }
|
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Test public void test6() { Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass());
int days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);
days = calendar.get(Calendar.DAY_OF_YEAR); System.out.println(days);
days = calendar.get(Calendar.DAY_OF_WEEK); System.out.println(days);
int months = calendar.get(Calendar.MONTH); System.out.println(months);
calendar.set(Calendar.DAY_OF_MONTH, 13); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);
calendar.add(Calendar.DAY_OF_MONTH, 10); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);
Date date = calendar.getTime(); System.out.println(date);
Date date1 = new Date(); calendar.setTime(date1); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); }
|
JDK8中日期时间API的介绍
新日期时间API出现的背景
如果我们可以跟别人说:“我们在1502643933071见面,别晚了!”那么就再简单不过了。但是我们希望时间与昼夜和四季有关,于是事情就变复杂了。JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是:
- 可变性:像日期和时间这样的类应该是不可变的。
- 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
- 格式化:格式化只对Date有用,Calendar则不行。
- 此外,它们也不是线程安全的;不能处理闰秒等。
1 2 3 4 5 6 7 8 9
| @Test public void test7() { Date date = new Date(2022,4,16); System.out.println(date); date = new Date(2022-1900,4-1,16); System.out.println(date); }
|
第三次引入的API是成功的,并且Java 8中引入的java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。
Java 8 吸收了Joda-Time 的精华,以一个新的开始为Java 创建优秀的API。新的java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。历史悠久的Date 类新增了toInstant()方法,用于把Date 转换成新的表示形式。这些新增的本地化时间日期API 大大简化了日期时间和本地化的管理。
LocalDate、LocalTime、LocalDateTime的使用
LocalDate、LocalTime、LocalDateTime类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
- LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储生日、纪念日等日期。
- LocalTime表示一个时间,而不是日期。
- LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。
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
| @Test public void test8() { System.out.println("获取年月日"); LocalDate localDate = LocalDate.now(); System.out.println(localDate);
System.out.println("获取时分秒"); LocalTime localTime = LocalTime.now(); System.out.println(localTime);
System.out.println("获取年月日时分秒"); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime);
System.out.println("设置指定的年,月,日,时,分,秒 没有偏移量"); LocalDateTime localDateTime1 = LocalDateTime.of(2077, 8, 26, 13, 14, 21); System.out.println(localDateTime1);
System.out.println("getXxx()"); System.out.println(localDateTime.getDayOfMonth()); System.out.println(localDateTime.getDayOfWeek()); System.out.println(localDateTime.getDayOfYear()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getMinute());
System.out.println("体现不可变性"); LocalDateTime localDateTime2 = localDateTime1.withDayOfMonth(10); System.out.println(localDateTime1); System.out.println(localDateTime2); LocalDateTime localDateTime3 = localDateTime1.plusDays(10); LocalDateTime localDateTime4 = localDateTime1.minusHours(8); System.out.println(localDateTime3); System.out.println(localDateTime4); }
|
方法 |
描述 |
now() |
静态方法,根据当前时间创建对象/指定时区的对象 |
of() |
静态方法,根据指定日期/时间创建对象 |
getDayOfMonth()/getDayOfYear() |
获得月份天数(1-31)/获得年份天数(1-366) |
getDayOfWeek() |
获得星期几(返回一个DayOfWeek枚举值) |
getMonth() |
获得月份,返回一个Month枚举值 |
getHour()/getMinute()/getSecond() |
获得当前对象对应的小时、分钟、秒 |
withDayOfMonth()/withDayOfYear()/ withMonth()/withYear() |
将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象 |
minusMonths()/minusWeeks()/minusDays()/ minusYears()/minusHours() |
从当前对象减去几月、几周、几天、几年、几小时 |
Instant类的使用
-
Instant:时间线上的一个瞬时点。这可能被用来记录应用程序中的事件时间戳。
-
在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
-
java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
1秒 = 1000毫秒 = 10^6微秒 = 10^9纳秒
方法 |
描述 |
now() |
静态方法,返回默认UTC时区的Instant类的对象 |
of() |
静态方法,返回在1970-01-01 00:00:00基础上 加上指定毫秒数之后的Instant类的对象 |
atOffset(ZoneOffset offset) |
结合即时的偏移来创建一个OffsetDateTime |
toEpochMilli() |
返回1970-01-0100:00:00到当前时间的毫秒数,即为时间戳 |
时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void test9() { Instant instant = Instant.now(); System.out.println(instant);
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime);
long milli = instant.toEpochMilli(); System.out.println(milli);
Instant instant1 = Instant.ofEpochMilli(1650166369891L); System.out.println(instant1); }
|
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:
- 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
- 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)
- 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
依旧是自定义格式最常用
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
| @Test public void test10() { System.out.println("方式一"); DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME; LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime); System.out.println(dateTimeFormatter1.format(localDateTime));
TemporalAccessor parse = dateTimeFormatter1.parse("2077-01-01T00:00:00.000"); System.out.println(parse);
System.out.println("方式二"); DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); System.out.println(dateTimeFormatter2.format(localDateTime));
System.out.println("方式三"); DateTimeFormatter dateTimeFormatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); System.out.println(dateTimeFormatter3.format(localDateTime)); TemporalAccessor parse1 = dateTimeFormatter3.parse("2077-01-17 12:52:47"); System.out.println(parse1); }
|
Java比较器
概述
Java中的对象,正常情况下,只能进行比较:==或 != 。不能使用 >或<的,但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。 如何实现?使用两个接口中的任何一个:Comparable或 Comparator
Java实现对象排序的方式有两种:
- 自然排序:java.lang.Comparable
- 定制排序:java.util.Comparator
Comparable自然排序举例
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Test public void test11() { String[] arr = new String[]{"AA", "GG", "EE", "FF", "DD", "CC", "BB"}; Arrays.sort(arr); System.out.println(Arrays.toString(arr));
Integer[] nums = new Integer[]{9, 4, 5, 7, 8, 2, 1, 3, 6}; Arrays.sort(nums); System.out.println(Arrays.toString(nums)); }
|
自定义类实现Comparable自然排序
对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序
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
| public class Goods implements Comparable { String name; double price;
public Goods(String name, double price) { this.name = name; this.price = price; }
public Goods() { }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override public String toString() { return "Goods{" + "name='" + name + '\'' + ", price=" + price + '}'; } @Override public int compareTo(Object o) { if (o instanceof Goods) { Goods goods = (Goods) o; return Double.compare(this.getPrice(),goods.getPrice()); } throw new RuntimeException("输入数据类型不一致"); } }
@Test public void test12() { Goods[] goods = new Goods[4]; goods[0] = new Goods("联想", 20); goods[1] = new Goods("小米", 50); goods[2] = new Goods("华为", 40); goods[3] = new Goods("罗技", 30);
Arrays.sort(goods); System.out.println(Arrays.toString(goods));
}
|
使用Comparator实现定制排序
-
背景:当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序
-
重写compare(Object o1,Object o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
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
| @Test public void test13() { String[] arr = new String[]{"AA", "GG", "EE", "FF", "DD", "CC", "BB"}; Arrays.sort(arr, new Comparator<String>() { @Override public int compare(String o1, String o2) { if (o1 instanceof String && o2 instanceof String) { return -o1.compareTo(o2); } throw new RuntimeException("输入的数据类型不一致"); } }); System.out.println(Arrays.toString(arr));
Integer[] nums = new Integer[]{9, 4, 5, 7, 8, 2, 1, 3, 6}; Arrays.sort(nums, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return -o1.compareTo(o2); } }); System.out.println(Arrays.toString(nums));
Goods[] goods = new Goods[4]; goods[0] = new Goods("联想", 20); goods[1] = new Goods("小米", 50); goods[2] = new Goods("华为", 40); goods[3] = new Goods("罗技", 30); Arrays.sort(goods, new Comparator<Goods>() { @Override public int compare(Goods o1, Goods o2) { return -o1.compareTo(o2); } }); System.out.println(Arrays.toString(goods)); }
|
Comparable接口与Comparator的使用的对比