Dete类
Date类有两个构造方法,无参的构造方法得到的时间是当前时间,有参构造方法传入一个毫秒值。
1
2
3
4
5
6
7
|
public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
fastTime = date;
}
|
代码:
1
2
3
4
5
6
7
8
9
|
public class TimeDemo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
}
//输出结果:
Fri Apr 23 14:24:50 CST 2020
|
输出结果不是我们需要的,一般我们需要一些指定的格式例如:2020-05-20 12:00:00或2020/05/20 12:00:00
甚至是不要时分秒的格式。所以Date都是和SimpleDateFormat一起使用。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class TimeDemo {
public static void main(String[] args) throws ParseException {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat simpleDateFormat3 = new SimpleDateFormat("yyyyMMdd HHmmss");
System.out.println("===================Date格式换转String==================");
System.out.println("格式化时间Date,返回类型String:" + simpleDateFormat.format(date));
System.out.println("格式化时间Date,返回类型String:" + simpleDateFormat1.format(date));
System.out.println("格式化时间Date,返回类型String:" + simpleDateFormat2.format(date));
System.out.println("格式化时间Date,返回类型String:" + simpleDateFormat3.format(date));
System.out.println("===================String格式化转Date==================");
String str = "2020-12-12 12:00:00";
Date parse = simpleDateFormat.parse(str);
System.out.println(parse);
}
}
|
但是SimpleDateFormat在多线程下,存在线程安全问题。
例如有下面这个工具类。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class DateUtil {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException {
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException{
return sdf.parse(strDate);
}
}
|
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class TimeDemo {
public static void main(String[] args) throws ParseException {
String dateStr = "2020-11-03 10:02:47";
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
System.out.println(DateUtil.parse(dateStr));
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
}
|
报错:
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
|
Exception in thread "Thread-5" Exception in thread "Thread-2" Exception in thread "Thread-8" Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "E.21111"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.log.DateUtil.parse(DateUtil.java:17)
at org.example.log.TimeDemo.lambda$main$0(TimeDemo.java:13)
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: For input string: "1111E.21111"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.log.DateUtil.parse(DateUtil.java:17)
at org.example.log.TimeDemo.lambda$main$0(TimeDemo.java:13)
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: For input string: "1111E.21111E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.log.DateUtil.parse(DateUtil.java:17)
at org.example.log.TimeDemo.lambda$main$0(TimeDemo.java:13)
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: For input string: "E.21111E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.log.DateUtil.parse(DateUtil.java:17)
at org.example.log.TimeDemo.lambda$main$0(TimeDemo.java:13)
at java.lang.Thread.run(Thread.java:748)
Tue Nov 03 10:02:47 CST 2020
Tue Nov 03 10:22:47 CST 2020
Tue Nov 03 10:22:47 CST 2020
Tue Nov 03 10:02:47 CST 2020
Tue Nov 03 10:02:47 CST 2020
Tue Nov 03 10:02:47 CST 2020
|
parse方法的调用情况:
先调用DateFormat对象的public Date parse(String source) throws ParseException
DateFormat对象的parse方法调用SimpleDateFormat对象的public Date parse(String text, ParsePosition pos)
SimpleDateFormat对象的parse方法调用 CalendarBuilder 对象的 Calendar establish(Calendar cal)
在 establish()中,做了cal.clear();把calendar清空且没有设置新值。如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题
解决方法:
1.创建局部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class DateUtil {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException {
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}
}
|
2.使用同步代码块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class DateUtil {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws ParseException {
synchronized (sdf) {
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException {
synchronized (sdf) {
return sdf.parse(strDate);
}
}
}
|
3.使用ThreadLocal线程独享
1
2
3
4
5
6
7
8
9
10
11
12
|
public class DateUtil {
private static ThreadLocal<DateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String formatDate(Date date) throws ParseException {
return threadLocal.get().format(date);
}
public static Date parse(String strDate) throws ParseException {
return threadLocal.get().parse(strDate);
}
}
|
LocalDate类
从Java 8之后,Java里面添加了许多的新特性,其中一个最常见也是最实用的便是日期处理的类——LocalDate。
java.time.LocalDate ->只对年月日做出处理
java.time.LocalTime ->只对时分秒纳秒做出处理
java.time.LocalDateTime ->同时可以处理年月日和时分秒
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class LocalDateDemo {
public static void main(String[] args) {
// 当前日期:yyyy-MM-dd
LocalDate localDate = LocalDate.now();
// 年份
int year = localDate.getYear();
// 月份
int month = localDate.getMonthValue();
// 日期
int dayOfMonth = localDate.getDayOfMonth();
System.out.println(localDate);
System.out.println(year);
System.out.println(month);
System.out.println(dayOfMonth);
}
}
|
LocalDate的使用比Date方便很多,格式化也不需要用SimpleDateFormat。注意LocalDate不包含时分秒,想要时分秒使用LocalDateTime这个类。
1
2
3
4
5
6
7
8
9
10
11
12
|
public class LocalDateDemo {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatters1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter formatters2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str1 = localDate.format(formatters1);
String str2 = localDateTime.format(formatters2);
System.out.println(str1);
System.out.println(str2);
}
}
|
LocalDate/LocalDateTime时间的加减修改
1
2
3
4
5
6
7
8
9
10
11
12
|
public class LocalDateDemo {
public static void main(String[] args) {
LocalDate localDate = LocalDate.of(2020,12,12);
LocalDateTime localDateTime = LocalDateTime.of(2020,11,1,16,12,12);
System.out.println(localDate);
System.out.println(localDateTime);
LocalDate result = localDate.plusYears(1);
LocalDateTime result2 = localDateTime.plusYears(2);
System.out.println(result);
System.out.println(result2);
}
}
|
LocalDate的时间相加相关的方法:
- public LocalDateTime plus(TemporalAmount amountToAdd)
- public LocalDateTime plus(long amountToAdd, TemporalUnit unit)
- public LocalDateTime plusYears(long years)
- public LocalDateTime plusMonths(long months)
- public LocalDateTime plusWeeks(long weeks)
- public LocalDateTime plusDays(long days)
LocalDateTime的时间相加相关的方法:
- public LocalDateTime plus(TemporalAmount amountToAdd)
- public LocalDateTime plus(long amountToAdd, TemporalUnit unit)
- public LocalDateTime plusYears(long years)
- public LocalDateTime plusMonths(long months)
- public LocalDateTime plusWeeks(long weeks)
- public LocalDateTime plusDays(long days)
- public LocalDateTime plusHours(long hours)
- public LocalDateTime plusMinutes(long minutes)
- public LocalDateTime plusNanos(long nanos)
LocalDateTime就是比LocalDate多了对于时分秒的操作。减法的操作就是把plus换成minus就是时间减法操作的函数了。
一顿操作下来LocalDate/LocalDateTime比Date好用太多了,也方便很多。最重要的是在格式化Date的时候使用到的SimpleDateFormat是线程不安全的。
而在LocalDate/LocalDateTime源码的注解中写道:This class is immutable and thread-safe
。LocalDate/LocalDateTime是不可变类,是线程安全的。