JDK8特性總結

JDK8(一)

1.Grande介紹

.Grandle/Maven:目錄結構一樣的,約定優於配置

源代碼:src/main/java

配置文件:src/main/resources

測試代碼:src/text/java

測試配置文件:src/text/resources

頁面相關:src/main/webapp

setting.gradle:項目名字

build.gradle:項目的描述,類似於pom.xml,包括項目所依賴的信息

 sourceCompatibility = 1.8//源代碼兼容性
 targetCompatibility = 1.8//編譯後代碼兼容性
 dependencies {
 //    testCompile group: 'junit', name: 'junit', version: '4.12'
     testCompile(
             "junit:junit:4.11"
     )
 }

2.Lambda表達式基本格式

(param1,param2,param3)->{

}

 package com.tang.jdk8;
 ​
 import javax.swing.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 ​
 public class SwingTest {
     public static void main(String[] args) {
         JFrame jFrame = new JFrame("My JFrame");
         JButton jButton = new JButton("My JButton");
 ​
         //按按鈕之後會自動調用方法
         //java爲靜態類型語言,e爲類型推斷後結果,
         jButton.addActionListener(e -> System.out.println("Button Pressed!"));
         jFrame.add(jButton);
         //正好是主鍵的大小
         jFrame.pack();
         jFrame.setVisible(true);
         //關閉的時候整個程序退出
         jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     }
 }

3.@FunctionalInterface

 package java.util.function;
 @FunctionalInterface
 /*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.
  ......
  */

1.函數式接口:如果一個接口只有一個抽象方法,那麼他就是函數式接口。

2.如果我們在某個接口上聲明瞭@FunctionalInterface註解,那麼編譯器就按照函數式接口的定義來要求該接口。

3.如果某個接口只有一個抽象方法,但我們沒有給該接口聲明,@FunctionalInterface註解,那麼編譯器依舊會將該接口看做函數式接口。

 /*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.
 */
 @FunctionalInterface
 public interface MyInterface {
     void test();
     
     String isString();
 }

Multiple non-overriding abstract methods found in interface com.tang.jdk8.MyInterface

 @FunctionalInterface
 public interface MyInterface {
     void test(); 
     
     @Override
     String toString();
 }

==toString 爲Object類下的public方法,所以註解不認爲他會一個新的抽象方法,因爲任何藉口都要直接或者間接繼承Object類,繼承裏面的所有方法

@FunctionalInterface
interface MyInterface {
    void test();

    @Override
    String toString();
}
public class Test2{
    public void myTest(MyInterface myInterface){
        System.out.println(1);
        myInterface.test();
        System.out.println(2);
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();

        test2.myTest(()-> System.out.println("mytest"));

        MyInterface myInterface=()-> System.out.println("hello");
        System.out.println(myInterface.getClass());
        System.out.println(myInterface.getClass().getSuperclass());
        System.out.println(myInterface.getClass().getInterfaces().length);
        System.out.println(myInterface.getClass().getInterfaces()[0]);
    }
}
1
mytest
2
class com.tang.jdk8.Test2$$Lambda$2/1078694789
class java.lang.Object 
1
interface com.tang.jdk8.MyInterface

4.forEach

List extends Collection extends Iterable
public interface Iterable<T> {
 
