??作者主页:小虚竹
??作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家🏆,华为云享专家🏆,掘金年度人气作者🏆,阿里云专家博主🏆
??技术活,该赏
??点赞 👍 收藏 ?再看,养成习惯
开篇介绍
? 学习Java的经典名著非常多,这本书是你一定不能错过的:由凯.霍斯特曼写的《Core Java》,中文版名为《Java核心技术》。这本书几乎出现在每个“学Java要看什么书”类似的书单里,影响了几代技术人。
豆瓣的评分很高
主题
? 《Java核心技术》 这本书好在哪?我们都知道技术会过时的,技术会升级的,尤其是JAVA技术一直在持续不断地更新。《Java核心技术》 好在与时俱进,我查了下这本书的历程,1996年出版以来,至今已经更新了11版。写一本需要投入大量的精力,升级一本书也需要大量的精力,当前书的内容已经补充了JAVA SE 9,10,11的新版本最新特性,这对于我来说,帮助挺大的,可以及时跟上JAVA前进的脚步。
特色
? 本书适合各个层次的Java开发者阅读,同时也可作为高等院校讲授面向对象程序设计语言以及Java语言的参考教材。
对我的影响
? 《Java核心技术》 分为两本书,卷一和卷二。卷一主要是介绍JAVA语言的基本概念以及程序设计的基础知识,我在这里吸收到了基础知识,对原先掌握的知识进行了查缺补漏。对我编写专栏 《JAVA筑基100例》 起到了指导作用。
卷二主要涉及企业级专业软件开发时需要了解的高级主题,我更喜欢卷二,因为卷二的章节大部分是相互独立的,可以直接研究自己感兴趣的章节,不用关心前后章节。学习卷二的内容,我掌握了不少新知识,例如JAVA8的流库,文件的加锁机制,文件的ZIP压缩和解压缩技术,日期和时间新旧API对比,和网络通信等。
内容分享:日期和时间新旧API对比
我简单分享一个知识点:日期和时间新旧API对比
java提供了两套处理日期和时间的API
1、旧的API,放在java.util 这个包下的:比较常用的有Date和Calendar等
2、新的API是java 8新引入的,放在java.time 这个包下的:LocalDateTime,ZonedDateTime,DateTimeFormatter和Instant等
为什么会有两套日期时间API,这个是有历史原因的,旧的API是jdk刚开始就提供的,随着版本的升级,逐渐发现原先的api不满足需要,暴露了一些问题,所以在java 8 这个版本中,重新引入新API。
这两套API都要了解,为什么呢?
因为java 8 发布时间是2014年,很多之前的系统还是沿用旧的API,所以这两套API都要了解,同时还要掌握两套API相互转化的技术。
Date
Date类说明
Date类负责时间的表示,在计算机中,时间的表示是一个较大的概念,现有的系统基本都是利用从1970.1.1 00:00:00 到当前时间的毫秒数进行计时,这个时间称为epoch(时间戳)
package java.util;
public class Date
implements java.io.Serializable, Cloneable, Comparable<Date>
{
...
private transient long fastTime;
....
}
java.util.Date是java提供表示日期和时间的类,类里有个long 类型的变量fastTime,它是用来存储以毫秒表示的时间戳。
date常用的用法
import java.util.Date;
-----------------------------------------
Date date = new Date();
System.out.println("获取当前时间:"+date);
System.out.println("获取时间戳:"+date.getTime());
Date afterDate = new Date(date.getTime()-3600*24*1000);
System.out.println("after:"+date.after(afterDate));
System.out.println("after:"+date.after(date));
Date beforeDate = new Date(date.getTime()+3600*24*1000);
System.out.println("before:"+date.before(beforeDate));
System.out.println("before:"+date.before(date));
System.out.println("compareTo:"+date.compareTo(date));
System.out.println("compareTo:"+date.compareTo(afterDate));
System.out.println("compareTo:"+date.compareTo(beforeDate));
System.out.println("转为字符串:"+date.toString());
System.out.println("转为GMT时区:"+date.toGMTString());
System.out.println("转为本地时区:"+date.toLocaleString());
自定义时间格式-SimpleDateFormat
date的toString方法转成字符串,不是我们想要的时间格式,如果要自定义时间格式,就要使用SimpleDateFormat
Date date = new Date();
System.out.println("获取当前时间:"+date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(date));
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
System.out.println(simpleDateFormat1.format(date));
SimpleDateFormat也可以方便的将字符串转成Date
String str = "2021-07-13 23:48:23";
try {
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
日期和时间格式化参数说明
yyyy:年
MM:月
dd:日
hh:1~12小时制(1-12)
HH:24小时制(0-23)
mm:分
ss:秒
S:毫秒
E:星期几
D:一年中的第几天
F:一月中的第几个星期(会把这个月总共过的天数除以7)
w:一年中的第几个星期
W:一月中的第几星期(会根据实际情况来算)
a:上下午标识
k:和HH差不多,表示一天24小时制(1-24)。
K:和hh差不多,表示一天12小时制(0-11)。
z:表示时区
SimpleDateFormat线程为什么是线程不安全的呢?
来看看SimpleDateFormat的源码,先看format方法:
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
calendar.setTime(date);
...
}
问题就出在成员变量calendar,如果在使用SimpleDateFormat时,用static定义,那SimpleDateFormat变成了共享变量。那SimpleDateFormat中的calendar就可以被多个线程访问到。
SimpleDateFormat的parse方法也是线程不安全的:
public Date parse(String text, ParsePosition pos)
{
...
Date parsedDate;
try {
parsedDate = calb.establish(calendar).getTime();
if (ambiguousYear[0]) {
if (parsedDate.before(defaultCenturyStart)) {
parsedDate = calb.addYear(100).establish(calendar).getTime();
}
}
}
catch (IllegalArgumentException e) {
pos.errorIndex = start;
pos.index = oldStart;
return null;
}
return parsedDate;
}
由源码可知,最后是调用**parsedDate = calb.establish(calendar).getTime();**获取返回值。方法的参数是calendar,calendar可以被多个线程访问到,存在线程不安全问题。
我们再来看看**calb.establish(calendar)**的源码
calb.establish(calendar)方法先后调用了cal.clear()和cal.set(),先清理值,再设值。但是这两个操作并不是原子性的,也没有线程安全机制来保证,导致多线程并发时,可能会引起cal的值出现问题了。
验证SimpleDateFormat线程不安全
public class SimpleDateFormatDemoTest {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(5);
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}
}
}
}
出现了两次false,说明线程是不安全的。而且还抛异常,这个就严重了。
LocalDateTime
LocalDateTime类说明
表示当前日期时间,相当于:yyyy-MM-ddTHH:mm:ss
LocalDateTime常用的用法
获取当前日期和时间
LocalDate d = LocalDate.now();
LocalTime t = LocalTime.now();
LocalDateTime dt = LocalDateTime.now();
System.out.println(d);
System.out.println(t);
System.out.println(dt);
由运行结果可行,本地日期时间通过now()获取到的总是以当前默认时区返回的
获取指定日期和时间
LocalDate d2 = LocalDate.of(2021, 07, 14);
LocalTime t2 = LocalTime.of(13, 14, 20);
LocalDateTime dt2 = LocalDateTime.of(2021, 07, 14, 13, 14, 20);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
System.out.println("指定日期时间:"+dt2);
System.out.println("指定日期时间:"+dt3);
日期时间的加减法及修改
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("------------------时间的加减法及修改-----------------------");
System.out.println("3.当前时间:" + currentTime);
System.out.println("3.当前时间加5年:" + currentTime.plusYears(5));
System.out.println("3.当前时间加2个月:" + currentTime.plusMonths(2));
System.out.println("3.当前时间减2天:" + currentTime.minusDays(2));
System.out.println("3.当前时间减5个小时:" + currentTime.minusHours(5));
System.out.println("3.当前时间加5分钟:" + currentTime.plusMinutes(5));
System.out.println("3.当前时间加20秒:" + currentTime.plusSeconds(20));
System.out.println("3.同时修改(向后加一年,向前减一天,向后加2个小时,向前减5分钟):" + currentTime.plusYears(1).minusDays(1).plusHours(2).minusMinutes(5));
System.out.println("3.修改年为2025年:" + currentTime.withYear(2025));
System.out.println("3.修改月为12月:" + currentTime.withMonth(12));
System.out.println("3.修改日为27日:" + currentTime.withDayOfMonth(27));
System.out.println("3.修改小时为12:" + currentTime.withHour(12));
System.out.println("3.修改分钟为12:" + currentTime.withMinute(12));
System.out.println("3.修改秒为12:" + currentTime.withSecond(12));
线程安全
网上大家都在说JAVA 8提供的LocalDateTime是线程安全的,但是它是如何实现的呢
今天让我们来挖一挖
public final class LocalDateTime
implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable {
...
}
由上面的源码可知,LocalDateTime是不可变类。我们都知道一个Java并发编程规则:不可变对象永远是线程安全的。
对比下Date的源码 ,Date是可变类,所以是线程不安全的。
public class Date
implements java.io.Serializable, Cloneable, Comparable<Date>
{
...
}
LocalDateTime和Date相互转化
Date转LocalDateTime
System.out.println("------------------方法一:分步写-----------------------");
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = instant.atZone(zoneId);
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
System.out.println("方法一:原Date = " + date);
System.out.println("方法一:转化后的LocalDateTime = " + localDateTime);
System.out.println("------------------方法二:一步到位(推荐使用)-----------------------");
Date todayDate = new Date();
LocalDateTime ldt = Instant.ofEpochMilli(todayDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println("方法二:原Date = " + todayDate);
System.out.println("方法二:转化后的LocalDateTime = " + ldt);
LocalDateTime转Date
System.out.println("------------------方法一:分步写-----------------------");
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
Instant instant = zonedDateTime.toInstant();
Date date = Date.from(instant);
System.out.println("方法一:原LocalDateTime = " + localDateTime);
System.out.println("方法一:转化后的Date = " + date);
System.out.println("------------------方法二:一步到位(推荐使用)-----------------------");
LocalDateTime now = LocalDateTime.now();
Date dateResult = Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("方法二:原LocalDateTime = " + now);
System.out.println("方法二:转化后的Date = " + dateResult);
DateTimeFormatter
DateTimeFormatter类说明
DateTimeFormatter的作用是进行格式化显示,且DateTimeFormatter是不可变类且是线程安全的。
public final class DateTimeFormatter {
...
}
说到时间的格式化显示,就要说老朋友SimpleDateFormat了,之前格式化Date就要用上。但是我们知道SimpleDateFormat是线程不安全的,还不清楚的,请看这里–>
DateTimeFormatter常用的用法
ZonedDateTime zonedDateTime = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
System.out.println(formatter.format(zonedDateTime));
DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
System.out.println(usFormatter.format(zonedDateTime));
DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
System.out.println(chinaFormatter.format(zonedDateTime));
总结
? 《Java核心技术》 它不仅仅是一本书,还是学习JAVA语言的进阶路径指导。它高层建瓯地介绍了Java语言的核心概念、语法、重要特性,又对Java语言基础知识进行了专业级详解。既照顾了新手易入门,易上手,也照顾到了中高级JAVA开发人员需要了解的高级主题。
? 书也要与时俱进,刚好响应了技术更新换代的市场需求。对于开发人员来说,学习有用的新技术是必要的。选择大于努力,一本优秀的书籍可以让我们不走弯路,而 《Java核心技术》 正是我们值得花时间去学习研究的一本好书。
推荐小虚竹技术专栏
《JAVA从零到壹》
《JAVA筑基100例》
《JDK源码剖析》
|