| 版本 | 說明 | 發布日期 |
|---|---|---|
| 1.0 | 發布文章第一版 | 2021-02-20 |
文章目錄
- 前言
- Java8新特性
- 函式式介面
- Lambda運算式
- 方法參考
- 類的非靜態方法參考
- 構造器的參考
- 陣列的參考
- Stream介面
- Stream的常用方法
- 案例
- 列印出集合中所有成年人(測驗filter和forEach)
- 判斷集合中是否都是成年人(測驗noneMatch)
- 將集合中所有人的年齡累加并列印(測驗map和reduce)
- Optional類
- 來個栗子
- Java9新特性
- 模塊化
- 模塊化的使用
- 震驚!匿名內部類泛型還能優化?到底是怎么回事呢?讓我們一起來看一看吧~
- 集合工廠方法
- 流拷貝方法
- Java10新特性
- 區域變數型別推斷
前言
- 這篇文章是我個人的學習筆記,可能無法做到面面俱到,也可能會有各種紕漏,如果任何疑惑的地方,歡迎一起討論~
- 如果想完整閱讀這個系列的文章,歡迎關注我的專欄《Java基礎系列文章》~
Java8新特性
- Java8是一個重要版本,雖然該版本早于2014年發布,但是目前依然有很多企業(比如俺滴老東家)依然在使用,
- 這個版本對包含語言、編譯器、庫、工具和JVM等在內的各個方面的增添了十多個新特性,尤其是圍繞“函式式介面”,搞出了很多新鮮玩意兒,
函式式介面
- 別看這個名字花里胡哨的,其實就是指有且僅有一個抽象方法的介面,這類介面在Java8之前其實也接觸過一些,比如多執行緒常用的java.lang.Runnable以及集合中常用的“輔助”java.util.Comparator等,
- Java8開始,java對函式式介面增加了一個預制注解@FunctionalInterface,當給介面加上了給注解,則在介面不滿足函式式介面時,會出現報錯,
- 此外,Java8專門增加了一個包java.util.function,該包中包含了很多Java官方提供的函式式介面,
- 下面列舉一些常用的:
| 介面名稱 | 介面方法 | 功能介紹 |
|---|---|---|
| Consumer | void accept(T t) | 用于實作有一個引數,但沒有回傳值的方法, |
| Supplier | T get() | 用于實作沒有引數和回傳值的方法, |
| Function<T,R> | R apply(T t) | 用于實作有引數和回傳值的方法, |
| Predicate | boolean test(T t) | 用于實作有引數和回傳值,并且回傳值是布爾型別的方法, |
Lambda運算式
- Java8專門為函式式介面的實作和實體化提供了一個語法糖——Lambda運算式,當然,Lambda運算式并不是僅僅用于此的語法糖,也不是匿名內部類的語法糖,
- 看過我之前文章的小伙伴應該對這個不陌生了,其寫法是:
介面名 變數名 = (引數串列) -> {方法體}; - 舉個栗子:
Function<String, String> consumer = (String str) -> {
return str;
};
- 此外,Lambda運算式在基礎語法之上,還有些地方可以省略:
- 引數串列中的變數型別始終可以省略,(因為重寫介面方法,變數型別都是已知的啦);
- 當引數串列中有且僅有一個引數時,()可以省略;
- 當且僅當方法體只有一行代碼時,{}可以省略;
- 當且僅當方法體只有一行代碼,且這一行代碼是一個return時,{}和return關鍵字都可以省略,
- 所以,上面的栗子可以簡化成下面這樣,emmm,可能看起來有點過分了,但卻是就是可以這么簡單,
Function<String, String> consumer = str -> str;
方法參考
- 這個也是針對函式式介面新增的語法糖,
- 先說說這玩意兒的意義吧,在特定場景下,我們可以把各路英雄齊聚一堂,也就是把來自各個類的方法聚集在一起,提取到同一個介面下面,從而可以很方便地利用多型,來對這些方法進行呼叫,例如下面要講的流(這個流指的是Java8新出的特性,并不是大家熟知的輸入輸出流哈),
- 所謂方法參考,就是當函式式介面的實作方法中,只有一個方法呼叫或物件創建,則可以通過方法參考來簡化代碼,
- 說起來有點抽象,大概就是下面這種感覺:
public class LambdaTest {
public static void main(String[] args) {
//正常的Lambda運算式寫法
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("Lambda寫法");
//方法參考寫法
Consumer<String> consumer1 = System.out::println;
consumer1.accept("方法參考寫法");
}
}
- 下面來具體列舉一下各種情況,由此可見,上面這個例子,就是物件的非靜態方法參考,因為out是一個列印流物件,而println是這個物件的一個成員方法,
| 種類 | 語法 |
|---|---|
| 物件的非靜態方法參考 | ObjectName :: MethodName |
| 類的靜態方法參考 | ClassName :: StaticMethodName |
| 類的非靜態方法參考 | ClassName :: MethodName |
| 構造器的參考 | ClassName :: new |
| 陣列的參考 | TypeName[] :: new |
- 對于前兩種,都很好理解,但后面三種怎么用?請待我徐徐道來~
類的非靜態方法參考
- 當我們重寫的方法中的一個入參作為非靜態方法的呼叫物件,則可以使用類的非靜態方法參考,
- 舉例如下,運行結果不重要,就不放了,因為o1是Integer入參之一,并且在方法體中是方法的呼叫者,所以可以如下簡化,
public class LambdaTest {
public static void main(String[] args) {
//這是最簡單的匿名內部類寫法
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(1, 2));
//類的非靜態方法參考,
comparator = Integer::compare;
System.out.println(comparator.compare(2, 1));
}
}
構造器的參考
- 當重寫的方法中,僅有一行new物件的陳述句,則可以如下簡化:
public class LambdaTest {
public static void main(String[] args) {
//這是最簡單的匿名內部類寫法
Supplier<Person> supplier = new Supplier<Person>() {
@Override
public Person get() {
return new Person();
}
};
System.out.println(supplier.get());
//通過構造器的參考實作
supplier = Person::new;
System.out.println(supplier.get());
}
}
陣列的參考
- 當重寫的方法中,僅有一行創建并回傳陣列物件的陳述句,則可以如下簡化:
public class LambdaTest {
public static void main(String[] args) {
//這是最簡單的匿名內部類寫法
Function<Integer, Integer[]> function = new Function<Integer, Integer[]>() {
@Override
public Integer[] apply(Integer integer) {
return new Integer[integer];
}
};
System.out.println(Arrays.toString(function.apply(5)));
//簡化后
function = Integer[]::new;
System.out.println(Arrays.toString(function.apply(2)));
}
}
Stream介面
- 位于java.util.stream
- 該介面對集合的增強,可以對集合元素進行復雜的查找、過濾、篩選等操作,
- Stream介面借助于Lambda運算式和方法參考,極大提高編程效率和程式可讀性,同時它提供串行和并行兩種模式進行匯聚操作,并發模式能夠充分利用多核處理器的優勢,
- Stream的使用基本就固定的三個步驟:
- 使用一個資料源創建Stream物件;
- 對Stream進行各種處理;
- 對Stream呼叫終止操作,以回傳結果,
Stream的常用方法
- Stream的創建方式通常有以下三種:
- 通過集合物件中新增的獲取流的方法:
Stream stream(), - 通過陣列工具類中的靜態方法,例如:
static IntStream stream(int[] array), - 通過Stream介面的靜態方法:
static Stream of(T... values)、static Stream generate(Supplier<? extends T> s)等,
- 通過集合物件中新增的獲取流的方法:
- Stream的邏輯處理方法:
| 方法宣告 | 功能介紹 |
|---|---|
| Stream ?lter(Predicate<? super T> predicate) | 回傳一個包含匹配元素的流 |
| Stream distinct() | 回傳去重之后的流 |
| Stream limit(long maxSize) | 回傳最多前指定數量個元素的流 |
| Stream skip(long n) | 回傳跳過前n個元素后的流 |
| Stream map(Function<? super T,? extends R> mapper) | 回傳對每個元素處理之后,新元素組成的流,也就是所謂的映射, |
| Stream sorted() | 回傳自然排序后的流 |
| Stream sorted(Comparator<? super T> comparator) | 回傳比較器排序后的流 |
- Stream的終止操作:
| 方法宣告 | 功能介紹 |
|---|---|
| Optional ?ndFirst() | 回傳該流的第一個元素 |
| boolean allMatch(Predicate<? super T> predicate) | 判斷所有元素是否匹配指定條件 |
| boolean noneMatch(Predicate<? super T> predicate) | 判斷所有元素是否不匹配指定條件 |
| Optional max(Comparator<? super T> comparator) | 根據比較器回傳最大元素 |
| Optional min(Comparator<? super T> comparator) | 根據比較器回傳最小元素 |
| long count() | 回傳元素的個數 |
| void forEach(Consumer<? super T> action) | 對流中每個元素執行指定操作 |
| Optional reduce(BinaryOperator accumulator) | 將集合中所有元素結合 |
案例
列印出集合中所有成年人(測驗filter和forEach)
- 我這里就不用傳統的寫法了,直接看看在Java8之后,我們可以如何實作這樣的需求:
//首先構造一個測驗用的Person類
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//然后是測驗方法
public class Starter {
public static void main(String[] args) {
getAdults();
}
public static void getAdults() {
//創建用于測驗的集合
ArrayList<Person> list = new ArrayList<>();
list.add(new Person(17, "豬豬俠"));
list.add(new Person(20, "蜘蛛俠"));
list.add(new Person(23, "咕咕俠"));
list.add(new Person(30, "大俠"));
//先用比較容易理解的匿名內部類來實作,便于理解,
//首先獲取list的流物件
//然后呼叫filter方法,實作Predicate介面,
list.stream().filter(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() >= 18;
}
}).forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person);
}
});
//使用方法參考和Lambda運算式來簡化
System.out.println("====================");
list.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);
}
}
- 運行結果如下,方法參考、Lambda運算式和Stream的強大之處就不需要我多言了吧~如果我們用傳統的迭代器來實作,還需要for回圈啦,各種blabla的操作,在Java8加持之下,只需要一行代碼就能實作需求,一個字:爽!
Person{age=20, name='蜘蛛俠'}
Person{age=23, name='咕咕俠'}
Person{age=30, name='大俠'}
====================
Person{age=20, name='蜘蛛俠'}
Person{age=23, name='咕咕俠'}
Person{age=30, name='大俠'}
判斷集合中是否都是成年人(測驗noneMatch)
- 這個案例呢就得用到noneMatch或者match方法了,但是實作步驟還是大同小異,代碼如下,Person類一樣的,就省略了,
public static void isAdults() {
//創建用于測驗的集合
ArrayList<Person> list = new ArrayList<>();
list.add(new Person(17, "豬豬俠"));
list.add(new Person(20, "蜘蛛俠"));
list.add(new Person(23, "咕咕俠"));
list.add(new Person(30, "大俠"));
//先用比較容易理解的匿名內部類來實作,便于理解,
boolean result = list.stream().noneMatch(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() < 18;
}
});
System.out.println("都是成年人嗎?" + result);
//使用方法參考和Lambda運算式來簡化
System.out.println("====================");
result = list.stream().noneMatch(person -> person.getAge() < 18);
System.out.println("都是成年人嗎?" + result);
}
- 運行結果如下,講這個例子主要是怕小伙伴們沒有理解noneMatch是什么意思,當集合中沒有任何元素匹配判斷條件時,noneMatch就會回傳true,否則回傳false,
都是成年人嗎?false
====================
都是成年人嗎?false
將集合中所有人的年齡累加并列印(測驗map和reduce)
- 通過map方法,可以重新映射出一個由年齡組成的流,而reduce方法可以對集合中所有元素進行二元處理(比如累加),
public static void sumAge(){
//創建用于測驗的集合
ArrayList<Person> list = new ArrayList<>();
list.add(new Person(17, "豬豬俠"));
list.add(new Person(20, "蜘蛛俠"));
list.add(new Person(23, "咕咕俠"));
list.add(new Person(30, "大俠"));
//先用比較容易理解的匿名內部類來實作,便于理解,
//先將集合映射為年齡集合,因為年齡是整數,所以需要使用Integer來存放
//再用reduce來累加年齡
Optional<Integer> result = list.stream().map(new Function<Person, Integer>() {
@Override
public Integer apply(Person person) {
return person.getAge();
}
}).reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println(result);
//使用方法參考和Lambda運算式來簡化
System.out.println("====================");
result = list.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println(result);
}
- 執行結果如下,
- 至于Optional類是什么個東西,下面馬上會講,
- BinaryOperator是BiFunction的子介面,也是一個函式式介面,用于將兩個同型別入參轉換為一個同型別的回傳值,
- 所以reduce是什么作用呢,也就可見一斑了,就是通過二元處理,來將集合中所有的元素,處理為一個元素,
Optional[90]
====================
Optional[90]
Optional類
- 位于java.util,是一個容器,主要用于處理可能為空的物件,以簡化傳統的通過if/else判空代碼,
- 常用的方法如下:
| 方法宣告 | 功能介紹 |
|---|---|
| static Optional ofNullable(T value) | 根據引數創建物件, |
| Optional map(Function<? super T,? extends U> mapper) | 將Optional物件映射為另一種Optional物件, |
| T orElse(T other) | 回傳物件中的值,否則回傳other指定的值, |
來個栗子
- 提供一個方法,列印字串的長度,如果字串為空,則長度視為0,
- 如果使用傳統的判斷,就不夠簡潔,java8之后我們就可以用Optional來搞:
public class OptionalTest {
public static void main(String[] args) {
StringLength("12341324");
StringLength(null);
}
public static void StringLength(String str){
//創建Optional物件
Optional<String> optionalS = Optional.ofNullable(str);
//映射為字串長度,并且獲取回傳值
System.out.println(optionalS.map(String::length).orElse(0));
}
}
- 運行結果如下,就兩行代碼就實作了,不服不行啊!順道一提,Java11中String類也增加了orElse方法,用法是一模一樣的,
8
0
Java9新特性
模塊化
- 隨著java的發展,一個專案的大小越來越大,導致啟動或者運行的時候效率下降,例如多時候,一些代碼之間聯系性很高,而一些代碼之間聯系性很低,如果同一時間都將他們全部放在一起管理、運行,會導致效率的低下,
- 為了解決這個問題,java9引入了模塊化,同一個專案下面可以有多個模塊,不同模塊之間是相互獨立的,資源的加載也是分開的,從而減少記憶體的開銷,提高專案的可維護性,
模塊化的使用
- 對于IDEA編譯器,可以通過在專案上右鍵直接創建模塊