    Iterator<T> iterator();
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

所以list繼承了forEach

默認方法保證了Lambda,函數式接口加入,又保證了向下兼容

 

在將函數作爲一等公民的語言中,Lambda表達式是函數(Python)。但在java中,Lambda表達式是對象,他們必須依附於一類特別的對象類型——函數式接口。

5.方法引用(粗略)

通過方法引用的形式創建Lambda接口的實例

list.stream().map(String::toUpperCase).forEach(System.out::println);
public String toUpperCase() {
    return toUpperCase(Locale.getDefault());
}

調用toUpperCase方法的當前對象作爲輸入

輸出爲執行完toUpperCase方法後的對象

6.Function

Function<String,String> function=String::toUpperCase;

不能寫成String.toupperCase,因爲toUpperCase不是靜態方法

**如5所講,一定會存在一個String的實例對象(假設爲str),去調用toUpperCase**

總結:如果一個類類型,直接通過 :: 引用實例方法,對應Lambda表達式的第一個參數,就是調用這個方法的對象(this)

 

public class FunctionTest {
    public static void main(String[] args) {
        FunctionTest functionTest=new FunctionTest();
        System.out.println(functionTest.compute(10, val->2*val));
        System.out.println(functionTest.convert( 10, val->String.valueOf(val+"你好")));
        System.out.println(functionTest.convert( 10, val->Integer.toString(val)+"hello"));
     	 System.out.println(functionTest.method1(10));
        
    }
    public int compute(int a, Function<Integer,Integer> function){
        int result = function.apply(a);
        return result;
    }
    public String convert(int a, Function<Integer,String> function){
        return function.apply(a);
    }
    public int method1(int a){
        return a*a;
    }
}

Integer -->String Integer.toString(a); String.valueOf

結論:Lambda表達式傳遞的是行爲,以前是行爲提前定義好,調用行爲

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

區別:

compose :先調用參數接口執行apply()方法,得到的結果作爲參數再執行apply()方法

andThen:先調用本接口內的apply()方法,得到的接口再調用參數的apply接口的apply()方法

public class FunctionTest2 {
    public static void main(String[] args) {
        FunctionTest2 functionTest2 = new FunctionTest2();
        //4
        int compute = functionTest2.compute(1, val -> val * val, val -> val + 1);
        System.out.println("compute = " + compute);
        //2
        int compute2 = functionTest2.compute2(1, val -> val * val, val -> val + 1);
        System.out.println("compute2 = " + compute2);
    }
    public int compute(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
        return function1.compose(function2).apply(a);
    }
    public int compute2(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
        return function1.andThen(function2).apply(a);
    }
}
compute = 4
compute2 = 2

先執行function2.apply()再把得到的結果作爲參數執行function1.apply()

 

拓展:BiFuction

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

7.高階函數

如果一個函數接收一個函數作爲參數,或者返回一個函數作爲返回值,那麼該函數稱爲高階函數。

8.面向對象和麪向函數

面向對象傳遞一個參數(對象),方法體裏面定義行爲,處理業務邏輯

函數式編程傳遞兩個參數(對象+函數式接口),具體行爲被調用着定義,在方法體裏面沒有具體表現(提供了更高層次的抽象化)

public class PredicateTest2 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        PredicateTest2 predicateTest2=new PredicateTest2();
        predicateTest2.conditionalFileter(list, num->num%2!=0);
        System.out.println();
        predicateTest2.conditionalFileter(list, num->num%2==0);

    }

    public void conditionalFileter(List<Integer> list, Predicate<Integer> predicate){
        for (Integer i : list) {
            if (predicate.test(i)){
                System.out.println(i);
            }
        }
    }
}
1 3 5 7 9 
2 4 6 8 10  

沒有判斷條件的時候,打印所有

predicateTest2.conditionalFileter(list, num->true);

9.Supplier

public class Student {
    private String name="張三";
    private int age=20;
    //構造方法略
}
public class StudentTest {
    public static void main(String[] args) {
        Supplier<Student> supplier=()->new Student();
        System.out.println(supplier.get().getName());
    }
}

用於沒有輸入參數的工廠

public class BinaryOperateorTest {
    public static void main(String[] args) {
        BinaryOperateorTest operateorTest = new BinaryOperateorTest();
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c+d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c-d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c*d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c+d));

        String aShort = operateorTest.getShort("tang", "yao", (a, b) -> a.length() -b.length());
        System.out.println("aShort = " + aShort);
        String aShort1 = operateorTest.getShort("tang", "yao", (a, b) -> a.getBytes()[0] - b.getBytes()[0]);
        System.out.println("aShort1 = " + aShort1);
    }
    public int opearte(int a,int b, BinaryOperator<Integer> binaryOperator){
       return binaryOperator.apply(a, b);
    }
    public String getShort(String a, String b, Comparator<String> comparator){
        return BinaryOperator.minBy(comparator).apply(a, b);
    }

}
30
-10
200
30
aShort = yao
aShort1 = tang
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
    Objects.requireNonNull(comparator);
    return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}

10.Optional

NPE NullPointerException

if(null!=person){ person...

}

創建Optional對象方法

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}
private static final Optional<?> EMPTY = new Optional<>(); private Optional() {
        this.value = null;
    }

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}
private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    
    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

重要方法

public boolean isPresent() {
    return value != null;
}
 public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
 //從數據庫查出一個對象,不確定是否爲空
Optional<String> optional=Optional.ofNullable("hello");
//        if (optional.isPresent()){
//            System.out.println(optional.get());
//        }
        optional.ifPresent(p-> System.out.println(p));
        System.out.println("------------");
        System.out.println(optional.orElse("world"));
        System.out.println("------------");
        System.out.println(optional.orElseGet(()->"你好"));

Optional沒有被序列化,做參數的時候會被警告

uploading.4e448015.gif轉存失敗重新上傳取消

11.Value-based Classes

Some classes, such as

java.util.Optional

and

java.time.LocalDateTime

, are

value-based

