Java8 HashMap優(yōu)化、lambda、Stream等詳解
你知道的越多,不知道的就越多,業(yè)余的像一棵小草!
你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!
編輯:業(yè)余草
modb.pro
推薦:https://www.xttblog.com/?p=5342

目錄
一、概述 二、優(yōu)化底層數(shù)據(jù)結(jié)構(gòu) 2.1、優(yōu)化HashMap 三、Lambda表達(dá)式 3.1、概述 3.2、匿名內(nèi)部類(lèi)到lambda 3.3、lambda語(yǔ)法 3.4、函數(shù)式接口 3.5、方法引用 四、Stream 4.1、概述 4.2、創(chuàng)建Stream 4.3、中間操作 4.3.1、篩選與切片 4.3.2、映射map 4.3.3、排序sorted 4.4、終止操作 4.4.1、查找與匹配 4.4.2、規(guī)約 4.4.3、收集collect 4.5、并行流 4.5.1、Fork/Join框架 4.5.2、測(cè)試并行流 五、新時(shí)間API 5.1、Java.time 5.1、日期 5.2、時(shí)間 5.3、時(shí)間+日期 5.4、時(shí)間戳 5.5、字符串轉(zhuǎn)日期格式化 5.6、時(shí)間推移 5.7、時(shí)間抽取 5.8、時(shí)區(qū) 5.9、Date與LocalDateTime互轉(zhuǎn) 六、Optional容器類(lèi) 七、接口中的默認(rèn)方法與靜態(tài)方法 八、重復(fù)注解與類(lèi)型注解 8.1、重復(fù)注解 8.2、類(lèi)型注解 8.3、@NonNull注解
一、概述
?「Java8 的優(yōu)勢(shì)」
?
速度更快 代碼更少 強(qiáng)大的Stream API 便于并行 最大化減少空指針Optional
二、優(yōu)化底層數(shù)據(jù)結(jié)構(gòu)
2.1、優(yōu)化HashMap
?「【原始Map】」
第10001個(gè)值存入,為避免k值相同,會(huì)進(jìn)行10000次equals,效率非常低
?

?「【java7 HashMap】:數(shù)組+鏈表」
HashMap 是 16 個(gè)位置的數(shù)組,并提供"加載因子",當(dāng)達(dá)到75%時(shí),自動(dòng)擴(kuò)容,會(huì)對(duì)所有元素進(jìn)行重新運(yùn)算生成新的數(shù)組+鏈表
當(dāng)存入值時(shí),會(huì)先把值通過(guò) hashcode 生成對(duì)應(yīng)的索引,
確定索引后,會(huì)跟該索引下的值進(jìn)行比較,比較len(鏈表長(zhǎng)度)次,沒(méi)有重復(fù)則形成鏈表(放鏈表頭)。
效率有所提高,但是如果鏈表較長(zhǎng)有10000個(gè)元素,依舊需要比較1000次。
?

?「【java8 HashMap】:數(shù)組+(鏈表/紅黑樹(shù))」
當(dāng)鏈表長(zhǎng)度大于8,且hashmap總?cè)萘看笥?4,會(huì)將鏈表自動(dòng)轉(zhuǎn)為紅黑樹(shù)
添加元素比鏈表慢,其他的都比鏈表更快速
?

