Java 8 新特性
官網(wǎng)java8介紹地址
菜鳥教程關(guān)于java8的介紹
Java 8 里面加了很多的新特性,在這里我們只討論一些常用和實(shí)際可能用的到的特性,其他的特性,感興趣的人可以去官網(wǎng)研究下。
總的來說,常用的特性主要有:
- Lambda表達(dá)式 ——允許把函數(shù)作為一個(gè)方法的參數(shù)
- 方法引用 ——方法引用提供了非常有用的語法,可以直接引用已有的Java類或?qū)ο螅▽?shí)例)的方法和構(gòu)造器。與lambda聯(lián)合使用,方法引用可以使語言的構(gòu)造更緊湊簡(jiǎn)潔,減少冗余代碼
- 默認(rèn)方法 ——默認(rèn)方法就是一個(gè)在接口里面有了一個(gè)實(shí)現(xiàn)的方法(為L(zhǎng)ambda所推出的特性,java8之前接口不能擁有實(shí)現(xiàn)方法)
- Stream API ——新添加的Stream API(java.util.stream)把真正的函數(shù)式編程風(fēng)格引入到j(luò)ava中
- Date Time API ——加強(qiáng)對(duì)日期與時(shí)間的處理(LocalDateTime,LocalDate,LocalTime)
- Optional ——Optional類已成為Java8類庫(kù)的一部分,用來解決空指針異常
- Base64 ——Java8中,Base64編碼已經(jīng)成為Java類庫(kù)標(biāo)準(zhǔn)。Java8內(nèi)置了Base64的編碼器和解碼器
Lambda 表達(dá)式
Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(可以參考js)
Lambda 表達(dá)式,也可稱為閉包,它是推動(dòng) Java 8 發(fā)布的最重要新特性。
Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。
使用 Lambda 表達(dá)式可以使代碼變的更加簡(jiǎn)潔緊湊。
語法
(parameters, parameters) -> expressions
(parameters) -> { statements; }
如何了解Lambda表達(dá)式
通過這段時(shí)間上網(wǎng)上找資料、看視頻、翻官方文檔,我發(fā)現(xiàn)我們可以通過這個(gè)脈絡(luò)去了解和使用Lambda表達(dá)式:匿名內(nèi)部類->函數(shù)式接口->Lambda表達(dá)式
內(nèi)部類->匿名內(nèi)部類
內(nèi)部類:將一個(gè)類的定義放在另一個(gè)類的定義內(nèi)部,就是內(nèi)部類。其優(yōu)點(diǎn)為可以訪問外部類中的資源,就是屬性、方法等;缺點(diǎn)破壞代碼結(jié)構(gòu),簡(jiǎn)單的說就是丑。
匿名內(nèi)部類:顧名思義就是沒有名字的內(nèi)部類;通常為直接初始化一個(gè)接口,并初始化時(shí)將接口的方法實(shí)現(xiàn)。(我在寫android的時(shí)候倒是經(jīng)常遇到)
這里我們通過一個(gè)官方內(nèi)部類的示例改寫下來了解下:
package com.education.java8;
/**
* @author weichen
* @description: 匿名內(nèi)部類實(shí)例
* @create 2020-04-23 13:47
* @from https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
*/
public class HelloWorldAnonymousClasses {
// 申明一個(gè)接口
interface HelloWorld {
void greet();
void greetSomeone(String someone);
}
interface HelloWorldLambda {
void greet();
}
// 定義一個(gè)方法
public void sayHello() {
// 方法內(nèi)部類實(shí)現(xiàn)了HelloWorld接口
class EnglishGreeting implements HelloWorld {
String name = "world";
@Override
public void greet() {
greetSomeone("world");
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
// 匿名內(nèi)部類實(shí)現(xiàn)
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
@Override
public void greet() {
greetSomeone("tout le monde");
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
// 匿名內(nèi)部類實(shí)現(xiàn)
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
@Override
public void greet() {
greetSomeone("mundo");
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola," + name);
}
};
// 匿名內(nèi)部類實(shí)現(xiàn)
HelloWorld chineseGreeting1 = new HelloWorld() {
String name = "世界";
@Override
public void greet() {
greetSomeone("世界");
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("你好 " + name);
}
};
// 匿名內(nèi)部類實(shí)現(xiàn)
// HelloWorldLambda chineseGreeting2 = new HelloWorldLambda() {
// @Override
// public void greet() {
// System.out.println("你好 世界");
// }
// };
HelloWorldLambda chineseGreeting2 = () -> System.out.println("你好 世界");
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
chineseGreeting1.greet();
chineseGreeting2.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
輸出:
Hello world
Salut Fred
Hola,mundo
你好 世界
你好 世界
獲取上面的例子在實(shí)際中沒啥卵用,這里再給出個(gè)線程相關(guān)的例子:
package com.education.java8;
/**
* @author weichen
* @description: 創(chuàng)建Runnable接口的實(shí)現(xiàn)類,重寫run方法,設(shè)置線程任務(wù)
* @create 2020-04-24 14:39
*/
public class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "被創(chuàng)建了");
}
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
Thread t = new Thread(runnable);
t.start();
// 簡(jiǎn)化代碼,使用匿名內(nèi)部類
Runnable runnable1 = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "被創(chuàng)建了");
}
};
new Thread(runnable1).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "被創(chuàng)建了");
}
}).start();
new Thread(() -> System.out.println(Thread.currentThread().getName() + "被創(chuàng)建了")).start();
}
}
輸出:
Thread-0被創(chuàng)建了
Thread-1被創(chuàng)建了
Thread-2被創(chuàng)建了
Thread-3被創(chuàng)建了
上面的例子中,我們發(fā)現(xiàn)對(duì)于有且僅有一個(gè)抽象方法的接口,我們可以使用lambda表達(dá)式,而大于一個(gè)的接口只能繼續(xù)使用匿名內(nèi)部類的方法。這里就衍生出了函數(shù)式接口的概念。
函數(shù)式接口
函數(shù)式接口就是一個(gè)有且僅有一個(gè)抽象方法,但是可以有多個(gè)非抽象方法的接口。
首先,我們來定義一個(gè)函數(shù)式接口:
/**
* @author weichen
* @description: 函數(shù)式接口,注意:只包含一個(gè)抽象方法,或多個(gè)default修飾的默認(rèn)方法
* @create 2020-05-12 22:17
*/
@FunctionalInterface
public interface FunctionService {
void test();
default void printString(String msg) {
System.out.println(msg);
}
}
在java8后,可以通過@FunctionalInterface注解來定義函數(shù)式接口,其實(shí)在該注解源碼的注釋里已經(jīng)給出了詳細(xì)的說明:
/**
* An informative annotation type used to indicate that an interface
* type declaration is intended to be a <i>functional interface</i> as
* defined by the Java Language Specification.
*
* 一種信息性注釋類型,用于指示接口類型聲明是由Java語言規(guī)范定義的<i>函數(shù)接口</i>
*
* Conceptually, a functional interface has exactly one abstract
* method. Since {@linkplain java.lang.reflect.Method#isDefault()
* default methods} have an implementation, they are not abstract. If
* an interface declares an abstract method overriding one of the
* public methods of {@code java.lang.Object}, that also does
* <em>not</em> count toward the interface's abstract method count
* since any implementation of the interface will have an
* implementation from {@code java.lang.Object} or elsewhere.
*
* 從概念上講,函數(shù)接口只有一個(gè)抽象方法。因?yàn)閧@linkplain
* java.lang.reflect.Method#isDefault()default
* methods}有一個(gè)實(shí)現(xiàn),所以它們不是抽象的。如果一個(gè)接口聲明了一個(gè)抽象方法重寫{@
* ode java.lang.Object}的一個(gè)公共方法,那么它也會(huì)對(duì)接口的抽象方法計(jì)數(shù)進(jìn)行<em>
* 不是<em>計(jì)數(shù),因?yàn)榻涌诘娜魏螌?shí)現(xiàn)都將具有來自{@code
* java.lang.Object}或其他地方的實(shí)現(xiàn)。
*
* <p>Note that instances of functional interfaces can be created with
* lambda expressions, method references, or constructor references.
*
* <p>注意,函數(shù)接口的實(shí)例可以使用lambda表達(dá)式、方法引用或構(gòu)造函數(shù)引用創(chuàng)建。
*
* <p>If a type is annotated with this annotation type, compilers are
* required to generate an error message unless:
*
* <p>如果使用此注解類型對(duì)類型進(jìn)行注解,則編譯器生成錯(cuò)誤消息,那么需要注意的是:
*
* <ul>
* <li> The type is an interface type and not an annotation type, enum, or
* class.
* <li> The annotated type satisfies the requirements of a functional interface.
* </ul>
*
* <ul><li>類型是接口類型,而不是注解類型、枚舉或類。<li>帶注解的類型滿足函數(shù)式接口的要求。</ul>
*
* <p>However, the compiler will treat any interface meeting the
* definition of a functional interface as a functional interface
* regardless of whether or not a {@code FunctionalInterface}
* annotation is present on the interface declaration.
*
* <p>但是,無論接口聲明中是否存在{@code-functionainterface}注解,編譯器都會(huì)將滿足函數(shù)接口定義的任何接口視為函數(shù)接口。
*
* @jls 4.3.2. The Class Object
* @jls 9.8 Functional Interfaces
* @jls 9.4.3 Interface Method Body
* @since 1.8
*/
@Documented // 可被javadoc導(dǎo)出
@Retention(RetentionPolicy.RUNTIME)// 運(yùn)行時(shí)注解
@Target(ElementType.TYPE)// 參數(shù)類型type
public @interface FunctionalInterface {}
注:在java8中如果你的接口符合函數(shù)式接口的定義,編譯器默認(rèn)是將其編譯為函數(shù)式接口,在上面的注釋中已經(jīng)有體現(xiàn)。
其實(shí)在java8之前,接口是只能包含抽象方法,然而在8之后添加了一種默認(rèn)方法。默認(rèn)方法使得8之后的代碼完美的與之前的版本相兼容。
函數(shù)式接口在java里實(shí)際上仍然是對(duì)象。
使用Lambda表達(dá)式
forEach -> Consumer
java中對(duì)一個(gè)數(shù)組遍歷有幾種方法。java8之前有 for->i, 增強(qiáng)型遍歷for->Object,迭代器遍歷等,java8后多出個(gè)forEach,及外部遍歷和內(nèi)部遍歷。
forEach的內(nèi)部其實(shí)也是使用迭代器遍歷的,注意forEach中不好對(duì)元素進(jìn)行刪除和新增會(huì)報(bào)ConcurrentModificationException異常,但可以修改屬性。