Java8新特性匯總(兩小時高效率學習Java8新特性知識)
檔案及資料已匯總至gitHub專區:
Java8NewProperty
1.Lambda運算式
Java8中引入了一個新的運算子“->”,該運算子稱為箭頭運算子或Lambda運算子,箭頭運算子將Lambda運算式拆分成兩部分,
左側:Lambda運算式的引數串列;
右側:Lambda運算式中所需執行的功能,即Lambda方法體,
Lambda運算式的語法格式及演示代碼:
/** * Lambda運算式支持函式式介面的編程方式 * 語法格式一:無引數、無回傳值 * () -> System.out.println("Hello Lambda!"); * 語法格式二:有一個引數無回傳值 * (x) -> System.out.println(x); * 語法格式三:若只有一個引數,小括號可以省略不寫 * x -> System.out.println(x); * 語法格式四:有兩個以上的引數,有回傳值,并且Lambda體中有多條陳述句 * Comparator<Integer> comparator = (x, y) -> { * return Integer.compare(x, y); * }; * 語法格式五:若Lambda體中只有一條陳述句,return和大括號都可以省略不寫 * 語法格式六:Lambda運算式的引數串列的資料型別可以省略不寫,因為JVM編譯器可以通過背景關系推斷出資料型別,即“型別推斷” * * 總結: * 1.左右遇一括號省,左側推斷型別省 * 2.Lambda運算式需要“函式式介面”的支持 * 函式式介面:介面中只有一個抽象方法的介面,稱為函式式介面,可以使用注解@FunctionalInterface修飾(檢查介面是否是函式式介面) */ public class TestLambda { @Test public void test1(){ final int num = 0; //jdk1.7以前,必須是final Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Hello Lambda!" + num); } }; r1.run(); System.out.println("--------------------------"); Runnable r2 = () -> System.out.println("Hello Lambda!" + num); r2.run(); } @Test public void test2(){ Consumer<String> con = x -> System.out.println(x); con.accept("這是單個引數的Lambda運算式"); } @Test public void test3(){ Comparator<Integer> comparator = (x, y) -> { return Integer.compare(x, y); }; } @Test public void test4(){ Comparator<Integer> comparator = (Integer x, Integer y) -> Integer.compare(x, y); } @Test public void test5(){ Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); } @Test public void test6(){ Integer res1 = operation(20, x -> x * 20); Integer res2 = operation(30, y -> y + 60); System.out.println(res1); System.out.println(res2); } public Integer operation(Integer num, MyFunction function){ return function.getValue(num); } }
2.函式式介面
函式式介面:介面中只有一個抽象方法的介面,稱為函式式介面,可以使用注解@FunctionalInterface修飾(檢查介面是否是函式式介面)
2.1.常用的java內置函式式介面
/** * Java8內置的四大核心函式式介面 * Consumer<T>:消費型介面:void accept(T t); * Supplier<T>:供給型介面:T get(); * Function<T, R>:函式型介面:R apply(T t); * Predicate<T>:斷言型介面:boolean test(T t); */ public class CoreFuction { //Consumer介面測驗 @Test public void testConsumer(){ consume(1000, m -> System.out.println("每月吃飯,消費" + m + "元")); } public void consume(double money, Consumer<Double> consumer){ consumer.accept(money); } //供給型介面 @Test public void testSupplier(){ List<Integer> list = getNumberList(10, () -> (int)(Math.random() * 100)); list.forEach(System.out::println); } public List<Integer> getNumberList(Integer num, Supplier<Integer> supplier){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { Integer res = supplier.get(); list.add(res); } return list; } //函式型介面 @Test public void testFunction(){ String handler = strHandler("\t\n B站資源好又多! ", str -> str.trim()); String strHandler1 = strHandler("B站資源好又多!", str -> str.substring(2, 4)); System.out.println(handler); System.out.println(strHandler1); } public String strHandler(String str, Function<String, String> function){ return function.apply(str); } //斷言型介面 @Test public void testPredicate(){ List<String> stringList = Arrays.asList("ded2d2d2", "dd22d", "shangguigu", "de", "楓夜Alex", "ddff"); List<String> res = predicateRes(stringList, t -> t.length() > 3 && t.startsWith("楓夜")); res.forEach(System.out::println); } public List<String> predicateRes(List<String> list, Predicate<String> predicate){ List<String> arrayList = new ArrayList<>(); for (String item : list) { if(predicate.test(item)){ arrayList.add(item); } } return arrayList; } }
3.方法參考與構造器參考
3.1.方法參考
/** * 方法參考:若Lamda體中的內容有方法已經實作了,我們可以使用“方法參考” * (可以理解為方法參考是Lambda運算式的另一種表現形式) * 主要有三種語法格式: * 物件::實體方法名 * 類::靜態方法名 * 類::實體方法名 * !!!注意: * ①Lambda體中呼叫方法的引數串列與回傳值型別,要與函式式介面中抽象方法的引數串列和回傳值型別保持一致 * ②若Lambda體中引數串列中的第一個引數是實體方法的呼叫者,而第二個引數是實體方法的引數值時,可以使用ClassName::method */ public class TestMethodRef { //物件::實體方法名 @Test public void test1(){ PrintStream ps = System.out; Consumer<String> con = x -> ps.println(x); //上面等價于 Consumer<String> ps1 = ps::println; //而ps又等價于System.out //最終等價于 Consumer<String> ps2 = System.out::println; } //物件::實體方法名-2 public void test11(){ Employee employee = new Employee(); Supplier<String> supplier = () -> employee.getName(); // T get(); String name = supplier.get(); System.out.println(name); //上面等價于 Supplier<String> supplier1 = employee::getName; String name1 = supplier.get(); System.out.println(name1); } //類::靜態方法名 @Test public void test2(){ //當呼叫Comparator Lambda方法體進行實作時,發現已經有Integer實作了這個方法,那么就可以直接呼叫 //而Integer的compare方法同時也是靜態方法,所以可以直接使用類名::方法名呼叫 Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2); //上面等價于: Comparator<Integer> comparator1 = Integer::compare; } //類::實體方法名 public void test3(){ BiPredicate<String, String> bp = (s1, s2) -> s1.equals(s2); BiPredicate<String, String> bp2 = String::equals; } }
3.2.構造器參考
/** * 構造器參考: * 語法格式: * ClassName::new * 注意:需要呼叫的構造器的引數串列要與函式式介面中抽象方法的引數串列保持一致 */ public class TestConstructorRef { @Test public void test(){ Supplier<Employee> supplier = () -> new Employee(); //上面形式等價于 Supplier<Employee> supplier1 = Employee::new; //注意:Employee::new自動呼叫無參構造器創建物件,取決于Supplier中介面方法是否有參 Employee employee = supplier1.get(); //Employee{name='null', age=null, salary=0.0, status=null} System.out.println(employee); } @Test public void test1(){ BiFunction<String, Integer, Employee> bif = (p1, p2) -> new Employee(); //上面形式等價于 BiFunction<String, Integer, Employee> bif1 = Employee::new; //需要Employee有對應的構造方法 Employee employee = bif1.apply("余杭", 23); System.out.println(employee); } }
3.3.陣列參考
/** * 陣列參考: * 語法格式: * Type::new */ public class TestTypeRef { @Test public void test(){ Function<Integer, String[]> function = x -> new String[x]; String[] apply = function.apply(10); System.out.println(apply.length); //上面形式等價于 Function<Integer, String[]> function1 = String[]::new; String[] apply1 = function1.apply(10); System.out.println(apply1.length); } }
4.Stream API
Java8中有兩大最為重要的改變,一個是Lambda運算式,另一個則是Stream API(java.util.stream.*),
Stream是Java8中處理集合(陣列)的關鍵抽象概念,利用Stream流式處理可以對集合執行非常復雜的查找、過濾和映射資料等操作,相當于資料庫的SQL執行資料庫查詢,還可以使用Stream API來并行執行操作,
注意:
- Stream自己不會存盤元素;
- Stream不會改變源物件,相反,它們會回傳一個持有結果的新Stream,
- Stream操作是延遲執行的,這意味者它們會等到需要結果的時候才執行,
4.1.創建Stream流的方式
//1.可以通過Collection系列集合提供的stream()或parallelStream()創建 List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //2.通過Arrays中的靜態方法stream()獲取陣列 Employee[] employees = new Employee[10]; Stream<Employee> stream1 = Arrays.stream(employees); //3.通過Stream類中的靜態方法0f() Stream<String> stream2 = Stream.of("aa", "bb", "cc"); //4.創建無限流 //迭代 Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2); stream3.limit(10).forEach(System.out::println); //5.使用Strem.generate生成 Stream<Double> stream4 = Stream.generate(() -> Math.random()); stream4.limit(5).forEach(System.out::println);
4.2.Stream的中間操作
多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!
而在終止操作時一次性全部處理,稱為“惰性求值”,
這部分是Java8流式操作集合陣列進行查詢、過濾、限制、分組、排序、映射功能操作的核心,代碼比較多,具體可以直接看我Github上的代碼,
4.3.并行流與順序流
并行流就是把一個內容分成多個資料塊,并用不同的執行緒分別處理每個資料塊的流,
Java8中將并行進行了優化,Stream API可以宣告性地通過parallel()與sequential()在并行流與順序流之間進行切換,
了解Fork/Join框架

使用fork/join框架在處理大資料情況下(資料量超過1000萬條),性能明顯;但Java8中parallel在處理大資料下性能更優秀!
/** * 大資料計算 */ public class ForkJoinCalculate extends RecursiveTask<Long> { private static final long serialVersionUID = 134656970987L; private long start; private long end; public ForkJoinCalculate(long start, long end){ this.start = start; this.end = end; } private static final long THRESHOLD = 10000; //拆分臨界值 @Override protected Long compute() { long sum = 0; long length = end -start; if(length <= THRESHOLD){ for (long i = start; i < end; i++) { sum += i; } return sum; }else { long middle = (start + end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start, middle); left.fork(); //拆分子任務,同時壓入執行緒佇列 ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end); right.fork(); //合并任務 return left.join() + right.join(); } } }
4.4.Optional類
Optional<T>類(java.util.Optional)是一個容器類,代表一個值存在或不存在,原來用null表示一個值不存在,現在Optional可以更好地表達這個概念,并且可以避免空指標例外,
常用方法:
-
Optional.of(T t):創建一個Optional實體
-
Optional.empty():創建一個空的Optional實體
-
Optional.ofNullable(T t):若t不為null,創建Optional實體,否則創建空實體
-
isPresent():判斷是否包含值
-
orElse(T t):如果呼叫物件包含值,回傳該值,否則回傳t
-
orElseGet(Supplier s):如果呼叫物件包含值,回傳該值,否則回傳s獲取的值
-
map(Function f):如果有值對其處理,并回傳處理后的Optional,否則回傳Optional.empty()
-
flatMap(Function mapper):與map類似,要求回傳值必須是Optional
Optional類在實際使用中可以避免大量重復的if/else回圈陳述句,達到美化代碼的目的:
//簡化if/else public String getUpperName(User user){ if (user != null) { String userName = user.getUserName(); if (userName != null) { return userName.toUpperCase(); } else { return null; } } else { return null; } } public String getOptionalUpName(User user){ return Optional.ofNullable(user) .map(User::getUserName) .map(String::toUpperCase) .orElse(null).toString(); }
5.介面中的默認方法與靜態方法
介面默認方法的“類優先”原則:
若一個介面中定義了一個默認方法,而另外一個父類或介面中又定義了一個同名的方法時,
-
優先選擇父類的方法,如果一個父類提供了具體實作,那么介面中具有相同名稱和引數的默認方法會被忽略,
-
介面沖突,如果一個父介面提供一個默認方法,而另一個父介面也提供了一個具有相同名稱和引數串列的方法(不管是否是默認方法),都必須覆寫該方法來解決沖突,
public interface MyFunction { //默認方法:可以實作默認方法 default String getName(){ return "Interface1 getName方法"; } //允許介面中有static靜態實作方法 public static void show(){ System.out.println("MyFunction介面中的靜態方法!"); } }
6.新時間日期API
替代了原來的Date、SimpleDateFormat等執行緒不安全的類,
新的日期時間API采用了java.time包下的chrono、format、temporal、zone等類,是不可變的執行緒安全的類,
6.1.本地時間與時間戳
LocalDate、LocalTime、LocalDateTime類的實體是不可變的物件,分別表示使用ISO-8601日歷系統的日期、時間、日期和時間,它們提供了簡單的日期或時間,并不包含當前的時間資訊,也不包含與時區相關的資訊,
//LocalDate LocalTime LocalDateTime:可供人識別的時間日期格式 @Test public void test1(){ LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime); LocalDateTime dateTime1 = LocalDateTime.of(2020, 7, 12, 6, 10); System.out.println(dateTime1); LocalDateTime plusDays = dateTime1.plusDays(3); System.out.println(plusDays); LocalDateTime minusDays = dateTime1.minusDays(3); System.out.println(minusDays); System.out.println(dateTime1.getYear()); System.out.println(dateTime1.getMonthValue()); System.out.println(dateTime1.getDayOfMonth()); System.out.println(dateTime1.getHour()); System.out.println(dateTime1.getMinute()); System.out.println(dateTime1.getSecond()); } //Instant:時間戳(以Unix元年:1970年1月1日00:00:00 到給定的時間之間的毫秒值) @Test public void test2(){ Instant ins1 = Instant.now(); //默認獲取UTC時區 System.out.println(ins1); System.out.println(ins1.toEpochMilli()); //獲取毫秒值 OffsetDateTime dateTime = ins1.atOffset(ZoneOffset.ofHours(8)); //獲取UTC時區帶偏移量的時間 System.out.println(dateTime); Instant epochSecond = Instant.ofEpochSecond(60);//相較于元年做時間加減運算 System.out.println(epochSecond); } //Duration:計算兩個“時間”之間的間隔 @Test public void test3(){ Instant ins1 = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant ins2 = Instant.now(); Duration duration = Duration.between(ins1, ins2); System.out.println(duration.toMillis()); System.out.println("--------------------------------"); LocalDateTime ldt1 = LocalDateTime.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } LocalDateTime ldt2 = LocalDateTime.now(); Duration duration2 = Duration.between(ldt1, ldt2); System.out.println(duration2.toMillis()); } //Period:計算兩個“日期”之間的間隔 @Test public void test4(){ LocalDate ld1 = LocalDate.of(2015, 10, 23); LocalDate ld2 = LocalDate.now(); Period period = Period.between(ld1, ld2); System.out.println(period); System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); }
6.2.日期的操縱
-
TemporalAdjuster:時間校正器,有時我們可能需要獲取例如:將日期調整到“下個周日”等操作,
-
TemporalAdjusters:該類通過靜態方法提供了大量的常用TemporalAdjuster的實作,
//TemporalAdjuster:時間校正器 //TemporalAdjusters:集成了TemporalAdjuster,內部增加了多種方法 @Test public void test5(){ LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt); LocalDateTime ldt2 = ldt.withDayOfMonth(10); System.out.println(ldt2); //將時間自動調整到下周日 LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println(ldt3); //自定義:下一個作業日 LocalDateTime workDate = ldt.with(lt -> { LocalDateTime ldt4 = (LocalDateTime) lt; if (ldt4.getDayOfWeek().equals(DayOfWeek.FRIDAY)) { return ldt4.plusDays(3); } else if (ldt4.getDayOfWeek().equals(DayOfWeek.SATURDAY)) { return ldt4.plusDays(2); } else { return ldt4.plusDays(1); } }); System.out.println(workDate); }
6.3.時區的處理
Java8中加入了對時區的支持,帶時區的時間為分別為:
ZonedDate、ZonedTime、ZonedDateTime
其中每個時區都對應著ID,地區ID都為“{區域}/{城市}”的格式,例如:Asia/Shanghai等,
ZoneId:該類中包含了所有的時區資訊:
getAvailableZoneIds():可以獲取所有時區時區資訊,
of(id):用指定的時區資訊獲取ZoneId物件,
/** * 獲取不同時區資訊 */ public class TestZoneTime { //獲取所有可用時區集合 @Test public void test1(){ Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); availableZoneIds.forEach(System.out::println); } @Test public void test2(){ LocalDateTime time1 = LocalDateTime.now(ZoneId.of("Asia/Pontianak")); System.out.println(time1); LocalDateTime time2 = LocalDateTime.now(ZoneId.of("Asia/Pontianak")); ZonedDateTime zoneShanghai = time2.atZone(ZoneId.of("Asia/Shanghai")); //2021-04-13T13:11:28.149+08:00[Asia/Shanghai] System.out.println(zoneShanghai); } }
7.其它新特性
7.1.重復注解與型別注解
Java8對注解處理提供了兩點改進:可重復的注解及可用于型別的注解,
可重復注解:可以在指定的方法或者是類上面添加相同的注解,
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotations { MyAnnotation[] value(); } @Repeatable(value = MyAnnotations.class) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "fengye"; }
本博客參閱原視頻教程Java8總結:
尚硅谷Java8新特性教程
代碼示例及筆記已匯總至Github專區,便于大家快速回顧及學習,歡迎fork查閱及提交.md檔案合并,一起完善筆記資料:
Java8NewProperty
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275663.html
標籤:Java
上一篇:SpringBoot整合阿里云OSS物件存盤實作檔案上傳
下一篇:day-10-函式進階-名稱空間