三、Lambda表達(dá)式
3.1、概述
?【概述】
?
Lambda是一個(gè)匿名函數(shù),可以理解為一段可以傳遞的代碼。
Lambda是特殊的匿名內(nèi)部類(lèi),允許將函數(shù)當(dāng)做方法的參數(shù)傳遞
?優(yōu)勢(shì)
?
可以寫(xiě)出更簡(jiǎn)潔、更靈活的代碼。
?經(jīng)驗(yàn)
?
Lambda表達(dá)式的作用主要是用來(lái)簡(jiǎn)化接口的創(chuàng)建,interface。
需要注意的是:
1.任何需要使用interface的地方都可以使用Lambda表達(dá)式來(lái)簡(jiǎn)化;
2.Lambda表達(dá)式不能夠簡(jiǎn)化類(lèi)或者抽象類(lèi)得創(chuàng)建,如果試圖使用Lambda表達(dá)式去創(chuàng)建一個(gè)類(lèi)或者抽象類(lèi)
將會(huì)報(bào)錯(cuò)如下英文信息 ”Target type of a lambda conversion must be an interface“
這就是為什么Lambda表達(dá)式只用用來(lái)簡(jiǎn)化創(chuàng)建接口
3.2、匿名內(nèi)部類(lèi)到lambda
「匿名內(nèi)部類(lèi)」
//?匿名內(nèi)部類(lèi)
Runnable?runnable?=?new?Runnable()?{
????@Override
????public?void?run()?{
????????System.out.println("子線程開(kāi)始執(zhí)行。。。。");
????}
};
new?Thread(runnable).start();
「lambda表達(dá)式」
//?lambda表達(dá)式
Runnable?runnable2?=?()?->?System.out.println("lambda子線程開(kāi)始執(zhí)行。。。。");
new?Thread(runnable2).start();
3.3、lambda語(yǔ)法
?參數(shù)列表 -> 方法體
?
「1、無(wú)參要寫(xiě)括號(hào)」
() -> System.out.println("無(wú)參");「2、只有一個(gè)參數(shù),可以不寫(xiě)括號(hào)」
x -> System.out.println(x);「3、多條語(yǔ)句用{}」
Comparator?com?=?(x,?y)?->?{
????System.out.println("...");
????return?Integer.compare(x,?y);
}
「4、若lambda體中只有一條語(yǔ)句,return和{}都可以不寫(xiě)」
Comparator
「5、lambda參數(shù)列表的數(shù)據(jù)類(lèi)型可以省略不寫(xiě)(JVM編譯器通過(guò)上下文做"類(lèi)型推斷")」
Comparator?com?=?(Integer?x,?Integer?y)?->??Integer.compare(x,?y);
Comparator?com?=?(x,?y)?->??Integer.compare(x,?y);
「6、匿名內(nèi)部類(lèi)會(huì)單獨(dú)生成一個(gè)單獨(dú)的.class文件,lambda表達(dá)式不會(huì)生成。」
3.4、函數(shù)式接口
只有函數(shù)式接口(只有一個(gè)抽象方法的接口@FunctionalInterface)才能使用lambda表達(dá)式
?常見(jiàn)函數(shù)式接口
?

?常用函數(shù)式接口子接口
?