. Instances of a value-based class:

  • are final and immutable (though may contain references to mutable objects);

  • have implementations of equals, hashCode, and toString which are computed solely from the instance's state and not from its identity or the state of any other object or variable;

  • make no use of identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;

  • are considered equal solely based on equals(), not based on reference equality (==);

  • do not have accessible constructors, but are instead instantiated through factory methods which make no committment as to the identity of returned instances;

  • are freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() in any computation or method invocation should produce no visible change in behavior.

A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.

12.方法引用

方法引用實際上是Lambda表達式的語法糖

我們可以將方法引用看做【函數指針】

方法引用共分爲四類

1.類名::靜態方法名

2.引用名(對象名)::實例方法名

3.類名::實例方法名

public int compareByName(Student stu1){
        return this.getName().compareToIgnoreCase(stu1.getName());
}

students.sort((stu1,stu2)->stu1.compareByName(stu2));
students.forEach(System.out::println);
students.sort(Student::compareByName);

類的名字引用方法的時候,一定有對象調用compareByName方法,這裏調用的compareByName實際上是第一個student對象(Lambda表達式第一個參數),也就是當前對象stu1(this),後面傳入的對象作爲參數

4.構造方法引用:類名::new

public String getString(Supplier<String> supplier){
    return supplier.get() + "test";
}

public String getString2(String str, Function<String,String> function){
    return function.apply(str);
}
MethodRefencedTest methodRefencedTest=new MethodRefencedTest();
System.out.println(methodRefencedTest.getString(String::new));
System.out.println(methodRefencedTest.getString2("hello",String::new));
public String() {
    this.value = "".value;
}
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

test hello

13.默認方法

public interface MyInterface1 {
    default void mythod(){
        System.out.println("MyInterface1");
    }
}
public interface MyInterface2 {
    default void mythod(){
        System.out.println("MyInterface2");
    }
}
public class MyClass implements MyInterface1,MyInterface2 {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.mythod();
    }
    @Override
    public void mythod() {
//        System.out.println("MyClass");
        MyInterface2.super.mythod();
    }
}

實現了多個接口,如果默認方法名相同,則必須重寫方法,如果想用其中一個方法,可以使用接口名+super+默認方法名的方法

java認爲實現類比接口更爲具體,所以如果MyClass繼承了MyInterface1Impl和實現了Interface2,那麼對於具有相同的默認方法,則類MyClass優先調用繼承類裏面的方法。

14.爲什麼要有默認方法

在 java 8 之前,接口與其實現類之間的 耦合度 太高了(tightly coupled),當需要爲一個接口添加方法時,所有的實現類都必須隨之修改。默認方法解決了這個問題,它可以爲接口添加新的方法,而不會破壞已有的接口的實現。這在 lambda 表達式作爲 java 8 語言的重要特性而出現之際,爲升級舊接口且保持向後兼容(backward compatibility)提供了途徑。

String[] array = new String[] {
        "hello",
        ", ",
        "world",
};
List<String> list = Arrays.asList(array);
list.forEach(System.out::println); // 這是 jdk 1.8 新增的接口默認方法

這個 forEach 方法是 jdk 1.8 新增的接口默認方法,正是因爲有了默認方法的引入,纔不會因爲 Iterable 接口中添加了 forEach 方法就需要修改所有 Iterable 接口的實現類。

15.Stream

三部分構成

1.source(源)

2.零個或多箇中間操作

3.終止操作

流操作的分類

1.惰性求值(中間操作)

2.及早求值(終止求值

構造stream的方法

Stream stream1=Stream.of("hello","world","helloWorld!");
String[] MyArray=new String[]{"hello","world","helloWorld!"};
Stream stream2=Stream.of(MyArray);
Stream stream3= Arrays.stream(MyArray);
List<String> list=Arrays.asList(MyArray);
Stream<String> stream4 = list.stream();

本質區別:函數式編程傳遞的是行爲,根據行爲對數據進行加工,面向對象編程傳遞的是數據

 

16.方法簽名:

方法簽名由方法名稱和一個參數列表(方法的參數的順序和類型)組成。

17.stream.collect

<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);
R result = supplier.get();
   for (T element : this stream)
       accumulator.accept(result, element);
    return result;
List<String> list =
        stream.collect(()->new ArrayList<>(),(list1,item)->list1.add(item),(list1,list2)->list1.addAll(list2));

collect需要三個參數,第一個參數是調用supplier的get方法,也就是相當於得到一個list集合,例如:

()->new ArrayList<>()

第二個參數是調用BiConSumer函數式接口的accept方法,該方法傳入兩個參數,不返回值,例如:

(list1,item)->list1.add(item) item爲stream流的每一個元素,添加到每一個ArrayList中

第三個參數也是調用BiConSumer函數式接口的accept方法,目的是將每次產生的Arraylist合併到一個新的ArrayList集合中

