快端午小長假了,要上線的項目差不多完結了,終於有時間可以坐下來寫篇博客了。
這是篇對我看到的java 8新特性的一些總結,也是自己學習過程的總結。
幾乎可以說java 8是目前為止,自2004年java 5發布以來的java世界中最大的事件了。它帶來了java語言層面上的諸多改變,主要包括下面一些方面:語法、編譯器、庫、工具和運行時。
一,語法層面:
1,Lambda表達式。
lambda表達式是一種可調用對象,它允許我們將函數作為函數參數傳入。諸如C++、Groovy、Scala都已經支持lambda表達式。lambda表達式的設計已經花費了許多時間和社區努力,它已經有了比較簡潔和緊湊的結構。它最簡單的形式如下:
1 Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
在這段代碼里面,它只包含了變量e和函數體以及箭頭運算符。e是ArrayList里面的String類型參數,被編譯器調用,通過forEach循環來獲取,並通過—>來調用函數體,將變量e打印出來。該段代碼的lambda完整格式如下:
1 Arrays.asList( "a", "b", "d" ).forEach( e -> { 2 System.out.print( e ); 3 System.out.print( e ); 4 } );
同時,lambda表達式也可以引用其它變量,但是該變量應該顯式地聲明為final,如下:
1 final String separator = ","; 2 Arrays.asList( "a", "b", "d" ).forEach( 3 ( String e ) -> System.out.print( e + separator ) );
還有一個lambda表達式的例子,是list進行排序的,也借以表達lambda表達式可以有返回值的特性:
1 Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> { 2 int result = e1.compareTo( e2 ); 3 return result; 4 } );
其實要是只是對list進行排序的話,上段代碼可以簡寫成這樣:
1 Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
java的設計原本就參考了C++的諸多特性,lambda表達式也是源於對C++的模仿,但java 8中的lambda相對於C++中的而言,已經簡化了很多,完整的C++的lambda表達式包括捕獲列表、參數列表、箭頭表達式、函數體以及尾置返回類型。有一個例子如下:
1 int size_t=100; 2 [size_t](const string src1, const string src1)—>{src1.size()<src2.size()};
2,Java的設計者們想了大量辦法以使業已存在的功能支持lambda表達式,功能函數的概念便是其中之一。
函數接口(Functional Interface)是只含有一個方法的接口,我們經常使用的Runnable和Callable接口(這兩個接口在多線程的環境下經常使用,兩者最明顯的區別就是Runnable無返回值,我們無法獲取該線程的運算狀態,而Callable則具有返回值,可以返回線程的運算結果。)便是最具代表性的例子。但是這兩個接口是非常脆弱的,因為當開發人員往接口的定義里面再次添加方法聲明時,已有實現該方法的類將不能夠再運行。通過使用java注解@FunctionalInterface將使得接口具備這種能力,代碼示例如下:
1 @FunctionalInterface 2 public interface Functional { 3 void method(); 4 }
在java 8中已經使接口Runnable和Callable接口添加了該注解。與此同時,java 8中提供的default和static方法不會對@FunctionalInterface接口產生影響。
3,下面了解一下java中的默認和靜態方法。
學習語言最有效的方法之一就是學習優秀的代碼實例,下面的就是優秀實例的其中之一:
1 public interface DefaulableFactory { 2 // Interfaces now allow static methods 3 static Defaulable create( Supplier< Defaulable > supplier ) { 4 return supplier.get(); 5 } 6 7 private interface Defaulable { 8 // Interfaces now allow default methods, the implementer may or 9 // may not implement (override) them. 10 default String notRequired() { 11 return "Default implementation"; 12 } 13 } 14 15 private static class DefaultableImpl implements Defaulable { 16 } 17 18 private static class OverridableImpl implements Defaulable { 19 @Override 20 public String notRequired() { 21 return "Overridden implementation"; 22 } 23 } 24 25 public static void main( String[] args ) { 26 Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); 27 System.out.println( defaulable.notRequired() ); 28 29 defaulable = DefaulableFactory.create( OverridableImpl::new ); 30 System.out.println( defaulable.notRequired() ); 31 } 32 33 }
默認方法在函數定義的返回類型前面添加關鍵字default,同時添加了實現了功能的函數體。接口中的default方法使得實現了該接口的類可以不必實現該方法,當然也可以對其進行重寫。
添加了默認方法的接口具有了java 5中抽象類所能夠提供的功能,使得所有使用抽象類的地方都可以用擁有了default方法的接口取代,因此,不免讓人遐想:抽象類在此之后還有存在的意義嗎?!
同時,也要注意一下靜態方法的參數及其使用。尤其是Supplier<Defaultable> supplier和DefaultableImp::new,這兩種表達方式對java開發人員而言,還是挺新穎的。
4,可重復注解。
在java 8以前,相同類型的注解只能聲明一次,例如在java開發的過程中,對某個方法重寫,只能聲明一次@override;在android構台過程中,對某個方法聲明目標api,只有聲明一次@targetapi。但是在java 8中,相同類型的注解卻可以聲明多次,示例代碼如下:
1 public class RepeatingAnnotations { 2 @Target( ElementType.TYPE ) 3 @Retention( RetentionPolicy.RUNTIME ) 4 public @interface Filters { 5 Filter[] value(); 6 } 7 8 @Target( ElementType.TYPE ) 9 @Retention( RetentionPolicy.RUNTIME ) 10 @Repeatable( Filters.class ) 11 public @interface Filter { 12 String value(); 13 }; 14 15 @Filter( "filter1" ) 16 @Filter( "filter2" ) 17 public interface Filterable { 18 } 19 20 public static void main(String[] args) { 21 for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) { 22 System.out.println( filter.value() ); 23 } 24 } 25 }
在main方法中,Fileterable接口通過反射機制來獲取自身定義的注解,然后打印出來。
二,編譯器新特性
1,參數名
長久以來java開發人員都在想盡各種辦法以達到在運行時獲取參數名字的目的,如今,java 8將此功能引入了java世界。
如下:
1 public class ParameterNames { 2 public static void main(String[] args) throws Exception { 3 Method method = ParameterNames.class.getMethod( "main", String[].class ); 4 for( final Parameter parameter: method.getParameters() ) { 5 System.out.println( "Parameter: " + parameter.getName() ); 6 } 7 } 8 }
通過反射機制獲取Parameter類,然后調用新增的方法getName來獲取名字。
三,庫新特性
1,Optional
NullPointerException(NPE)是引起應用崩潰的最著名的原因,這個估計每個開發人員都曾遇到過無數次。Google Guova項目組曾將Optional作為NPE的解決方案,受它的啟發,java 8將Optional引入java庫進來。
Optional僅僅是一個泛型容器:它保存一個T值或者是null,而且提供了大量的方法來檢查null值。它的使用方式,正好有個例子:
1 Optional< String > fullName = Optional.ofNullable( null );//Optional.of( "Tom" ); 2 System.out.println( "Full Name is set? " + fullName.isPresent() ); 3 System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 4 System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
第二行isPresent()表示Optional中保存的值是否為null;第三行orElseGet()表示如果為空將進行怎樣的操作;第四行map()和orElse()表示如果Optional中的值不為空,則進行map里面的操作,否則的話執行orElse里面的操作。
2,日期時間API
java中的日期和時間API一直為開發人員所詬病,緊隨Calendar之后的Date也沒有改變這種狀況。而開源java包Joda-Time卻為這種狀況進行了極大的改善,java -8中的日期與時間API則受到了Joda-Time極大的啟發,並充分把它吸納了進來。
java 8中的日期與時間API使用非常簡單,一段代碼就可以把它闡明,有一段代碼如下:
1 // Get the system clock as UTC offset 2 final Clock clock = Clock.systemUTC(); 3 System.out.println( clock.instant() ); 4 System.out.println( clock.millis() ); 5 6 // Get the local date and local time 7 final LocalDate date = LocalDate.now(); 8 final LocalDate dateFromClock = LocalDate.now( clock ); 9 10 System.out.println( date ); 11 System.out.println( dateFromClock ); 12 13 // Get the local date and local time 14 final LocalTime time = LocalTime.now(); 15 final LocalTime timeFromClock = LocalTime.now( clock ); 16 17 System.out.println( time ); 18 System.out.println( timeFromClock ); 19 20 // Get the local date/time 21 final LocalDateTime datetime = LocalDateTime.now(); 22 final LocalDateTime datetimeFromClock = LocalDateTime.now( clock ); 23 24 System.out.println( datetime ); 25 System.out.println( datetimeFromClock ); 26 27 // Get the zoned date/time 28 final ZonedDateTime zonedDatetime = ZonedDateTime.now(); 29 final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock ); 30 final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) ); 31 32 System.out.println( zonedDatetime ); 33 System.out.println( zonedDatetimeFromClock ); 34 System.out.println( zonedDatetimeFromZone ); 35 36 // Get duration between two dates 37 final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 ); 38 final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 ); 39 40 final Duration duration = Duration.between( from, to ); 41 System.out.println( "Duration in days: " + duration.toDays() ); 42 System.out.println( "Duration in hours: " + duration.toHours() );
3,Base64
終於,java把Base64加密/解密算法引入到最新版本中。使用如下:
1 public class Base64s { 2 public static void main(String[] args) { 3 final String text = "Base64 finally in Java 8!"; 4 5 final String encoded = Base64 6 .getEncoder() 7 .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) ); 8 System.out.println( encoded ); 9 10 final String decoded = new String( 11 Base64.getDecoder().decode( encoded ), 12 StandardCharsets.UTF_8 ); 13 System.out.println( decoded ); 14 } 15 }
與此同時,java 8中的Base64也提供了對URL和MIME的加密/解密支持。(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())
4,並行數組
java 8提供了許多新的方法來支持對數組的並行處理,尤其是並行排序(parralelSort()),這種並行機制將充分利用處理品的多核機制,是對當前處理器發展趨勢的順應和完全把握。有個小例子如下:
1 public class ParallelArrays { 2 public static void main( String[] args ) { 3 long[] arrayOfLong = new long [ 20000 ]; 4 5 Arrays.parallelSetAll( arrayOfLong, 6 index -> ThreadLocalRandom.current().nextInt( 1000000 ) ); 7 Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 8 i -> System.out.print( i + " " ) ); 9 System.out.println(); 10 11 Arrays.parallelSort( arrayOfLong ); 12 Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 13 i -> System.out.print( i + " " ) ); 14 System.out.println(); 15 } 16 }
5,並發
並發常用的ConcurrentHashMap在java 8中改進了hash算法,降低了沖突的概率,當然,只是針對當鍵的類型為String的時候,其它類型鍵類型的沖突概率沒有變化。
同時變化的並發用的類還有:ForkJoinPool,StampedLock,ReadWriteLock。新增的類有:DoubleAccumulator,DoubleAdder,LongAccumulator和LongAdder。
四,工具
java 8中新增加了兩個工具,一個是js引擎工具(jjs),一個是類依存分析器(jdeps)。
例如一個js文件func.js,內容為function f(){return 1;}; print(f()+1);則在命令行中輸入jjs func.js,就會輸出2。
而對於jdeps工具,當輸入一個 .class,文件夾或者.jar文件時,就會輸出輸入內容的類之間的依賴關系。例如輸入:jdeps or.springframework.core-3.0.5.RELEASE.jar時,就會輸出如下內容:
1 org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar 2 org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar) 3 -> java.io 4 -> java.lang 5 -> java.lang.annotation 6 -> java.lang.ref 7 -> java.lang.reflect 8 -> java.util 9 -> java.util.concurrent 10 -> org.apache.commons.logging not found 11 -> org.springframework.asm not found 12 -> org.springframework.asm.commons not found 13 org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar) 14 -> java.lang 15 -> java.lang.annotation 16 -> java.lang.reflect 17 -> java.util
五,運行時(JVM)
在java 8中PermGen將不再存在,相反,Metaspace將會取代其位置。
JVM中-XX:PermSize和-XX:MaxPermSize將會被-XX:MetaSpaceSize和-XX:MaxMetaSpace取代。
總之,java 8的發布是java進化史上的一個重要的里程碑,java吸收了大量其它語言(尤其是c++)的優勢,java 8也是今年java界最為令人激動的一件事。
本文參考許多其它的文章,地址附錄如下,有興趣的同行可以看一下:
http://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html ;
http://www.javacodegeeks.com/2014/04/abstract-class-versus-interface-in-the-jdk-8-era.html ;
http://openjdk.java.net/jeps/180 ;