?使用演示
?
「Consumer(有參無(wú)返回值)」
@Test
public?void?test(){
????//?匿名內(nèi)部類(lèi)
????Consumer?consumer1?=?new?Consumer(){
????????@Override
????????public?void?accept(Integer?n)?{
????????????System.out.println(n?+?1);
????????}
????};
????consumer(consumer1,?100);
????//?lambda
????Consumer?consumer?=?n?->?System.out.println(n?+?1);
????consumer(consumer,?100);
}
public?void?consumer(Consumer?consumer,?Integer?n) {
????consumer.accept(n);
}
「Supplier(無(wú)參有返回值)」
@Test
public?void?test(){
????//?lambda表達(dá)式
????int[]?arr?=?getNums(()?->?new?Random().nextInt(100),?10);
????System.out.println(Arrays.toString(arr));
}
public?int[]?getNums(Supplier?supplier,?int?count){
????int[]?arr?=?new?int[count];
????for(int?i=0;?i????????arr[i]?=?supplier.get();
????}
????return?arr;
}
「Function有參有返回值」
@Test
public?void?test(){
????//?lambda表達(dá)式
????String?str?=?dealStr(s?->?s.toUpperCase(),?"abc");
????System.out.println(str);
}
public?String?dealStr(Function?function,?String?str) {
????return?function.apply(str);
}
「Predicate有參,返回布爾值」
@Test
public?void?test(){
????//?lambda表達(dá)式
????List?ageList?=?new?ArrayList<>();
????ageList.add(10);
????ageList.add(19);
????List?resultList?=?filterAge(age?->?age?>?18,?ageList);
????System.out.println(resultList.toString());
}
public?List?filterAge(Predicate?predicate,?List?ageList) {
????List?resultList?=?new?ArrayList<>();
????for?(Integer?i:?ageList)?{
????????if(predicate.test(i)){
????????????resultList.add(i);
????????}
????}
????return?resultList;
}
3.5、方法引用
是Lambda表達(dá)式的一種簡(jiǎn)寫(xiě)
如果lambda表達(dá)式中,只是調(diào)用一個(gè)特定的已存在的方法,則可以使用方法引用
?「對(duì)象::實(shí)例方法」
?
//?lambda表達(dá)式
Consumer?consumer?=?s?->?System.out.println(s);
consumer.accept("hello");
//?對(duì)象::實(shí)例方法
Consumer?consumer2?=?System.out::println;
consumer2.accept("world");
?「對(duì)象::靜態(tài)方法」
?
//?lambda表達(dá)式
Comparator?comparator?=?(x,?y)?->?Integer.compare(x,?y);
//?對(duì)象::靜態(tài)方法
Comparator?comparator2?=?Integer::compare;
?「類(lèi)::實(shí)例方法」
?
【注意】
1、Lambda體中參數(shù)列表和返回值類(lèi)型,要與函數(shù)式抽象方法中抽象方法的保持一致
2、若Lambda參數(shù)列表中,第一個(gè)參數(shù)是實(shí)例方法的調(diào)用者,第二個(gè)是實(shí)例方法的參數(shù),可以使用ClassName::method
//?lambda表達(dá)式
BiPredicate?bp?=?(x,?y)?->?x.equals(y);
//?對(duì)象::靜態(tài)方法(ClassName::method)
BiPredicate?bp2?=?String::equals;
?「類(lèi)::new」
?
「構(gòu)造器引用」
//?lambda表達(dá)式
Supplier?supplier?=?()?->?new?User();
//?對(duì)象::靜態(tài)方法
Supplier?supplier2?=?User?::?new;
「數(shù)組引用」
//?lambda表達(dá)式
Function?function?=?x?->?new?String[x];
//?對(duì)象::靜態(tài)方法
Function?function2?=?String[]::new;
四、Stream