(list1,list2)->list1.addAll(list2)

轉換爲對應的方法引用爲:

List<String> list=stream.collect(LinkedList::new,LinkedList::add,LinkedList::addAll);
list.forEach(System.out::println);

hello world helloWorld!

/*
*
* @param <R> type of the result
* @param supplier a function that creates a new result container. For a
*                 parallel execution, this function may be called
*                 multiple times and must return a fresh value each time.
* @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
*                    <a href="package-summary.html#NonInterference">non-interfering</a>,
*                    <a href="package-summary.html#Statelessness">stateless</a>
*                    function for incorporating an additional element into a result
* @param combiner an <a href="package-summary.html#Associativity">associative</a>,
*                    <a href="package-summary.html#NonInterference">non-interfering</a>,
*                    <a href="package-summary.html#Statelessness">stateless</a>
*                    function for combining two values, which must be
*                    compatible with the accumulator function
* @return the result of the reduction
*/

==========華麗的分割線

stream.collect(Collectors.toList());
public static <T>
Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}

默認返回的是ArrayList,如果想返回LinkedList就需要看懂源代碼操作

Collections.toCollection();

public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
    return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                               (r1, r2) -> { r1.addAll(r2); return r1; },
                               CH_ID);
}

根據自己需要創建集合

TreeSet<String> set = stream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(set.getClass());
set.forEach(System.out::println);

hello helloWorld! world

Collectors.joining()拼接stream中內容

String string = stream.collect(Collectors.joining(" ")).toString();
System.out.println(string);


hello world helloWorld!

18.map()

List<String> list = Arrays.asList("hello", "world", "helloWorld", "test");
list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);
List<Integer> list1=Arrays.asList(1,2,34,5,6);
list1.stream().map(i->i*2).collect(Collectors.toList()).forEach(System.out::println);

TEST 2 4 68 10 12

flatMap()

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<List<Integer>> stream = Stream.of(Arrays.asList(0), Arrays.asList(1, 2), Arrays.asList(3, 4, 5, 6));
stream.flatMap(theList->theList.stream()).map(i->i*i).forEach(System.out::println);

0 1 4 9 16 25 36

19.generate()

generate根據Supplier獲得對象得到帶流

public static<T> Stream<T> generate(Supplier<T> s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
            new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
Stream<String> stream = Stream.generate(UUID.randomUUID()::toString);
stream.findFirst().ifPresent(System.out::println);

01a7d356-448d-47f3-80b9-9c91e6eda6a7

iterate通過種子(seed)得到流

Returns an infinite sequential ordered {@code Stream} produced by iterative
application of a function {@code f} to an initial element {@code seed},
producing a {@code Stream} consisting of {@code seed}, {@code f(seed)},
{@code f(f(seed))}, etc
<p>The first element (position {@code 0}) in the {@code Stream} will be
the provided {@code seed}.  For {@code n > 0}, the element at position
{@code n}, will be the result of applying the function {@code f} to the
element at position {@code n - 1}.
   簡單來說就是得到一個無限串順序流,給定一個seed,然後f作用於seed生成新的f(seed)無限循環下去
   f(f(seed))...
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f{
...
}

UnaryOperator繼承Function,得到一個元素返回相同類型元素

public interface UnaryOperator<T> extends Function<T, T> {
    ...
}

因爲是無限串行流,所以需要限制一下(limit)次數

Stream.iterate(2, item->item*2).limit(10).forEach(System.out::println);

2 4 8 16 32 64 128 256 512 1024

int sum = Stream.iterate(1, item -> item + 2).limit(6).filter(i -> i > 2).mapToInt(i -> i * 2).skip(2).limit(2).sum();
System.out.println("sum = " + sum);

sum = 32

Stream.iterate(0, i->(i+1)%2).distinct().limit(6).forEach(System.out::println);

0 1

distinct在前 程序沒有終止

Stream.iterate(0, i->(i+1)%2).limit(6).distinct().forEach(System.out::println);

0 1

Process finished with exit code 0

20.內部迭代和外部迭代的區別

uploading.4e448015.gif轉存失敗重新上傳取消

集合關注的是數據與數據存儲本身,流關注的是數據的計算。

流與迭代器類似的一點是:流是無法重複使用消費的

21.習題:去重

List<String> list = Arrays.asList("hello welcome", "world hello", "hello world hello", "hello welcome");
//        list.stream().map(item->item.split(" ")).distinct().collect(Collectors.toList())
//                .forEach(System.out::println);
        list.stream().map(item->item.split(" ")).flatMap(s->Arrays.stream(s))
                .distinct().collect(Collectors.toList()).forEach(System.out::println);

hello welcome world

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章