- 給模塊更改名字和路徑

- 建好之后,兩個模塊就分別會有自己的源路徑src,默認情況下,不同的模塊之間的類無法相互訪問,那如果我們想訪問另一個模塊的類怎么辦呢?這個時候就需要配置一個特殊的模塊資訊檔案,
- 例如現在想在NewModule模塊訪問JavaPractice模塊的類,則在兩個模塊中如下創建檔案:

- 在JavaPractice模塊資訊中寫入如下代碼,將該包暴露給其他模塊,我在這個包下面封裝了一個Person類,待會兒用這個類進行測驗,
module JavaPractice {
//想要暴露給其他模塊的包使用exports關鍵字來實作
exports com.JavaSE.NewFeature.Java8.Stream;
}
- 在NewModule模塊資訊中寫入如下代碼,以引入JavaPractice模塊,寫好之后會報錯,因為還沒有在專案配置中依賴JavaPractice模塊,根據修改意見add dependency就好,
module NewModule {
//需要依賴的模塊使用requires關鍵字來實作
requires JavaPractice;
}
- 在NewModule中創建一個測驗類,如下,
package com;
import com.JavaSE.NewFeature.Java8.Stream.Person;
public class Starter {
public static void main(String[] args) {
Person person = new Person(12, "帥");
System.out.println(person);
}
}
- 運行結果如下,可以看到這樣就能使用其他模塊的類了,
Person{age=12, name='帥'}
震驚!匿名內部類泛型還能優化?到底是怎么回事呢?讓我們一起來看一看吧~
public class GenericityOptimization {
public static void main(String[] args) {
//以前的寫法
Function<String, String> function = new Function<String, String>() {
@Override
public String apply(String s) {
return null;
}
};
//java9的優化
function = new Function<>() {
@Override
public String apply(String s) {
return null;
}
};
}
}
- 看到區別了么?其實就是實體化陳述句的泛型可以省略了(因為必須得一樣嘛),所以其實就是很小的一個優化,一看就是老營銷號了,
集合工廠方法
- Java9給集合增加了靜態工廠方法of,該方法創建的集合物件的元素是不可新增、洗掉和修改的,并且集合中的元素不能為null,
- 簡單測驗一下:
public class CollectionTest {
public static void main(String[] args) {
List<String> list = List.of("null", "123");
System.out.println(list);
//假如想添加一個元素
list.add("哇哦");
}
}
- 運行結果如下,當添加的元素為null或者想對集合中的元素進行修改的時候,編譯不會報錯,但是運行時會拋出例外,
[null, 123]
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75)
at JavaPractice/com.JavaSE.NewFeature.Java9.CollectionTest.main(CollectionTest.java:11)
流拷貝方法
- 看過我之前講流的小伙伴,肯定會對流拷貝的例子有印象,好訊息!Java9把這個方法封裝啦!到時候我們直接一個方法呼叫就完事兒~這個方法就是InputStream的transferTo,
- 來看個例子就懂啦~流的copy so easy!
public class StreamTest {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\吉他譜\\這一生關于你的風景\\這一生關于你的風景1.png");
OutputStream outputStream = new FileOutputStream("D:\\吉他譜\\這一生關于你的風景\\test.png");
//以前的話我們得在這兒用緩沖流寫一堆拷貝的方法,現在直接一個方法呼叫就OK
inputStream.transferTo(outputStream);
inputStream.close();
outputStream.close();
}
}
Java10新特性
- Java10其實是一個比較小的“大版本”,其中有兩個比較關鍵的變更:區域變數型別推斷和垃圾回收器的增強,不過垃圾回收涉及到JVM了,我還不太了解,所以這里就不講啦,
區域變數型別推斷
- 有時候變數型別寫起來會覺得好麻煩,于是Java10看不下去了,就整了個新活,
- Java10可以使用var來代替變數型別,僅適用于有初始值的區域變數、增強for回圈的索引和傳統for回圈的回圈變數,不能使用于方法形參、方法回傳型別、catch形參或任何其他型別的變數宣告,
- var不是關鍵字,意味著var可以繼續用于方法名、包名,但var不能作為類或則介面的名字,
- 給個栗子:
public class VarTest {
public static void main(String[] args) {
//代替區域變數型別
var list = new ArrayList<String>();
//代替增強for回圈索引
for(var str:list){
}
//代替傳統for回圈的回圈變數
for(var i = 0;i < list.size();i++){
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/262203.html
標籤:java