4.1、概述
?「概述」
?
【概述】:流(Stream)中保存對(duì)集合或數(shù)組數(shù)據(jù)的操作
【特點(diǎn)】:
1、Stream只保存操作,自己不保存數(shù)據(jù)
2、Stream不會(huì)改變?cè)瓕?duì)象
3、Stream操作是延遲執(zhí)行的(只有執(zhí)行終止操作,才一次性全部執(zhí)行,稱(chēng)為"惰性求值")
?「操作步驟」
?
「1、創(chuàng)建Stream」
一個(gè)數(shù)據(jù)源(如:集合、數(shù)組),獲取一個(gè)流
「2、中間操作」
一個(gè)中間操作鏈,對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行處理
「3、終止操作」
一個(gè)終止操作,執(zhí)行中間操作鏈,并產(chǎn)生結(jié)果。在這之后,該Stream就不能使用了
4.2、創(chuàng)建Stream
?「【方式一】:通過(guò)Collection對(duì)象的stream()或parallelStream()方法」
?
ArrayList?arrayList?=?new?ArrayList<>();
arrayList.add("hello")
//?創(chuàng)建Stream(單線程)
Stream?stream?=?arrayList.stream();
//?創(chuàng)建Stream(并行流,多線程)
Stream?stream?=?arrayList.parallelStream();
stream.forEach(Systerm.out::println);
?「【方式二】:通過(guò)Arrays類(lèi)的stream()方法」
?
String[]?arr?=?{"aaa",?"bbb",?"ccc"};
//?創(chuàng)建Stream
Stream?stream?=?Arrays.stream(arr);
stream.forEach(Systerm.out::println);
?「【方式三】:通過(guò)Stream接口的of()、iterate()、generate()方法」
?
//?創(chuàng)建Stream(直接把一組數(shù)變?yōu)榱?
Stream?stream?=?Stream.of(10,?20,?30,?40,?50,?60);
//?創(chuàng)建Stream(迭代流,iterate(起始值,?操作方法),配合limit限制)
Stream?stream?=?Stream.iterate(0,?x?->?x+2);
//?創(chuàng)建Stream(生成流,無(wú)參有返回值)
Stream?stream?=?Stream.generate(()?->?new?Random().nextInt(100));
stream.forEach(Systerm.out::println);
//?stream.limit(10).forEach(Systerm.out::println);
?「【方式四】:IntStream」
?
//?創(chuàng)建Stream(1,?2,?3)
IntStream?stream?=?IntStream.of(1,?2,?3);
//?創(chuàng)建Stream([1,?10))
IntStream?stream?=?IntStream.range(1,?10)
//?創(chuàng)建Stream([1,?10])
IntStream?stream?=?IntStream.rangeClosed(1,?10)
4.3、中間操作
//?創(chuàng)建一個(gè)集合用于測(cè)試操作
List?employees?=?Arrays.asList(
????new?Employee("張三",?18,?9999.99,?Status.FREE),
????new?Employee("李四",?18,?8999.99,?Status.FREE),
????new?Employee("王五",?18,?7999.99,?Status.FREE),
????new?Employee("趙六",?18,?6999.99,?Status.BASY),
????new?Employee("田七",?18,?5999.99,?Status.BASY)
);
4.3.1、篩選與切片
?「【filter】」:接收l(shuí)ambda,從流中排除某些元素
?
//?filter
Stream?stream?=?employees.stream().filter(e?->?e.getAge()>18);
stream.forEach(System.out?::?println);
?「【limit】」:截?cái)嗔?/p>?
//?limit
Stream?stream?=?employees.stream().limit(2);
stream.forEach(System.out?::?println);
?「【skip】」:skip(n)類(lèi)似于pandas的iloc[n:]
?
//?skip
Stream?stream?=?employees.stream().skip(2);
stream.forEach(System.out?::?println);
?「【distinct】」:去重(根據(jù)hashcode和equals去重,因此,類(lèi)要重寫(xiě)hashcode和equals方法)
?
//?distinct
Stream?stream?=?employees.stream().distinct();
stream.forEach(System.out?::?println);
4.3.2、映射map
map和flatmap的區(qū)別就相當(dāng)于python list中append和extend的區(qū)別
?「【map】」:接收函數(shù),把函數(shù)應(yīng)用在所有元素上(多個(gè)流相互獨(dú)立在一個(gè)大流中)
?
//?將元素轉(zhuǎn)為大寫(xiě)
List?list?=?Array.aslist("aaa",?"bbb",?"ccc");
list.stream().map(str)?->?str.toUpperCase().forEach(System.out?::?println);
//?提取員工名
employess.stream().map(Employee:getName).forEach(System.out?::?println);
?「【flatmap】」:接收函數(shù),把函數(shù)應(yīng)用在所有元素上(多個(gè)流合完全混合成一個(gè)大流)
?
List?list?=?Arrays.aslist("aaa",?"bbb",?"ccc");
//?編寫(xiě)將字符串拆分為字符的方法,返回一個(gè)流
public?Stream?filterCharacter(String?str) {
????List?list?=?new?ArrayList<>();
????
????for?(Character?ch?:?str.toCharArray()){
????????list.add(ch)
????}
????return?list.stream()
}
//?【方式1】:使用map的方法使用以上方法
Stream>?stream?=?list.stream().map(str)?->?filterCharacter(str);?//[['a',?'a',?'a'],?['b',?'b',?'b'],?['c',?'c',?'c']]
stream.forEach((sm)?->?sm.forEach(System.out?::?println));
//?【方式1】:使用flatmap的方法使用以上方法
Stream?stream?=?list.stream().flatmap(str)?->?filterCharacter(str);?//['a',?'a',?'a',?'b',?'b',?'b',?'c',?'c',?'c']
stream.forEach(System.out?::?println);
4.3.3、排序sorted
?【sorted】:排序
?
//?員工自定義定義排序[sorted(無(wú)參)代表默認(rèn)排序]
employees.stream().sorted(
????(e1,?e2)?->?{
????????if?(e1.getAge().equals(e2.getAge())){
????????????return?e1.getName().compareTo(e2.getName());?//?名字正序
????????}else?{
????????????return?-e1.getAge().compareTo(e2.getAge());?//?年齡倒序
????????}
????}
).forEach(System.out::println)
4.4、終止操作
4.4.1、查找與匹配
allMatch:檢查是否匹配所有元素
anyMatch:檢查是否至少匹配一個(gè)元素
noneMatch:檢查是否沒(méi)有匹配所有元素
findFirst:返回第一個(gè)元素
findAny:返回流中任意一個(gè)元素
count:返回流中元素的總個(gè)數(shù)
max:返回流中最大值
min:返回流中最小值
//?查看員工是不是全是18歲
Booblean?b?=?employees.stream().allMatch((e)?->?e.getAge().equals(18));
//?查看工資最高的員工?[Optional避免空指針]
Optional?op?=?employees.stream()
????.max((e1,?e2)?->?Double.compare(e1.getSalary(),?e2.getSalary());
System.out.println(op.get())
????
//?查出工資為9999.99的隨便一個(gè)員工
Optional?op?=?employees.stream()
????.filter((e)?->?e.getSalary()?==?9999.99)
????.findAny();
System.out.println(op.get())
4.4.2、規(guī)約
?reduce():將流中元素反復(fù)結(jié)合起來(lái),得到一個(gè)值
?
//?列表累加
List?list?=?Arrays.asList(1,?2,?3,?4,?5,?6);
Integer?sum?=?list.stream()
????.reduce(0,?(x,?y)?->?x?+?y);?//?從索引0開(kāi)始,累加
System.out.println(sum);
//?所有人工資總和
Optional?op?=?employees.stream()
????.map(Emplyee?::?getSalary)
????.reduce(Double?::?sum);
System.out.println(op.get());
4.4.3、收集collect
Collectors實(shí)用類(lèi)提供了很多靜態(tài)方法,可以方便的創(chuàng)建常見(jiàn)收集器實(shí)例
?將流轉(zhuǎn)為其他格式
?
//?把所有員工名放到列表
List?li?=?employees.stream()
????.map(Employee?::?getName)
????.collect(Collectors.toList());
//?把所有員工名放到HashSet
HashSet?hs?=?employees.stream()
????.map(Employee?::?getName)
????.collect(Collectors.toCollection(HashSet::new));
?分組
?
//?根據(jù)狀態(tài)分組
Map>?map?=?employees.stream()
????.collect(Collectors.groupingBy(Employee?::?getStatus));
System.out.println(map)
????
//?多級(jí)分組(先根據(jù)狀態(tài)分,再根據(jù)年齡分)
Map>>?map?=?employees.stream()
????.collect(Collectors.groupingBy(Employee?::?getStatus,?Collectors.groupingBy(
????????(e)?->?{
????????????if?(e.getAge()<18){
????????????????return?"未成年";
????????????}?else{
????????????????return?"成年";
????????????}
????????}));
System.out.println(map)
?分區(qū)
?
Map>?map?=?employees.stream()
????.collect(Collectors.partitioningBy((e)?->?e.getSalary()>8000));
?統(tǒng)計(jì)
?
//?公司總?cè)藬?shù)
Long?count?=?employees.stream()
????.collect(Collectors.counting());
//?工資平均值
Double?avg?=?employees.stream()
????.collect(Collectors.averagingDouble(Employee?::?getSalary));
//?工資總和
Double?sum?=?employees.stream()
????.collect(Collectors.summingDouble(Employee?::?getSalary));
//?最大值
List?li?=?Arrays.asList(1,?3,?5,?6,?7);
Optional?max?=?li.stream()
????.collect(Collectors.maxBy((n1,?n2)?->?Double.compare(n1,?n2)));
System.out.println(max.get());
//?最小值
List?li?=?Arrays.asList(1,?3,?5,?6,?7);
Optional?max?=?li.stream()
????.collect(Collectors.maxBy((n1,?n2)?->?Double.compare(n1,?n2)));
System.out.println(max.get());
DoubleSummaryStatistics?dss?=?employees.stream()
????.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());?//?求和
System.out.println(dss.getAverage());?//?求平均數(shù)
System.out.println(dss.getMax());?//?求最大值
?連接
?
String?str?=?employees.stream()
????.map(Employee::getName)
????.collect(Collectors.joining(",",?"===",?"==="));
//?===張三,李四,王五,趙六,田七===
4.5、并行流
?
并行流就是把一個(gè)內(nèi)容分成多個(gè)數(shù)據(jù)塊,并用不同的線程分別處理每個(gè)數(shù)據(jù)塊的流(Fork/Join框架)java8中Stream API 可以通過(guò)
?parallel()與sequential()在并行流與順序流之間進(jìn)行切換
4.5.1、Fork/Join框架
?
更能充分的利用到cpu性能(各個(gè)CPU的利用率基本保持一致)【多線程】:某一核上線程阻塞,這樣會(huì)造成多核中有的在阻塞有的在空閑,不能很好利用cpu資源
【Fork/Join】:使用工作竊取模式,當(dāng)某一核上執(zhí)行完,會(huì)幫助別的核執(zhí)行任務(wù)
?

4.5.2、測(cè)試并行流
?測(cè)試1000億累加
?
「循環(huán)累加」
Instant?start?=?Instant.now();
long?sum?=?0;
for?(long?num=0;?num<=100000000000L;?num++){
????sum?+=?num;
}
Instant?end?=?Instant.now();
System.out.println("耗時(shí):"?+?Duration.between(start,?end).toMillis());?//?耗時(shí):30827
「Fork/Join并行流」
Instant?start?=?Instant.now();
LongStream.rangeClosed(0,?100000000000L).parallel().reduce(0,?Long::sum);
Instant?end?=?Instant.now();
System.out.println("耗時(shí):"?+?Duration.between(start,?end).toMillis());//?耗時(shí):27704
五、新時(shí)間API
?【Java.time】
線程安全、使用方便
?
5.1、Java.time
5.1、日期
LocalDate?localDate?=?LocalDate.now();
LocalDate?localDate2?=?LocalDate.of(2015,?12,?31);
5.2、時(shí)間
LocalTime?localTime?=?LocalTime.now();
LocalTime?localTime2?=?LocalTime.of(21,?30,?59,?11001);
5.3、時(shí)間+日期
LocalDateTime?localDateTime?=?LocalDateTime.now();
LocalDateTime?localDateTime2?=?LocalDateTime.of(2015,?11,?30,?23,?45,?59,?1234);
LocalDateTime?localDateTime3?=?LocalDateTime.of(localDate2,?localTime2);
5.4、時(shí)間戳
//毫秒數(shù)時(shí)間戳
Long?milliSecond?=?LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();?
//秒數(shù)時(shí)間戳
Long?second?=?LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));?
//?時(shí)間戳轉(zhuǎn)時(shí)間字符串
String?result?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss").format(milliSecond);
5.5、字符串轉(zhuǎn)日期格式化
//?指定格式
DateTimeFormatter?dtf?=?DateTimeFormatter.ofPattern("yyyy-MM-dd?HH:mm:ss");?
//?將時(shí)間轉(zhuǎn)化為指定格式字符串
String?df?=?dtf.format(LocalDateTime.now());?
//?LocalDateTime格式字符串轉(zhuǎn)LocalDateTime
LocalDateTime?dt1?=?LocalDateTime.parse("2016-11-30T15:16:17");?
//?將指定格式字符串轉(zhuǎn)化為L(zhǎng)ocalDateTime
LocalDateTime?dt2?=?LocalDateTime.parse("2016-11-30?15:16:17",?dtf);?
5.6、時(shí)間推移
//?5天后
LocalDateTime?after5days?=?LocalDateTime.now().plusDays(5);?
//?5天前
LocalDateTime?before5days?=?LocalDateTime.now().minusDays(5);?
?//?加1月減2周
LocalDateTime?monthWeek?=?LocalDateTime.now().plusMonths(1).minusWeeks(2);
5.7、時(shí)間抽取
//?年份
Integer?year?=?LocalDate.now().getYear();?
//?本年第幾天
Integer?dayOfYear?=?LocalDate.now().getDayOfYear();?
//?月份
Integer?month?=?LocalDate.now().getMonthValue();?
//?幾號(hào)
Integer?dayOfMonth?=?LocalDate.now().getMonthValue();?
//?周幾
Integer?week?=?LocalDate.now().getDayOfWeek().getValue();
//?獲得本月第1天
LocalDate?firstDay?=?LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());?
//?獲得本月最后1天
LocalDate?lastDay?=?LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());?
//?本月第1個(gè)星期天
LocalDate?firstSunday?=?LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY));?
//?判斷那個(gè)日期在前
Boolean?b?=?firstSunday.isBefore(LocalDate.now());?
//相距多少年月日
Period?p?=?LocalDate.now().until(LocalDate.of(2050,?1,?1));?
//相距多少天
Long?d?=?LocalDate.of(2050,?1,?1).toEpochDay()?-?LocalDate.now().toEpochDay();?
5.8、時(shí)區(qū)
//?獲取當(dāng)前默認(rèn)時(shí)區(qū)的日期和時(shí)間
ZonedDateTime?now?=?ZonedDateTime.now();?
//?獲得時(shí)區(qū)(Asia/Shanghai)
now.getZone();?
//?時(shí)區(qū)為0的時(shí)間
Instant?ins?=?now.toInstant();?
//?指定時(shí)區(qū)的時(shí)間
ZonedDateTime?london?=?ZonedDateTime.now(ZoneId.of("Europe/London"));?
//把倫敦時(shí)間轉(zhuǎn)換到紐約時(shí)間
ZonedDateTime?newYork?=?london.withZoneSameInstant(ZoneId.of("America/New_York"));?
5.9、Date與LocalDateTime互轉(zhuǎn)
//?Date轉(zhuǎn)LocalDateTime
LocalDateTime?localDateTime?=?date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime()
//?LocalDateTime轉(zhuǎn)Date
Date?date?=?Date.from(localDateTime.toInstant(ZoneOffset.of("+8")))
六、Optional容器類(lèi)
?
使用Optional后,如果空指針一定是創(chuàng)建Optional實(shí)例時(shí)出現(xiàn)的,更容易定位Optional類(lèi)(java.util.Optional)是一個(gè)容器類(lèi),代表一個(gè)值存在或不存在,原來(lái)用null表示一個(gè)值不存在,現(xiàn)在Optional可以更好的表達(dá)這個(gè)概念。并且可以避免空指針異常。
?
「常用方法」
| 「函數(shù)」 | 「描述」 |
|---|---|
| Optional.of(T t) | 創(chuàng)建一個(gè) Optional 實(shí)例 |
| Optional.empty() | 創(chuàng)建一個(gè)空的 Optional 實(shí)例 |
| Optional.ofNullable(T t) | 若 t 不為 null,創(chuàng)建 Optional 實(shí)例,否則創(chuàng)建空實(shí)例 |
| isPresent() | 判斷是否包含值 |
| orElse(T t) | 如果調(diào)用對(duì)象包含值,返回該值,否則返回t |
| orElseGet(Supplier s) | 如果調(diào)用對(duì)象包含值,返回該值,否則返回 s 獲取的值 |
| map(Function f) | 如果有值對(duì)其處理,并返回處理后的Optional,否則返回 Optional.empty() |
| flatMap(Function mapper) | 與 map 類(lèi)似,要求返回值必須是Optional |
七、接口中的默認(rèn)方法與靜態(tài)方法
?在java8中允許有實(shí)現(xiàn)的靜態(tài)方法和默認(rèn)方法
?
「"類(lèi)優(yōu)先"原則」 接口和父類(lèi)沖突,使用父類(lèi)的方法。(一個(gè)類(lèi)繼承的父類(lèi)和接口實(shí)現(xiàn)了同名的方法,父類(lèi)的會(huì)生效) 接口沖突,需要指定。(一個(gè)類(lèi)實(shí)現(xiàn)兩個(gè)接口,兩個(gè)接口有同名的實(shí)現(xiàn)方法,需要使用接口名.supper.方法名,指定使用哪個(gè)) 「示例」
public?interface?MyInterface?{
????//?允許有實(shí)現(xiàn)的默認(rèn)方法
????default?String?getName(){
????????return?"接口中的默認(rèn)方法";
????}
????//?允許有實(shí)現(xiàn)的靜態(tài)方法
????public?static?String?show(){
????????return?"接口中的靜態(tài)方法";
????}
}
八、重復(fù)注解與類(lèi)型注解
8.1、重復(fù)注解
?java8支持方法使用重復(fù)注解(同一個(gè)注解使用兩次)
新的反射API提供了getAnnotationsByType()方法,獲得該注解所有的值
?
「定義注解容器類(lèi)」
要想定義重復(fù)注解,必須要先定義一個(gè)該注解的容器
@Target({ElementType.TYPE,?ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface?MyAnnotations{
????MyAnnotation[]?value();
}
「自定義一個(gè)注解」
要用@Repeatable修飾,并指定容器類(lèi)
//?要用@Repeatable修飾,并指定容器類(lèi)
@Repeatable(MyAnnotations.class)
//?表示注解的適用范圍(TYPE:類(lèi),?METHOD:方法)
@Target({ElementType.TYPE,?ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface?MyAnnotation{
????//?注解的參數(shù):類(lèi)型?參數(shù)名
????int?time()?default?1000;
}
「重復(fù)注解」
public?class?TestAnnotation?{
????@MyAnnotation(100)
????@MyAnnotation(200)
????public?void?show(){
????}
}
「使用反射的getAnnotationsByType()獲取所有值」
Class?clazz?=?TestAnnotation.class;
Method?m1?=?clazz.getMethod("show");
MyAnnotation[]?mas?=?m1.getAnnotationsByType(MyAnnotation.class);
for?(MyAnnotation?myAnnotation?:?mas){
????System.out.println(myAnnotation.value());?//?100、200
}
8.2、類(lèi)型注解
?注解的@Target可以使用TYPE_PARAMETER,可以用注解給參數(shù)賦默認(rèn)值
類(lèi)似于@RequestParam,@RequestParam使用的是PARAMETER,給形參設(shè)置默認(rèn)值
?
@MyAnnotation(100)
public?void?show(@MyAnnotation(111)?Integer?num){
}
8.3、@NonNull注解
?提供@NonNull注解,如果運(yùn)行時(shí)加了該注解的值是null則運(yùn)行時(shí)則會(huì)報(bào)錯(cuò)(運(yùn)行時(shí)異常)
但是在java8中并沒(méi)有內(nèi)置,不可以直接使用,可以配合框架使用,不如SpringBoot中可以使用
?
// obj如果為null,運(yùn)行時(shí)會(huì)報(bào)錯(cuò) private @NonNull Object obj;
