版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
JDK1.8十大新特性詳解一、接口的默認方法Java8允許我們給接口添加一個非抽象的方法實現,只需要使用default關鍵字即可,這個特征又叫做擴展方法,示例如下:interfaceFormula{doublecalculate(inta);defaultdoublesqrt(inta){returnMath.sqrt(a);}}Formula接口在擁有calculate方法之外同時還定義了sqrt方法,實現了Formula接口的子類只需要實現一個calculate方法,默認方法sqrt將在子類上可以直接使用。代碼如下:Formulaformula=newFormula(){@Overridepublicdoublecalculate(inta){returnsqrt(a*100);}};formula.calculate(100);//100.0formula.sqrt(16);//4.0文中的formula被實現為一個匿名類的實例,該代碼非常容易理解,6行代碼實現了計算sqrt(a*100)。在下一節(jié)中,我們將會看到實現單方法接口的更簡單的做法。:在Java中只有單繼承,如果要讓一個類賦予新的特性,通常是使用接口來實現,在C++中支持多繼承,允許一個子類同時具有多個父類的接口與功能,在其他語言中,讓一個類同時具有其他的可復用代碼的方法叫做mixin。新的Java8的這個特新在編譯器實現的角度上來說更加接近Scala的trait。在C#中也有名為擴展方法的概念,允許給已存在的類型擴展方法,和Java8的這個在語義上有差別。二、Lambda表達式首先看看在老版本的Java中是如何排列字符串的:代碼如下:List<String>names=Arrays.asList("peter","anna","mike","xenia");Collections.sort(names,newComparator<String>(){@Overridepublicintcompare(Stringa,Stringb){returnpareTo(a);}});只需要給靜態(tài)方法Collections.sort傳入一個List對象以及一個比較器來按指定順序排列。通常做法都是創(chuàng)建一個匿名的比較器對象然后將其傳遞給sort方法。在Java8中你就沒必要使用這種傳統(tǒng)的匿名對象的方式了,Java8提供了更簡潔的語法,lambda表達式:代碼如下:Collections.sort(names,(Stringa,Stringb)->{returnpareTo(a);});看到了吧,代碼變得更短且更具有可讀性,但是實際上還可以寫得更短:代碼如下:Collections.sort(names,(Stringa,Stringb)->pareTo(a));對于函數體只有一行代碼的,你可以去掉大括號{}以及return關鍵字,但是你還可以寫得更短點:代碼如下:Collections.sort(names,(a,b)->pareTo(a));Java編譯器可以自動推導出參數類型,所以你可以不用再寫一次類型。接下來我們看看lambda表達式還能作出什么更方便的東西來:三、函數式接口Lambda表達式是如何在java的類型系統(tǒng)中表示的呢?每一個lambda表達式都對應一個類型,通常是接口類型。而“函數式接口”是指僅僅只包含一個抽象方法的接口,每一個該類型的lambda表達式都會被匹配到這個抽象方法。因為默認方法不算抽象方法,所以你也可以給你的函數式接口添加默認方法。我們可以將lambda表達式當作任意只包含一個抽象方法的接口類型,確保你的接口一定達到這個要求,你只需要給你的接口添加@FunctionalInterface注解,編譯器如果發(fā)現你標注了這個注解的接口有多于一個抽象方法的時候會報錯的。示例如下:代碼如下:@FunctionalInterfaceinterfaceConverter<F,T>{Tconvert(Ffrom);}Converter<String,Integer>converter=(from)->Integer.valueOf(from);Integerconverted=converter.convert("123");System.out.println(converted);//123需要注意如果@FunctionalInterface如果沒有指定,上面的代碼也是對的。將lambda表達式映射到一個單方法的接口上,這種做法在Java8之前就有別的語言實現,比如RhinoJavaScript解釋器,如果一個函數參數接收一個單方法的接口而你傳遞的是一個function,Rhino解釋器會自動做一個單接口的實例到function的適配器,典型的應用場景有org.w3c.dom.events.EventTarget的addEventListener第二個參數EventListener。四、方法與構造函數引用前一節(jié)中的代碼還可以通過靜態(tài)方法引用來表示:代碼如下:Converter<String,Integer>converter=Integer::valueOf;Integerconverted=converter.convert("123");System.out.println(converted);//123Java8允許你使用::關鍵字來傳遞方法或者構造函數引用,上面的代碼展示了如何引用一個靜態(tài)方法,我們也可以引用一個對象的方法:代碼如下:converter=something::startsWith;Stringconverted=converter.convert("Java");System.out.println(converted);//"J"接下來看看構造函數是如何使用::關鍵字來引用的,首先我們定義一個包含多個構造函數的簡單類:代碼如下:classPerson{StringfirstName;StringlastName;Person(){}Person(StringfirstName,StringlastName){this.firstName=firstName;this.lastName=lastName;}}接下來我們指定一個用來創(chuàng)建Person對象的對象工廠接口:代碼如下:interfacePersonFactory<PextendsPerson>{Pcreate(StringfirstName,StringlastName);}這里我們使用構造函數引用來將他們關聯起來,而不是實現一個完整的工廠:代碼如下:PersonFactory<Person>personFactory=Person::new;Personperson=personFactory.create("Peter","Parker");我們只需要使用Person::new來獲取Person類構造函數的引用,Java編譯器會自動根據PersonFactory.create方法的簽名來選擇合適的構造函數。五、Lambda作用域在lambda表達式中訪問外層作用域和老版本的匿名對象中的方式很相似。你可以直接訪問標記了final的外層局部變量,或者實例的字段以及靜態(tài)變量。訪問局部變量我們可以直接在lambda表達式中訪問外層的局部變量:代碼如下:finalintnum=1;Converter<Integer,String>stringConverter=(from)->String.valueOf(from+num);stringConverter.convert(2);//3但是和匿名對象不同的是,這里的變量num可以不用聲明為final,該代碼同樣正確:代碼如下:intnum=1;Converter<Integer,String>stringConverter=(from)->String.valueOf(from+num);stringConverter.convert(2);//3不過這里的num必須不可被后面的代碼修改(即隱性的具有final的語義),例如下面的就無法編譯:代碼如下:intnum=1;Converter<Integer,String>stringConverter=(from)->String.valueOf(from+num);num=3;在lambda表達式中試圖修改num同樣是不允許的。七、訪問對象字段與靜態(tài)變量和本地變量不同的是,lambda內部對于實例的字段以及靜態(tài)變量是即可讀又可寫。該行為和匿名對象是一致的:代碼如下:classLambda4{staticintouterStaticNum;intouterNum;voidtestScopes(){Converter<Integer,String>stringConverter1=(from)->{outerNum=23;returnString.valueOf(from);};Converter<Integer,String>stringConverter2=(from)->{outerStaticNum=72;returnString.valueOf(from);};}}八、訪問接口的默認方法還記得第一節(jié)中的formula例子么,接口Formula定義了一個默認方法sqrt可以直接被formula的實例包括匿名對象訪問到,但是在lambda表達式中這個是不行的。Lambda表達式中是無法訪問到默認方法的,以下代碼將無法編譯:代碼如下:Formulaformula=(a)->sqrt(a*100);Built-inFunctionalInterfacesJDK1.8API包含了很多內建的函數式接口,在老Java中常用到的比如Comparator或者Runnable接口,這些接口都增加了@FunctionalInterface注解以便能用在lambda上。Java8API同樣還提供了很多全新的函數式接口來讓工作更加方便,有一些接口是來自GoogleGuava庫里的,即便你對這些很熟悉了,還是有必要看看這些是如何擴展到lambda上使用的。Predicate接口Predicate接口只有一個參數,返回boolean類型。該接口包含多種默認方法來將Predicate組合成其他復雜的邏輯(比如:與,或,非):代碼如下:Predicate<String>predicate=(s)->s.length()>0;predicate.test("foo");//truepredicate.negate().test("foo");//falsePredicate<Boolean>nonNull=Objects::nonNull;Predicate<Boolean>isNull=Objects::isNull;Predicate<String>isEmpty=String::isEmpty;Predicate<String>isNotEmpty=isEmpty.negate();Function接口Function接口有一個參數并且返回一個結果,并附帶了一些可以和其他函數組合的默認方法(compose,andThen):代碼如下:Function<String,Integer>toInteger=Integer::valueOf;Function<String,String>backToString=toInteger.andThen(String::valueOf);backToString.apply("123");//"123"Supplier接口Supplier接口返回一個任意范型的值,和Function接口不同的是該接口沒有任何參數代碼如下:Supplier<Person>personSupplier=Person::new;personSupplier.get();//newPersonConsumer接口Consumer接口表示執(zhí)行在單個參數上的操作。代碼如下:Consumer<Person>greeter=(p)->System.out.println("Hello,"+p.firstName);greeter.accept(newPerson("Luke","Skywalker"));Comparator接口Comparator是老Java中的經典接口,Java8在此之上添加了多種默認方法:代碼如下:Comparator<Person>comparator=(p1,p2)->p1.firstNpareTo(p2.firstName);Personp1=newPerson("John","Doe");Personp2=newPerson("Alice","Wonderland");pare(p1,p2);//>0comparator.reversed().compare(p1,p2);//<0Optional接口Optional不是函數是接口,這是個用來防止NullPointerException異常的輔助類型,這是下一屆中將要用到的重要概念,現在先簡單的看看這個接口能干什么:Optional被定義為一個簡單的容器,其值可能是null或者不是null。在Java8之前一般某個函數應該返回非空對象但是偶爾卻可能返回了null,而在Java8中,不推薦你返回null而是返回Optional。代碼如下:Optional<String>optional=Optional.of("bam");optional.isPresent();//trueoptional.get();//"bam"optional.orElse("fallback");//"bam"optional.ifPresent((s)->System.out.println(s.charAt(0)));//"b"Stream接口java.util.Stream表示能應用在一組元素上一次執(zhí)行的操作序列。Stream操作分為中間操作或者最終操作兩種,最終操作返回一特定類型的計算結果,而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來。Stream的創(chuàng)建需要指定一個數據源,比如java.util.Collection的子類,List或者Set,Map不支持。Stream的操作可以串行執(zhí)行或者并行執(zhí)行。首先看看Stream是怎么用,首先創(chuàng)建實例代碼的用到的數據List:代碼如下:List<String>stringCollection=newArrayList<>();stringCollection.add("ddd2");stringCollection.add("aaa2");stringCollection.add("bbb1");stringCollection.add("aaa1");stringCollection.add("bbb3");stringCollection.add("ccc");stringCollection.add("bbb2");stringCollection.add("ddd1");Java8擴展了集合類,可以通過Collection.stream()或者Collection.parallelStream()來創(chuàng)建一個Stream。下面幾節(jié)將詳細解釋常用的Stream操作:Filter過濾過濾通過一個predicate接口來過濾并只保留符合條件的元素,該操作屬于中間操作,所以我們可以在過濾后的結果來應用其他Stream操作(比如forEach)。forEach需要一個函數來對過濾后的元素依次執(zhí)行。forEach是一個最終操作,所以我們不能在forEach之后來執(zhí)行其他Stream操作。代碼如下:stringCollection.stream().filter((s)->s.startsWith("a")).forEach(System.out::println);//"aaa2","aaa1"Sort排序排序是一個中間操作,返回的是排序好后的Stream。如果你不指定一個自定義的Comparator則會使用默認排序。代碼如下:stringCollection.stream().sorted().filter((s)->s.startsWith("a")).forEach(System.out::println);//"aaa1","aaa2"需要注意的是,排序只創(chuàng)建了一個排列好后的Stream,而不會影響原有的數據源,排序之后原數據stringCollection是不會被修改的:代碼如下:System.out.println(stringCollection);//ddd2,aaa2,bbb1,aaa1,bbb3,ccc,bbb2,ddd1Map映射中間操作map會將元素根據指定的Function接口來依次將元素轉成另外的對象,下面的示例展示了將字符串轉換為大寫字符串。你也可以通過map來講對象轉換成其他類型,map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。代碼如下:stringCollection.stream().map(String::toUpperCase).sorted((a,b)->pareTo(a)).forEach(System.out::println);//"DDD2","DDD1","CCC","BBB3","BBB2","AAA2","AAA1"Match匹配Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是最終操作,并返回一個boolean類型的值。代碼如下:booleananyStartsWithA=stringCollection.stream().anyMatch((s)->s.startsWith("a"));System.out.println(anyStartsWithA);//truebooleanallStartsWithA=stringCollection.stream().allMatch((s)->s.startsWith("a"));System.out.println(allStartsWithA);//falsebooleannoneStartsWithZ=stringCollection.stream().noneMatch((s)->s.startsWith("z"));System.out.println(noneStartsWithZ);//trueCount計數計數是一個最終操作,返回Stream中元素的個數,返回值類型是long。代碼如下:longstartsWithB=stringCollection.stream().filter((s)->s.startsWith("b")).count();System.out.println(startsWithB);//3Reduce規(guī)約這是一個最終操作,允許通過指定的函數來講stream中的多個元素規(guī)約為一個元素,規(guī)越后的結果是通過Optional接口表示的:代碼如下:Optional<String>reduced=stringCollection.stream().sorted().reduce((s1,s2)->s1+"#"+s2);reduced.ifPresent(System.out::println);//"aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"并行Streams前面提到過Stream有串行和并行兩種,串行Stream上的操作是在一個線程中依次完成,而并行Stream則是在多個線程上同時執(zhí)行。下面的例子展示了是如何通過并行Stream來提升性能:首先我們創(chuàng)建一個沒有重復元素的大表:代碼如下:intmax=1000000;List<String>values=newArrayList<>(max);for(inti=0;i<max;i++){UUIDuuid=UUID.randomUUID();values.add(uuid.toString());}然后我們計算一下排序這個Stream要耗時多久,串行排序:代碼如下:longt0=System.nanoTime();longcount=values.stream().sorted().count();System.out.println(count);longt1=System.nanoTime();longmillis=TimeUnit.NANOSECONDS.toMillis(t1-t0);System.out.println(String.format("sequentialsorttook:%dms",millis));//串行耗時:899ms并行排序:代碼如下:longt0=System.nanoTime();longcount=values.parallelStream().sorted().count();System.out.println(count);longt1=System.nanoTime();longmillis=TimeUnit.NANOSECONDS.toMillis(t1-t0);System.out.println(String.format("parallelsorttook:%dms",millis));//并行排序耗時:472ms上面兩個代碼幾乎是一樣的,但是并行版的快了50%之多,唯一需要做的改動就是將stream()改為parallelStream()。Map前面提到過,Map類型不支持stream,不過Map提供了一些新的有用的方法來處理一些日常任務。代碼如下:Map<Integer,String>map=newHashMap<>();for(inti=0;i<10;i++){map.putIfAbsent(i,"val"+i);}map.forEach((id,val)->System.out.println(val));以上代碼很容易理解,putIfAbsent不需要我們做額外的存在性檢查,而forEach則接收一個Consumer接口來對map里的每一個鍵值對進行操作。下面的例子展示了map上的其他有用的函數:代碼如下:puteIfPresent(3,(num,val)->val+num);map.get(3);//puteIfPresent(9,(num,val)->null);map.containsKey(9);//puteIfAbsent(23,num->"val"+num);map.containsKey(23);//puteIfAbsent(3,num->"bam");map.get(3);//val33接下來展示如何在Map里刪除一個鍵值全都匹配的項:代碼如下:map.remove(3,"val3");map.get(3);//val33map.remove(3,"val33");map.get(3);//null另外一個有用的方法:代碼如下:map.getOrDefault(42,"notfound");//notfound對Map的元素做合并也變得很容易了:代碼如下:map.merge(9,"val9",(value,newValue)->value.concat(newValue));map.get(9);//val9map.merge(9,"concat",(value,newValue)->value.concat(newValue));map.get(9);//val9concatMerge做的事情是如果鍵名不存在則插入,否則則對原鍵對應的值做合并操作并重新插入到map中。九、DateAPIJava8在包java.time下包含了一組全新的時間日期API。新的日期API和開源的Joda-Time庫差不多,但又不完全一樣,下面的例子展示了這組新API里最重要的一些部分:Clock時鐘Clock類提供了訪問當前日期和時間的方法,Clock是時區(qū)敏感的,可以用來取代System.currentTimeMillis()來獲取當前的微秒數。某一個特定的時間點也可以使用Instant類來表示,Instant類也可以用來創(chuàng)建老的java.util.Date對象。代碼如下:Clockclock=Clock.systemDefaultZone();longmillis=lis();Instantinstant=clock.instant();DatelegacyDate=Date.from(instant);//legacyjava.util.DateTimezones時區(qū)在新API中時區(qū)使用ZoneId來表示。時區(qū)可以很方便的使用靜態(tài)方法of來獲取到。時區(qū)定義了到UTS時間的時間差,在Instant時間點對象到本地日期對象之間轉換的時候是極其重要的。代碼如下:System.out.println(ZoneId.getAvailableZoneIds());//printsallavailabletimezoneidsZoneIdzone1=ZoneId.of("Europe/Berlin");ZoneIdzone2=ZoneId.of("Brazil/East");System.out.println(zone1.getRules());System.out.println(zone2.getRules());//ZoneRules[currentStandardOffset=+01:00]//ZoneRules[currentStandardOffset=-03:00]LocalTime本地時間LocalTime定義了一個沒有時區(qū)信息的時間,例如晚上10點,或者17:30:15。下面的例子使用前面代碼創(chuàng)建的時區(qū)創(chuàng)建了兩個本地時間。之后比較時間并以小時和分鐘為單位計算兩個時間的時間差:代碼如下:LocalTimenow1=LocalTime.now(zone1);LocalTimenow2=LocalTime.now(zone2);System.out.println(now1.isBefore(now2));//falselonghoursBetween=ChronoUnit.HOURS.between(now1,now2);longminutesBetween=ChronoUnit.MINUTES.between(now1,now2);System.out.println(hoursBetween);//-3System.out.println(minutesBetween);//-239LocalTime提供了多種工廠方法來簡化對象的創(chuàng)建,包括解析時間字符串。代碼如下:LocalTimelate=LocalTime.of(23,59,59);System.out.println(late);//23:59:59DateTimeFormattergermanFormatter=DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(Locale.GERMAN);LocalTimeleetTime=LocalTime.parse("13:37",germanFormatter);System.out.println(leetTime);//13:37LocalDate本地日期LocalDate表示了一個確切的日期,比如2014-03-11。該對象值是不可變的,用起來和LocalTime基本一致。下面的例子展示了如何給Date對象加減天/月/年。另外要注意的是這些對象是不可變的,操作返回的總是一個新實例。代碼如下:LocalDatetoday=LocalDate.now();LocalDatetomorrow=today.plus(1,ChronoUnit.DAYS);LocalDateyesterday=tomorrow.minusDays(2);LocalDateindependenceDay=LocalDate.of(2014,Month.JULY,4);DayOfWeekdayOfWeek=independenceDay.getDayOfWeek();System.out.println(dayOfWeek);//FRIDAY從字符串解析一個LocalDate類型和解析LocalTime一樣簡單:代碼如下:DateTimeFormattergermanFormatter=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.GERMAN);LocalDatexmas=LocalDate.parse("24.12.2014",germanFormatter);System.out.println(xmas);//2014-12-24LocalDateTime本地日期時間LocalDateTime同時表示了時間和日期,相當于前兩節(jié)內容合并到一個對象上了。LocalDateTime和LocalTime還有LocalDate一樣,都是不可變的。LocalDateTime提供了一些能訪問具體字段的方法。代碼如下:LocalDateTimesylvester=LocalDateTime.of(2014,Month.DECEMBER,31,23,59,59);DayOfWeekdayOfWeek=sylvester.getDayOfWeek();System.out.println(dayOfWeek);//WEDNESDAYMonthmonth=sylvester.getMonth();System.out.println(month);//DECEMBERlongminuteOfDay=sylvester.getLong(ChronoField.MINUTE_OF_DAY);System.out.println(minuteOfDay);//1439只要附加上時區(qū)信息,就可以將其轉換為一個時間點Instant對象,Instant時間點對象可以很容易的轉換為老式的java.util.Date。代碼如下:Instantinstant=s
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年初中美術真題匯編試卷
- 商標大賽試題及答案
- 脾胃常見試題及對應答案
- 膽囊穿孔考試題目及精準答案
- 食堂管理服務合同(2026版)
- 五年級下冊數學北師大含答案 總復習5數據的分析和表示
- 視敏度專項試題及答案呈現
- 新疆昌吉回族自治州2026屆九年級中考模擬物理試卷(有答案)
- 河科大機械制造基礎習題及答案05多選
- 資金管理練習題及答案信息整合
- 模塊概述《英語專業(yè)導論》教案(2025-2026學年)
- 英語培訓機構師資培訓教材
- 食品工廠衛(wèi)生管理操作標準流程
- 2024天津石油職業(yè)技術學院教師招聘考試真題及答案
- 2025年健康照護師初級考試模擬題及答案
- 巡防員考勤管理辦法
- 人員職業(yè)健康管理辦法
- 解除合同協議書條款
- 2024寵物營養(yǎng)師考試復習資料試題及答案
- 中級維保全部抽考題
- 廣東省工程勘察設計服務成本取費導則(2024版)
評論
0/150
提交評論