28.java中Java8.0的新特性(附讲解和练习)
Java 8新特性簡介
- 速度更快
- 代碼更少(增加了新的語法: Lambda 表達式)
- 強大的 Stream API
- 便于并行
- 最大化減少空指針異常: Optional
- Nashorn引擎,允許在JVM上運行JS應用
Lambda表達式(重要)(Lambda表達式就是一個函數式接口的實例)
Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。使用它可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。
Lambda表達式的使用
1.舉例: (o1,o2) -> Integer.compare(o1,o2);
2.格式:
- -> :lambda操作符 或 箭頭操作符
- ->左邊:lambda形參列表 (其實就是接口中的抽象方法的形參列表)
- ->右邊:lambda體 (其實就是重寫的抽象方法的方法體)
3.Lambda表達式的使用:(分為6種情況介紹)
總結:
- ->左邊:lambda形參列表的參數類型可以省略(類型推斷);如果lambda形參列表只有一個參數,其一對()也可以省略
- ->右邊:lambda體應該使用一對{}包裹;如果lambda體只有一條執行語句(可能是return語句),省略這一對{}和return關鍵字
4.Lambda表達式的本質:作為函數式接口的實例
5.如果一個接口中,只聲明了一個抽象方法,則此接口就稱為函數式接口。我們可以在一個接口上使用 @FunctionalInterface 注解,這樣做可以檢查它是否是一個函數式接口。
6. 所以以前用匿名實現類表示的現在都可以用Lambda表達式來寫。
下面展示一些 Lambda表達式的使用。
public class LambdaTest1 {//語法格式一:無參,無返回值@Testpublic void test1(){Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("我愛北京天安門");}};r1.run();System.out.println("***********************");Runnable r2 = () -> {System.out.println("我愛北京故宮");};r2.run();}//語法格式二:Lambda 需要一個參數,但是沒有返回值。@Testpublic void test2(){Consumer<String> con = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};con.accept("謊言和誓言的區別是什么?");System.out.println("*******************");Consumer<String> con1 = (String s) -> {System.out.println(s);};con1.accept("一個是聽得人當真了,一個是說的人當真了");}//語法格式三:數據類型可以省略,因為可由編譯器推斷得出,稱為“類型推斷”@Testpublic void test3(){Consumer<String> con1 = (String s) -> {System.out.println(s);};con1.accept("一個是聽得人當真了,一個是說的人當真了");System.out.println("*******************");Consumer<String> con2 = (s) -> {System.out.println(s);};con2.accept("一個是聽得人當真了,一個是說的人當真了");}@Testpublic void test4(){ArrayList<String> list = new ArrayList<>();//類型推斷int[] arr = {1,2,3};//類型推斷}//語法格式四:Lambda 若只需要一個參數時,參數的小括號可以省略@Testpublic void test5(){Consumer<String> con1 = (s) -> {System.out.println(s);};con1.accept("一個是聽得人當真了,一個是說的人當真了");System.out.println("*******************");Consumer<String> con2 = s -> {System.out.println(s);};con2.accept("一個是聽得人當真了,一個是說的人當真了");}//語法格式五:Lambda 需要兩個或以上的參數,多條執行語句,并且可以有返回值@Testpublic void test6(){Comparator<Integer> com1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);}};System.out.println(com1.compare(12,21));System.out.println("*****************************");Comparator<Integer> com2 = (o1,o2) -> {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};System.out.println(com2.compare(12,6));}//語法格式六:當 Lambda 體只有一條語句時,return 與大括號若有,都可以省略@Testpublic void test7(){Comparator<Integer> com1 = (o1,o2) -> {return o1.compareTo(o2);};System.out.println(com1.compare(12,6));System.out.println("*****************************");Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);System.out.println(com2.compare(12,21));}@Testpublic void test8(){Consumer<String> con1 = s -> {System.out.println(s);};con1.accept("一個是聽得人當真了,一個是說的人當真了");System.out.println("*****************************");Consumer<String> con2 = s -> System.out.println(s);con2.accept("一個是聽得人當真了,一個是說的人當真了");}}函數式(Functional)接口
函數式(Functional)接口
? 只包含一個抽象方法的接口,稱為函數式接口。(Lambda表達式使用的前提)
? 你可以通過 Lambda 表達式來創建該接口的對象。(若 Lambda 表達式拋出一個受檢異常(即:非運行時異常),那么該異常需要在目標接口的抽象方法上進行聲明)。
? 我們可以在一個接口上使用 @FunctionalInterface 注解,這樣做可以檢查它是否是一個函數式接口。同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。
? 在java.util.function包下定義了Java 8 的豐富的函數式接口
下面展示一些 數據測試。
public class Employee {private int id;private String name;private int age;private double salary;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public Employee() {System.out.println("Employee().....");}public Employee(int id) {this.id = id;System.out.println("Employee(int id).....");}public Employee(int id, String name) {this.id = id;this.name = name;}public Employee(int id, String name, int age, double salary) {this.id = id;this.name = name;this.age = age;this.salary = salary;}@Overridepublic String toString() {return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Employee employee = (Employee) o;if (id != employee.id)return false;if (age != employee.age)return false;if (Double.compare(employee.salary, salary) != 0)return false;return name != null ? name.equals(employee.name) : employee.name == null;}@Overridepublic int hashCode() {int result;long temp;result = id;result = 31 * result + (name != null ? name.hashCode() : 0);result = 31 * result + age;temp = Double.doubleToLongBits(salary);result = 31 * result + (int) (temp ^ (temp >>> 32));return result;} }下面展示一些 提供用于測試的數據。
public class EmployeeData {public static List<Employee> getEmployees(){List<Employee> list = new ArrayList<>();list.add(new Employee(1001, "馬化騰", 34, 6000.38));list.add(new Employee(1002, "馬云", 12, 9876.12));list.add(new Employee(1003, "劉強東", 33, 3000.82));list.add(new Employee(1004, "雷軍", 26, 7657.37));list.add(new Employee(1005, "李彥宏", 65, 5555.32));list.add(new Employee(1006, "比爾蓋茨", 42, 9500.43));list.add(new Employee(1007, "任正非", 26, 4333.32));list.add(new Employee(1008, "扎克伯格", 35, 2500.32));return list;}}
下面展示一些 Java內置的4大核心函數式接口。
方法引用與構造器引用
方法引用(Method References)
? 當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
? 方法引用可以看做是Lambda表達式深層次的表達。換句話說,方法引用就是Lambda表達式,也就是函數式接口的一個實例,通過方法的名字來指向一個方法,可以認為是Lambda表達式的一個語法糖。
? 要求: 實現接口的抽象方法的參數列表和返回值類型,必須與方法引用的方法的參數列表和返回值類型保持一致!
? 格式: 使用操作符 “::” 將類(或對象) 與 方法名分隔開來。
? 如下三種主要使用情況:
- 對象::實例方法名
- 類::靜態方法名
- 類::實例方法名
下面展示一些 方法引用(Method References)。
/*** 方法引用的使用** 1.使用情境:當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!** 2.方法引用,本質上就是Lambda表達式,而Lambda表達式作為函數式接口的實例。所以* 方法引用,也是函數式接口的實例。** 3. 使用格式: 類(或對象) :: 方法名** 4. 具體分為如下的三種情況:* 情況1 對象 :: 非靜態方法* 情況2 類 :: 靜態方法** 情況3 類 :: 非靜態方法** 5. 方法引用使用的要求:要求接口中的抽象方法的形參列表和返回值類型與方法引用的方法的* 形參列表和返回值類型相同!(針對于情況1和情況2)** Created by shkstart.*/ public class MethodRefTest {// 情況一:對象 :: 實例方法//Consumer中的void accept(T t)//PrintStream中的void println(T t)@Testpublic void test1() {Consumer<String> con1 = str -> System.out.println(str);con1.accept("北京");System.out.println("*******************");PrintStream ps = System.out;Consumer<String> con2 = ps::println;con2.accept("beijing");}//Supplier中的T get()//Employee中的String getName()@Testpublic void test2() {Employee emp = new Employee(1001,"Tom",23,5600);Supplier<String> sup1 = () -> emp.getName();System.out.println(sup1.get());System.out.println("*******************");Supplier<String> sup2 = emp::getName;System.out.println(sup2.get());}// 情況二:類 :: 靜態方法//Comparator中的int compare(T t1,T t2)//Integer中的int compare(T t1,T t2)@Testpublic void test3() {Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);System.out.println(com1.compare(12,21));System.out.println("*******************");Comparator<Integer> com2 = Integer::compare;System.out.println(com2.compare(12,3));}//Function中的R apply(T t)//Math中的Long round(Double d)@Testpublic void test4() {Function<Double,Long> func = new Function<Double, Long>() {@Overridepublic Long apply(Double d) {return Math.round(d);}};System.out.println("*******************");Function<Double,Long> func1 = d -> Math.round(d);System.out.println(func1.apply(12.3));System.out.println("*******************");Function<Double,Long> func2 = Math::round;System.out.println(func2.apply(12.6));}// 情況三:類 :: 實例方法 (有難度)// Comparator中的int comapre(T t1,T t2)// String中的int t1.compareTo(t2)@Testpublic void test5() {Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);System.out.println(com1.compare("abc","abd"));System.out.println("*******************");Comparator<String> com2 = String :: compareTo;System.out.println(com2.compare("abd","abm"));}//BiPredicate中的boolean test(T t1, T t2);//String中的boolean t1.equals(t2)@Testpublic void test6() {BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);System.out.println(pre1.test("abc","abc"));System.out.println("*******************");BiPredicate<String,String> pre2 = String :: equals;System.out.println(pre2.test("abc","abd"));}// Function中的R apply(T t)// Employee中的String getName();@Testpublic void test7() {Employee employee = new Employee(1001, "Jerry", 23, 6000);Function<Employee,String> func1 = e -> e.getName();System.out.println(func1.apply(employee));System.out.println("*******************");Function<Employee,String> func2 = Employee::getName;System.out.println(func2.apply(employee));}}構造器引用
格式: ClassName::new
與函數式接口相結合,自動與函數式接口中方法兼容。可以把構造器引用賦值給定義的方法,要求構造器參數列表要與接口中抽象方法的參數列表一致!且方法的返回值即為構造器對應類的對象。
下面展示一些 構造器引用。
/*** 一、構造器引用* 和方法引用類似,函數式接口的抽象方法的形參列表和構造器的形參列表一致。* 抽象方法的返回值類型即為構造器所屬的類的類型*** Created by shkstart*/ public class ConstructorRefTest {//構造器引用//Supplier中的T get()//Employee的空參構造器:Employee()@Testpublic void test1(){Supplier<Employee> sup = new Supplier<Employee>() {@Overridepublic Employee get() {return new Employee();}};System.out.println("*******************");Supplier<Employee> sup1 = () -> new Employee();System.out.println(sup1.get());System.out.println("*******************");Supplier<Employee> sup2 = Employee :: new;System.out.println(sup2.get());}//Function中的R apply(T t)@Testpublic void test2(){Function<Integer,Employee> func1 = id -> new Employee(id);Employee employee = func1.apply(1001);System.out.println(employee);System.out.println("*******************");Function<Integer,Employee> func2 = Employee :: new;Employee employee1 = func2.apply(1002);System.out.println(employee1);}//BiFunction中的R apply(T t,U u)@Testpublic void test3(){BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);System.out.println(func1.apply(1001,"Tom"));System.out.println("*******************");BiFunction<Integer,String,Employee> func2 = Employee :: new;System.out.println(func2.apply(1002,"Tom"));} }數組引用
格式: type[] :: new
大家可以把數組看做是一個特殊的類,則寫法與構造器引用一致。
下面展示一些 數組引用。
//數組引用//Function中的R apply(T t)@Testpublic void test4(){Function<Integer,String[]> func1 = length -> new String[length];String[] arr1 = func1.apply(5);System.out.println(Arrays.toString(arr1));System.out.println("*******************");Function<Integer,String[]> func2 = String[] :: new;String[] arr2 = func2.apply(10);System.out.println(Arrays.toString(arr2));} var foo = 'bar';強大的Stream API(重要)
Stream API說明
使用Stream API 對集合數據進行操作,就類似于使用 SQL 執行的數據庫查詢。也可以使用 Stream API 來并行執行操作。 Stream API 提供了一種高效且易于使用的處理數據的方式。
Stream 和 Collection 集合的區別: Collection 是一種靜態的內存數據結構,而 Stream 是有關計算的。 前者是主要面向內存,存儲在內存中,后者主要是面向 CPU,通過 CPU 實現計算。
注意:
①Stream 自己不會存儲元素。
②Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。
Stream 的操作三個步驟
1- 創建 Stream一個數據源(如:集合、數組),獲取一個流
2- 中間操作: 一個中間操作鏈,對數據源的數據進行處理
3- 終止操作(終端操作) :一旦執行終止操作, 就執行中間操作鏈,并產生結果。之后,不會再被使用
創建 Stream方式一:通過集合
Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法:
? default Stream stream() : 返回一個順序流
? default Stream parallelStream() : 返回一個并行流
創建 Stream方式二:通過數組
Java8 中的 Arrays 的靜態方法 stream() 可以獲取數組流:
? static Stream stream(T[] array): 返回一個流
重載形式,能夠處理對應基本類型的數組:
? public static IntStream stream(int[] array)
? public static LongStream stream(long[] array)
? public static DoubleStream stream(double[] array)
創建 Stream方式三:通過Stream的of()
可以調用Stream類靜態方法 of(), 通過顯示值創建一個流。它可以接收任意數量的參數。
? public static Stream of(T… values) : 返回一個流
創建 Stream方式四:創建無限流
可以使用靜態方法 Stream.iterate() 和 Stream.generate(),創建無限流。
? 迭代
public static Stream iterate(final T seed, final UnaryOperator f)
? 生成
public static Stream generate(Supplier s)
下面展示一些 創建 Stream一個數據源(如:集合、數組)即Stream的實例化。
public class StreamAPITest {//創建 Stream方式一:通過集合@Testpublic void test1(){List<Employee> employees = EmployeeData.getEmployees();// default Stream<E> stream() : 返回一個順序流Stream<Employee> stream = employees.stream();// default Stream<E> parallelStream() : 返回一個并行流Stream<Employee> parallelStream = employees.parallelStream();}//創建 Stream方式二:通過數組@Testpublic void test2(){int[] arr = new int[]{1,2,3,4,5,6};//調用Arrays類的static <T> Stream<T> stream(T[] array): 返回一個流IntStream stream = Arrays.stream(arr);Employee e1 = new Employee(1001,"Tom");Employee e2 = new Employee(1002,"Jerry");Employee[] arr1 = new Employee[]{e1,e2};Stream<Employee> stream1 = Arrays.stream(arr1);}//創建 Stream方式三:通過Stream的of()@Testpublic void test3(){Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);}//創建 Stream方式四:創建無限流@Testpublic void test4(){// 迭代 // public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)//遍歷前10個偶數Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);// 生成 // public static<T> Stream<T> generate(Supplier<T> s)Stream.generate(Math::random).limit(10).forEach(System.out::println);}}Stream 的中間操作
多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱為“惰性求值” 。
1-篩選與切片
下面展示一些 篩選與切片。
2-映 射
下面展示一些 映射。
3-排序
下面展示一些 排序。
Stream 的終止操作
? 終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如: List、 Integer,甚至是 void 。
? 流進行了終止操作后,不能再次使用。
1-匹配與查找
下面展示一些 匹配與查找。
2-歸約
備注: map 和 reduce 的連接通常稱為 map-reduce 模式,因 Google用它來進行網絡搜索而出名。
下面展示一些 歸約。
3-收集
Collector 接口中方法的實現決定了如何對流執行收集的操作(如收集到 List、 Set、Map)。
另外, Collectors 實用類提供了很多靜態方法,可以方便地創建常見收集器實例,具體方法與實例如下表:
下面展示一些 收集。
Optional類
? Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。
? 創建Optional類對象的方法:
- Optional.of(T t) : 創建一個 Optional 實例, t必須非空;
- Optional.empty() : 創建一個空的 Optional 實例
- Optional.ofNullable(T t): t可以為null
? 判斷Optional容器中是否包含對象:
- boolean isPresent() : 判斷是否包含對象
- void ifPresent(Consumer<? super T> consumer) : 如果有值,就執行Consumer接口的實現代碼,并且該值會作為參數傳給它。
? 獲取Optional容器的對象:
- T get(): 如果調用對象包含值,返回該值,否則拋異常
- T orElse(T other) : 如果有值則將其返回,否則返回指定的other對象。
- T orElseGet(Supplier<? extends T> other) : 如果有值則將其返回,否則返回由Supplier接口實現提供的對象。
- T orElseThrow(Supplier<? extends X> exceptionSupplier) : 如果有值則將其返回,否則拋出由Supplier接口實現提供的異常。
下面展示一些 測試數據一。
public class Boy {private Girl girl;@Overridepublic String toString() {return "Boy{" +"girl=" + girl +'}';}public Girl getGirl() {return girl;}public void setGirl(Girl girl) {this.girl = girl;}public Boy() {}public Boy(Girl girl) {this.girl = girl;} }下面展示一些 測試數據二。
public class Girl {private String name;@Overridepublic String toString() {return "Girl{" +"name='" + name + '\'' +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public Girl() {}public Girl(String name) {this.name = name;} }下面展示一些 Optional類的使用。
public class OptionalTest {/* Optional.of(T t) : 創建一個 Optional 實例,t必須非空; Optional.empty() : 創建一個空的 Optional 實例 Optional.ofNullable(T t):t可以為null*/@Testpublic void test1(){Girl girl = new Girl(); // girl = null;//of(T t):保證t是非空的Optional<Girl> optionalGirl = Optional.of(girl);}@Testpublic void test2(){Girl girl = new Girl(); // girl = null;//ofNullable(T t):t可以為nullOptional<Girl> optionalGirl = Optional.ofNullable(girl);System.out.println(optionalGirl);//orElse(T t1):如果單前的Optional內部封裝的t是非空的,則返回內部的t.//如果內部的t是空的,則返回orElse()方法中的參數t1.Girl girl1 = optionalGirl.orElse(new Girl("趙麗穎"));System.out.println(girl1);}public String getGirlName(Boy boy){return boy.getGirl().getName();}@Testpublic void test3(){Boy boy = new Boy();boy = null;String girlName = getGirlName(boy);System.out.println(girlName);}//優化以后的getGirlName():public String getGirlName1(Boy boy){if(boy != null){Girl girl = boy.getGirl();if(girl != null){return girl.getName();}}return null;}@Testpublic void test4(){Boy boy = new Boy();boy = null;String girlName = getGirlName1(boy);System.out.println(girlName);}//使用Optional類的getGirlName():public String getGirlName2(Boy boy){Optional<Boy> boyOptional = Optional.ofNullable(boy);//此時的boy1一定非空Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪麗熱巴")));Girl girl = boy1.getGirl();Optional<Girl> girlOptional = Optional.ofNullable(girl);//girl1一定非空Girl girl1 = girlOptional.orElse(new Girl("古力娜扎"));return girl1.getName();}@Testpublic void test5(){Boy boy = null;boy = new Boy();boy = new Boy(new Girl("蒼老師"));String girlName = getGirlName2(boy);System.out.println(girlName);}}總結
以上是生活随笔為你收集整理的28.java中Java8.0的新特性(附讲解和练习)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis使用过程出现类型转换异常问题-
- 下一篇: java美元兑换,(Java实现) 美元