關(guān)于java8的學(xué)習(xí)(一)

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異常,但可以修改屬性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容