接前一篇 java基礎(chǔ)-時(shí)間相關(guān)(篇一 java7)
這一篇我們講一下java8中時(shí)間類(lèi)的基本使用,順便講一下final修飾符的作用。
基于java7中時(shí)間類(lèi)的諸多問(wèn)題仲墨,在Java8中進(jìn)行了改進(jìn)
首先勋磕,還是一個(gè)常規(guī)使用的代碼悼枢,可以和java7進(jìn)行對(duì)比狂男。
//@Test
public void java8DateTest(){
//日期
LocalDate date = LocalDate.now();
System.out.println(date.toString());
//output 2019-11-20
//分別獲取年月日
System.out.printf("年=%d 月=%d 日=%d",date.getYear(),date.getMonthValue(),date.getDayOfMonth());
System.out.println(" ");
//output 年=2019 月=11 日=20
//時(shí)間
LocalTime time = LocalTime.now();
System.out.println(time.toString());
//output 17:58:08.508
LocalDateTime now = LocalDateTime.now();
System.out.println(now.toString());
//output 2019-11-20T18:17:24.565
System.out.println(now.toLocalDate());
//output 2019-11-20
System.out.println(now.toLocalTime());
//output 18:17:24.565
//日期和字符串轉(zhuǎn)換
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 日期時(shí)間轉(zhuǎn)字符串
String nowText = now.format(formatter);
System.out.println("nowText= " + nowText);
//output nowText= 2019-11-20 18:19:35
// 字符串轉(zhuǎn)日期時(shí)間
String datetimeText = "2019-11-20 23:59:59";
LocalDateTime datetime = LocalDateTime.parse(datetimeText, formatter);
System.out.println(datetime);
//output 2019-11-20T23:59:59
long timeStamp = 1574245577429L;
Instant instant = Instant.ofEpochMilli(timeStamp);
String timeToStr = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).format(formatter);
System.out.println(timeToStr);
//output 2019-11-20 18:26:17
long timeStamp2 = now.toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println(timeStamp2);
//output 1574245919701
now = LocalDateTime.now();
System.out.println(now.toLocalTime());
}
上一篇我們講到综看,java7的SimpleDateFormat
有線程安全的問(wèn)題,java8里怎么解決的呢岖食。
首先红碑,如果不使用format,在java8里泡垃,直接把時(shí)間戳轉(zhuǎn)化為字符串
@Test
public void timestampToStr(){
long times = System.currentTimeMillis();
Instant instant = Instant.ofEpochMilli(times);
ZoneId zone = ZoneId.systemDefault();
System.out.println(LocalDateTime.ofInstant(instant, zone));
}
得到的字符串是
2020-03-12T18:33:00.041
這個(gè)比java7的格式友好一些析珊,基本能用了
如果需要format呢,添加一個(gè)format即可
@Test
public void timestampToStr(){
long times = System.currentTimeMillis();
Instant instant = Instant.ofEpochMilli(times);
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(formatter.format(LocalDateTime.ofInstant(instant, zone)));
}
得到的輸出格式是
2020-03-12 18:38:37
首先蔑穴,DateTimeFormatter
不會(huì)再犯 SimpleDateFormat
中的錯(cuò)誤忠寻,放一個(gè)公共變量在里面,讓不同的線程都能修改存和。DateTimeFormatter
中的公共變量奕剃,都使用了 final
修飾,final
修飾有啥用呢捐腿,我們寫(xiě)個(gè)demo來(lái)測(cè)試一下纵朋。
首先,不去思考final的正確用法茄袖,我們一步一步的來(lái)操软。
按照常規(guī)的思維,寫(xiě)一個(gè)類(lèi)
public class FinalDemo {
private final Integer number;
public void setNumber(Integer number){
this.number = number;
}
}
這個(gè)寫(xiě)出來(lái)后宪祥,idea提示了兩個(gè)錯(cuò)誤
第一個(gè)是
private final Integer number;
提示錯(cuò)誤:number沒(méi)有被初始化
第二個(gè)是
this.number = number;
提示錯(cuò)誤:不能給final變量number賦值聂薪。
根據(jù)上面的錯(cuò)誤提示,我們知道
- final變量需要初始化蝗羊。
- final變量在第一次賦值后胆建,不能修改。
DateTimeFormatter
中的公共變量肘交,都是final修飾的笆载,首先保證了一點(diǎn),在第一次賦值之后涯呻,不能修改凉驻。
我們繼續(xù)研究一下final,既然說(shuō)到這里复罐,就把這個(gè)搞清楚涝登。
我們修改一下demo。
public class FinalDemo {
public final Integer number = 3;
public Integer getNumber(){
return number;
}
}
然后效诅,我們寫(xiě)一個(gè)測(cè)試方法胀滚,去使用它
@Test
public void finalTest(){
FinalDemo finalDemo = new FinalDemo();
System.out.println(finalDemo.number);
}
可以拿到number的值趟济,我們?cè)谶@里進(jìn)行修改呢
@Test
public void finalTest(){
FinalDemo finalDemo = new FinalDemo();
System.out.println(finalDemo.number);
finalDemo.number = 5;
System.out.println(finalDemo.number);
}
會(huì)提示報(bào)錯(cuò)
Error:(95, 18) java: 無(wú)法為最終變量number分配值
也就是final的值,在第一次賦值后咽笼,無(wú)法修改顷编。
接下來(lái),我們修改一下代碼
public class FinalNumberDemo {
private final Integer number;
FinalNumberDemo(Integer number){
this.number = number;
}
public Integer getNumber(){
return this.number;
}
public void setNumber(Integer number){
this.number = number;
}
}
這里剑刑,我們?cè)跇?gòu)造函數(shù)里給number賦值媳纬,這里,沒(méi)有在聲明number的地方報(bào)錯(cuò)施掏,因?yàn)樵跇?gòu)造函數(shù)中賦值钮惠,就保證了number在使用的時(shí)候,就已經(jīng)有初始值了七芭。
同時(shí)素挽,我們加入了get和set方法。在idea中狸驳,會(huì)提示set方法有錯(cuò)毁菱,不能改變final變量的值,我們不管锌历,寫(xiě)一個(gè)方法來(lái)調(diào)用看看贮庞。
@Test
public void finalTest(){
FinalNumberDemo finalNumberDemo = new FinalNumberDemo(3);
System.out.println(finalNumberDemo.getNumber());
finalNumberDemo.setNumber(4);
System.out.println(finalNumberDemo.getNumber());
}
執(zhí)行起來(lái)也是會(huì)報(bào)錯(cuò)的。
好了究西,我們對(duì)final的特性有了一些了解窗慎,繼續(xù)看下面的demo。
首先卤材,我們創(chuàng)建一個(gè)用戶類(lèi)
public class User {
private Long id;
private String name;
User(Long id,String name){
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
非常簡(jiǎn)單的一個(gè)類(lèi)遮斥,記錄一個(gè)用戶的信息。
然后扇丛,我們用final來(lái)聲明它术吗。
public class FinalDemo {
private final User user;
FinalDemo(User user){
this.user = user;
}
public User getUser(){
return this.user;
}
}
在這個(gè)demo中,我們?cè)跇?gòu)造函數(shù)里給user賦值帆精,符合final的要求较屿。
我們現(xiàn)在來(lái)使用這個(gè)demo
@Test
public void finalTest(){
User user = new User(1L,"Joe");
FinalDemo finalDemo = new FinalDemo(user);
User somebody = finalDemo.getUser();
System.out.println(somebody.getId());
System.out.println(somebody.getName());
System.out.println("==========magic=========");
user.setName("Li");
System.out.println(somebody.getId());
System.out.println(somebody.getName());
}
我們先設(shè)置一個(gè)user進(jìn)去,然后卓练,修改user自己隘蝎,沒(méi)有去修改demo類(lèi)里面的user,結(jié)果會(huì)怎么樣呢
1
Joe
==========magic=========
1
Li
user的名字被修改了襟企。
為什么呢嘱么?
前面我們確定了final保證他指向的數(shù)據(jù)在第一次賦值之后是不可修改的
但是,引用類(lèi)型顽悼,本身存儲(chǔ)的是實(shí)際數(shù)據(jù)的地址曼振。
我們?nèi)绻麑⒁妙?lèi)型設(shè)定為final几迄,只保證了這個(gè)地址不變,但是地址指向的內(nèi)容是否變動(dòng)冰评,是保證不了的映胁。
用圖來(lái)簡(jiǎn)單說(shuō)明一下
首先,創(chuàng)建一個(gè)userA對(duì)象
在userA這個(gè)變量里面存儲(chǔ)的集索,是實(shí)際數(shù)據(jù)的地址屿愚。
如果這個(gè)時(shí)候汇跨,我們通過(guò)
=
的方式务荆,賦值給userB
User userA = new User();
User userB = userA;
這樣操作,實(shí)際上是把userA里存的數(shù)據(jù)地址賦值給了userB穷遂,也就是說(shuō)函匕,userA和userB指向的是相同的數(shù)據(jù)
Talk is cheap. Show me the code.
User userA = new User();
userA.setId(1L);
userA.setName("John");
User userB = userA;
System.out.println("userB id==>"+userB.getId());
System.out.println("userB name==>"+userB.getName());
System.out.println("================");
userA.setName("John Snow");
System.out.println("userB id==>"+userB.getId());
System.out.println("userB name==>"+userB.getName());
輸出結(jié)果是:
userB id==>1
userB name==>John
================
userB id==>1
userB name==>John Snow
我們修改A的名字,B的名字跟著改了蚪黑,這下明白了吧盅惜。
好了,引用類(lèi)型的大致基本原理我們搞清楚了忌穿。
當(dāng)我們用final來(lái)修飾引用類(lèi)型的時(shí)候抒寂,保證的是,這個(gè)數(shù)據(jù)地址不可變掠剑,無(wú)法保證實(shí)際數(shù)據(jù)被修改屈芜。如果不了解這一點(diǎn),可能會(huì)搞出bug朴译。
好了井佑。java8的時(shí)間類(lèi),還有很多用法以及細(xì)節(jié)眠寿,不清楚的可以查看文檔或者百度躬翁。我們這里結(jié)合java7和java8的時(shí)間類(lèi)進(jìn)行對(duì)比,實(shí)際更多的是希望大家了解一下線程安全性相關(guān)問(wèn)題盯拱,在寫(xiě)代碼的時(shí)候盒发,有這么一個(gè)概念,不致于寫(xiě)出bug之后狡逢,找不到原因迹辐。如果后面有時(shí)間,我會(huì)繼續(xù)寫(xiě)線程相關(guān)的文章甚侣。