Lambda 表達式
Lambda 是一個匿名函數(shù),我們可以把 Lambda 表達式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進行傳遞)。可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。
Lambda 表達式在Java 語言中引入了一個新的語法元素和操作符。這個操作符為 “->” , 該操作符被稱為 Lambda 操作符或箭頭操作符。它將 Lambda 分為兩個部分:
- 左側:指定了 Lambda 表達式需要的所有參數(shù)
- 右側:指定了 Lambda 體,即 Lambda 表達式要執(zhí)行的功能。
Lambda 表達式語法
語法格式一:無參,無返回值,Lambda 體只需一條語句
Runnable runnable = () -> System.out.println("hello Lambda");
語法格式二:Lambda 需要一個參數(shù)
Consumer<String> con = (t) -> System.out.println(t);
語法格式三:Lambda 只需要一個參數(shù)時,參數(shù)的小括號可以省略
Consumer<String> con = t -> System.out.println(t);
語法格式四:Lambda 需要兩個參數(shù),并且有返回值
Comparator<Integer> comparator = (x,y) -> {
? ? ? ? System.out.println("相加結果是:"+(x+y));
? ? ? ? return Integer.compare(x,y);
? ? };
語法格式五:當 Lambda 體只有一條語句時,return 與大括號可以省略
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
語法格式六:數(shù)據(jù)類型可以省略,因為可由編譯器推斷得出,稱為“類型推斷”
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
類型判斷
Lambda 表達式中無需指定類型,程序依然可以編譯,這是因為 javac 根據(jù)程序的上下文,在后臺推斷出了參數(shù)的類型。Lambda 表達式的類型依賴于上下文環(huán)境,是由編譯器推斷出來的。這就是所謂的“類型推斷”。
函數(shù)式接口
只包含一個抽象方法的接口,稱為函數(shù)式接口。
可以通過 Lambda 表達式來創(chuàng)建該接口的對象。(若 Lambda 表達式拋出一個受檢異常,那么該異常需要在目標接口的抽象方法上進行聲明)。
我們可以在任意函數(shù)式接口上使用 @FunctionalInterface 注解,這樣做可以檢查它是否是一個函數(shù)式接口,同時 javadoc 也會包含一條聲明,說明這個接口是一個函數(shù)式接口。
Java 內(nèi)置四大核心函數(shù)式接口
方法引用
當要傳遞給Lambda體的操作,已經(jīng)有實現(xiàn)的方法了,可以使用方法引用(實現(xiàn)抽象方法的參數(shù)列表,必須與方法引用方法的參數(shù)列表保持一致。)方法引用:使用操作符 “::” 將方法名和對象或類的名字分隔開來。
如下三種主要使用情況:
- 對象::實例方法
- 類::靜態(tài)方法
- 類::實例方法
使用注意事項:
* 1.Lambda 體中調(diào)用方法的參數(shù)列表與返回值類型,要與函數(shù)式接口中抽象方法的函數(shù)列表和返回值類型保持一致。
* 2.若Lambda 參數(shù)列表中第一個參數(shù)是實例方法調(diào)用者,第二個參數(shù)是實例方法的參數(shù) 可以使用 ClassName :: method
構造器引用
與函數(shù)式接口相結合,自動與函數(shù)式接口中方法兼容。可以把構造器引用賦值給定義的方法,與構造器參數(shù)列表要與接口中抽象方法的參數(shù)列表一致!
格式: ClassName::new
數(shù)組引用
格式: type[] :: new
? ? /**
? ? * 方法引用:若Lambda 體中的內(nèi)容有方法已經(jīng)實現(xiàn)了,可以使用"方法引用"
? ? */
? ? public class TestMethodRef {
? ? ? ? @Test
? ? ? ? public void test1(){
? ? ? ? ? ? Consumer<String> con = (x) -> System.out.println(x);
? ? ? ? ? ? con.accept("shuai");
? ? ? ? ? ? //方法引用,對象::實例方法名
? ? ? ? ? ? Consumer<String> consumer = System.out::println;
? ? ? ? ? ? consumer.accept("test");
? ? ? ? }
? ? ? ? @Test
? ? ? ? public void test2(){
? ? ? ? ? ? Person person = new Person();
? ? ? ? ? ? Supplier<String> supplier = () -> person.getName();
? ? ? ? ? ? String str = supplier.get();
? ? ? ? ? ? System.err.println(str);
? ? ? ? ? ? //方法引用,對象::實例方法名
? ? ? ? ? ? Supplier<Integer> sup = person::getAge;
? ? ? ? ? ? Integer age = sup.get();
? ? ? ? ? ? System.out.println(age);
? ? ? ? }
? ? ? ? //類::靜態(tài)方法名
? ? ? ? @Test
? ? ? ? public void test3(){
? ? ? ? ? ? Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
? ? ? ? ? ? //使用前提,compare的參數(shù)和返回值與Comparator一致
? ? ? ? ? ? Comparator<Integer> comparator = Integer :: compare;
? ? ? ? }
? ? ? ? //類::實例方法名
? ? ? ? @Test
? ? ? ? public void test4(){
? ? ? ? ? ? BiPredicate<String, String> bp = (x,y) -> x.equals(y);
? ? ? ? ? ? //使用條件:第一個參數(shù)是實例方法調(diào)用者,第二個參數(shù)是實例方法的參數(shù)
? ? ? ? ? ? BiPredicate<String, String> biPredicate = String :: equals;
? ? ? ? }
? ? ? ? //構造器引用
? ? ? ? @Test
? ? ? ? public void test5(){
? ? ? ? ? ? Supplier<Person> sup = () -> new Person();
? ? ? ? ? ? //構造器引用方式
? ? ? ? ? ? Supplier<Person> supplier = Person :: new;
? ? ? ? ? ? Person person = supplier.get();
? ? ? ? ? ? System.out.println(person);
? ? ? ? }
? ? ? ? //構造器引用
? ? ? ? @Test
? ? ? ? public void test6(){
? ? ? ? ? ? Function<Integer, Person> fun = (x) -> new Person(x);
? ? ? ? ? ? Function<Integer, Person> function = Person :: new;
? ? ? ? ? ? Person person = function.apply(2);
? ? ? ? ? ? System.out.println(person);
? ? ? ? ? ? System.out.println("--------------------");
? ? ? ? ? ? BiFunction<String, Integer, Person> biFunction = Person :: new;
? ? ? ? ? ? Person person2 = biFunction.apply("張三", 23);
? ? ? ? ? ? System.out.println(person2);
? ? ? ? }
? ? ? ? //數(shù)組引用
? ? ? ? @Test
? ? ? ? public void test7(){
? ? ? ? ? ? Function<Integer, String[]> fun = (x) -> new String[x];
? ? ? ? ? ? String[] strs = fun.apply(8);
? ? ? ? ? ? System.out.println(strs.length);
? ? ? ? ? ? Function<Integer, String[]> function = String[] :: new;
? ? ? ? ? ? String[] strArray = function.apply(6);
? ? ? ? ? ? System.out.println(strArray.length);
? ? ? ? }
? ? }
Stream API
Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定 希望對集合進行的操作,可以執(zhí)行非常復雜的查找、過濾和映射數(shù)據(jù)等操作。使用Stream API 對集合數(shù)據(jù)進行操作,就類似于使用 SQL 執(zhí)行的數(shù)據(jù)庫查詢。也可以使用 Stream API 來并行執(zhí)行操作。簡而言之,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式。
Stream
是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。
“集合講的是數(shù)據(jù),流講的是計算!”
1. Stream 自己不會存儲元素。
2. Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
3. Stream 操作是延遲執(zhí)行的。這意味著他們會等到需要結果的時候才執(zhí)行。
Stream 的操作三個步驟
創(chuàng)建 Stream
一個數(shù)據(jù)源(如:集合、數(shù)組),獲取一個流
中間操作
一個中間操作鏈,對數(shù)據(jù)源的數(shù)據(jù)進行處理
終止操作(終端操作)
一個終止操作,執(zhí)行中間操作鏈,并產(chǎn)生結果
創(chuàng)建Stream?
Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法:
- default Stream stream() : 返回一個順序流
- default Stream parallelStream() : 返回一個并行流
Java8 中的 Arrays 的靜態(tài)方法 stream() 可以獲取數(shù)組流:
- static Stream stream(T[] array): 返回一個流
可以使用靜態(tài)方法 Stream.of(), 通過顯示值創(chuàng)建一個流。它可以接收任意數(shù)量的參數(shù)。
- public static Stream of(T… values) : 返回一個流
可以使用靜態(tài)方法 Stream.iterate() 和Stream.generate(), 創(chuàng)建無限流。
- 迭代
public static Stream iterate(final T seed, final UnaryOperator f)
- 生成
public static Stream generate(Supplier s) :
? ? //創(chuàng)建Stream
? ? @Test
? ? public void test1(){
? ? ? ? //1.可以通過Collection系列集合提供的stream() 或parallelStream()
? ? ? ? List<String> list = new ArrayList<>();
? ? ? ? Stream<String> stream = list.stream();
? ? ? ? //2.通過Arrays中靜態(tài)方法 stream() 獲取數(shù)組流
? ? ? ? Person[] persons = new Person[10];
? ? ? ? Stream<Person> stream2 = Arrays.stream(persons);
? ? ? ? //3.通過Stream類中的靜態(tài)方法 of()
? ? ? ? Stream<String> stream3 = Stream.of("a","b","c");
? ? ? ? //4.創(chuàng)建無限流
? ? ? ? //迭代
? ? ? ? Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
? ? ? ? stream4.limit(8).forEach(System.out :: println);
? ? ? ? //生成
? ? ? ? Stream.generate(() -> Math.random()).limit(6)
? ? ? ? ? ? .forEach(System.out :: println);
? ? }? ? ?
Stream 的中間操作
多個中間操作可以連接起來形成一個流水線,除非流水線上觸發(fā)終止操作,否則中間操作不會執(zhí)行任何的處理!而在終止操作時一次性全部處理,稱為“惰性求值”。
? ? /**
? ? * Stream API的中間操作
? ? */
? ? public class TestSteamAPI2 {
? ? ? ? List<Person> persons = Arrays.asList(
? ? ? ? ? ? ? ? new Person(2, "錢四", 24),
? ? ? ? ? ? ? ? new Person(1, "張三", 33),
? ? ? ? ? ? ? ? new Person(2, "李四", 24),
? ? ? ? ? ? ? ? new Person(3, "王五", 65),
? ? ? ? ? ? ? ? new Person(4, "趙六", 26),
? ? ? ? ? ? ? ? new Person(5, "陳七", 27)
? ? ? ? );
? ? ? ? //內(nèi)部迭代,由Stream API完成
? ? ? ? @Test
? ? ? ? public void test1(){
? ? ? ? ? ? //中間操作,不會執(zhí)行任何操作
? ? ? ? ? ? Stream<Person> stream = persons.stream()
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .filter((e) -> {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("Stream的中間操作");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return e.getAge() > 25;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? });
? ? ? ? ? ? //終止操作,一次性執(zhí)行全部內(nèi)容,即"惰性求值"
? ? ? ? ? ? stream.forEach(System.out :: println);
? ? ? ? }
? ? ? ? //外部迭代
? ? ? ? @Test
? ? ? ? public void test2(){
? ? ? ? ? ? Iterator<Person> iterator = persons.iterator();
? ? ? ? ? ? while (iterator.hasNext()) {
? ? ? ? ? ? ? ? System.out.println(iterator.next());
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //limit,截斷
? ? ? ? @Test
? ? ? ? public void test3(){
? ? ? ? ? ? persons.stream()
? ? ? ? ? ? ? ? .filter((e) -> {
? ? ? ? ? ? ? ? ? ? System.out.println("迭代操作"); //短路
? ? ? ? ? ? ? ? ? ? return e.getAge() > 24;
? ? ? ? ? ? ? ? })
? ? ? ? ? ? ? ? .limit(2)
? ? ? ? ? ? ? ? .forEach(System.out :: println);
? ? ? ? }
? ? ? ? //跳過skip,distinct去重(要重寫equals和hashcode)
? ? ? ? @Test
? ? ? ? public void test4(){
? ? ? ? ? ? persons.stream()
? ? ? ? ? ? ? ? ? ? .filter((e) -> e.getAge() > 23)
? ? ? ? ? ? ? ? ? ? .skip(2)
? ? ? ? ? ? ? ? ? ? .distinct()
? ? ? ? ? ? ? ? ? ? .forEach(System.out :: println);
? ? ? ? }
? ? ? ? //映射
? ? ? ? @Test
? ? ? ? public void test5(){
? ? ? ? ? ? List<String> list = Arrays.asList("a","bb","c","d","e");
? ? ? ? ? ? list.stream().map((str) -> str.toUpperCase())
? ? ? ? ? ? ? ? .forEach(System.out :: println);
? ? ? ? ? ? System.out.println("---------------");
? ? ? ? ? ? persons.stream().map((Person :: getName)).forEach(System.out :: println);
? ? ? ? ? ? System.out.println("---------------");
? ? ? ? ? ? Stream<Stream<Character>> stream = list.stream()
? ? ? ? ? ? ? ? .map(TestSteamAPI2 :: filterCharacter);
? ? ? ? ? ? stream.forEach((s) -> {
? ? ? ? ? ? ? ? s.forEach(System.out :: println);
? ? ? ? ? ? });
? ? ? ? ? ? System.out.println("-----------------");
? ? ? ? ? ? //flatMap
? ? ? ? ? ? Stream<Character> stream2 = list.stream()
? ? ? ? ? ? ? ? .flatMap(TestSteamAPI2 :: filterCharacter);
? ? ? ? ? ? stream2.forEach(System.out :: println);
? ? ? ? }
? ? ? ? //處理字符串
? ? ? ? public static Stream<Character> filterCharacter(String str){
? ? ? ? ? ? List<Character> list = new ArrayList<>();
? ? ? ? ? ? for (Character character : str.toCharArray()) {
? ? ? ? ? ? ? ? list.add(character);
? ? ? ? ? ? }
? ? ? ? ? ? return list.stream();
? ? ? ? }
? ? ? ? //排序
? ? ? ? @Test
? ? ? ? public void test6(){
? ? ? ? ? ? List<String> list = Arrays.asList("bb","c","aa","ee","ddd");
? ? ? ? ? ? list.stream()
? ? ? ? ? ? ? ? .sorted() //自然排序
? ? ? ? ? ? ? ? .forEach(System.out :: println);
? ? ? ? ? ? System.out.println("------------");
? ? ? ? ? ? persons.stream()
? ? ? ? ? ? ? ? ? ? .sorted((p1,p2) -> {
? ? ? ? ? ? ? ? ? ? ? ? if (p1.getAge() == p2.getAge()) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? return p1.getName().compareTo(p2.getName());
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? return p1.getAge() - p2.getAge();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }).forEach(System.out :: println);
? ? ? ? }
? ? }
接口中的默認方法與靜態(tài)方法
接口中的默認方法
Java 8中允許接口中包含具有具體實現(xiàn)的方法,該方法稱為“默認方法”,默認方法使用 default 關鍵字修飾。
接口默認方法的”類優(yōu)先”原則
若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時
- 選擇父類中的方法。如果一個父類提供了具體的實現(xiàn),那么接口中具有相同名稱和參數(shù)的默認方法會被忽略。
- 接口沖突。如果一個父接口提供一個默認方法,而另一個接口也提供了一個具有相同名稱和參數(shù)列表的方法(不管方法是否是默認方法),那么必須覆蓋該方法來解決沖突。
接口中的靜態(tài)方法
Java8 中,接口中允許添加靜態(tài)方法。
? ? public interface MyInterface {
? ? ? ? default String getName(){
? ? ? ? ? ? return "接口測試";
? ? ? ? }
? ? ? ? public static void show(){
? ? ? ? ? ? System.out.println("接口中的靜態(tài)方法");
? ? ? ? }
? ? }
新特性:
? ? public static void main(String[] args) throws InterruptedException, ExecutionException {
? ? ? ? DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
? ? ? ? Callable<LocalDate> callable = new Callable<LocalDate>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public LocalDate call() throws Exception {
? ? ? ? ? ? ? ? return LocalDate.parse("20170521",dtf);
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? ExecutorService pool = Executors.newFixedThreadPool(10);
? ? ? ? List<Future<LocalDate>> results = new ArrayList<>();
? ? ? ? for (int i = 0; i < 8; i++) {
? ? ? ? ? ? results.add(pool.submit(callable));
? ? ? ? }
? ? ? ? for (Future<LocalDate> future : results) {
? ? ? ? ? ? System.out.println(future.get());
? ? ? ? }
? ? ? ? //關閉資源
? ? ? ? pool.shutdown();
}
LocalDate LocalTime LocalDateTime
? ? //1.LocalDate LocalTime LocalDateTime
? ? @Test
? ? public void test1(){
? ? ? ? LocalDateTime ldt = LocalDateTime.now();
? ? ? ? System.out.println(ldt);
? ? ? ? LocalDateTime ldt2 = LocalDateTime.of(2017, 05, 21, 21, 43, 55, 33);
? ? ? ? System.out.println(ldt2);
? ? ? ? LocalDateTime ldt3 = ldt.plusYears(3);
? ? ? ? System.out.println(ldt3);
? ? ? ? LocalDateTime ldt4 = ldt.minusMonths(5);
? ? ? ? System.out.println(ldt4);
? ? }
java.time.format.DateTimeFormatter 類
該類提供了三種格式化方法:
- 預定義的標準格式
- 語言環(huán)境相關的格式
- 自定義的格式
? ? //DateTimeFormatter:格式化時間/日期
? ? @Test
? ? public void test6(){
? ? ? ? DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
? ? ? ? LocalDateTime ldt = LocalDateTime.now();
? ? ? ? System.out.println(ldt);
? ? ? ? String format = ldt.format(dtf);
? ? ? ? System.out.println(format);
? ? ? ? System.out.println("------------");
? ? ? ? DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
? ? ? ? String format2 = dtf2.format(ldt);
? ? ? ? System.out.println(format2);
? ? ? ? LocalDateTime ldt2 = ldt.parse(format2,dtf2);
? ? ? ? System.out.println(ldt2);
? ? }
HashMap:減少碰撞,位置相同時,條件達到鏈表上超過8個,總數(shù)超過64個時,數(shù)據(jù)結構改為紅黑樹。
ConcurrentHashMap:取消鎖分段,與HashMap相同,達到條件時,數(shù)據(jù)結構改為紅黑樹。
Optional 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在,原來用 null 表示一個值不存在,現(xiàn)在 Optional 可以更好的表達這個概念。并且可以避免空指針異常。
常用方法:
- Optional.of(T t) : 創(chuàng)建一個 Optional 實例
- Optional.empty() : 創(chuàng)建一個空的 Optional 實例
- Optional.ofNullable(T t):若 t 不為 null,創(chuàng)建 Optional 實例,否則創(chuàng)建空實例
- isPresent() : 判斷是否包含值
- orElse(T t) : 如果調(diào)用對象包含值,返回該值,否則返回t
- orElseGet(Supplier s) :如果調(diào)用對象包含值,返回該值,否則返回 s 獲取的值
- map(Function f): 如果有值對其處理,并返回處理后的Optional,否則返回 Optional.empty()
- flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
? ? /**
? ? * Optional類
? ? */
? ? public class TestOptional {
? ? ? ? @Test
? ? ? ? public void test1(){
? ? ? ? ? ? //參數(shù)不能為空
? ? ? ? ? ? Optional<Person> op = Optional.of(new Person());
? ? ? ? ? ? Person person = op.get();
? ? ? ? ? ? System.out.println(person);
? ? ? ? }
? ? ? ? @Test
? ? ? ? public void test2(){
? ? ? ? ? ? //構建空optional
? ? ? ? ? ? Optional<Person> op = Optional.empty();
? ? ? ? ? ? System.out.println(op.get());
? ? ? ? }
? ? ? ? @Test
? ? ? ? public void test3(){
? ? ? ? ? ? //如果為null,調(diào)用empty,如果不為null,調(diào)用of
? ? ? ? ? ? Optional<Person> op = Optional.ofNullable(null);
? ? //? ? ? Optional<Person> op = Optional.ofNullable(new Person());
? ? ? ? ? ? if (op.isPresent()) {
? ? ? ? ? ? ? ? System.out.println(op.get());
? ? ? ? ? ? }
? ? ? ? ? ? //有值就用值,沒值就替代
? ? ? ? ? ? Person person = op.orElse(new Person("張三", 23));
? ? ? ? ? ? System.out.println(person);
? ? ? ? }
? ? }
Java 8對注解處理提供了兩點改進:可重復的注解及可用于類型的注解。
? ? @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
? ? @Retention(RetentionPolicy.RUNTIME)
? ? public @interface MyAnnotations {
? ? ? ? MyAnnotation[] value();
? ? }
? ? @Repeatable(MyAnnotations.class)
? ? @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ElementType.TYPE_PARAMETER})
? ? @Retention(RetentionPolicy.RUNTIME)
? ? public @interface MyAnnotation {
? ? ? ? String value() default "aric";
? ? }
? ? /**
? ? * 重復注解與類型注解
? ? */
? ? public class TestAnnotation {
? ? ? ? @MyAnnotation("hello")
? ? ? ? @MyAnnotation("test")
? ? ? ? public void show(@MyAnnotation("a") String str){
? ? ? ? ? ? System.out.println(str);
? ? ? ? }
? ? }