JAVA8 新特性和個人理解

官方文檔

1. Lambda表達式和函數(shù)式接口

Lambda表達式(也稱為閉包)是Java 8中最大和最令人期待的語言改變。它允許我們將函數(shù)當成參數(shù)傳遞給某個方法,或者把代碼本身當作數(shù)據(jù)處理:函數(shù)式開發(fā)者非常熟悉這些概念。很多JVM平臺上的語言(Groovy、Scala等)從誕生之日就支持Lambda表達式,但是Java開發(fā)者沒有選擇,只能使用匿名內(nèi)部類代替Lambda表達式。
Lambda的設(shè)計耗費了很多時間和很大的社區(qū)力量,最終找到一種折中的實現(xiàn)方案,可以實現(xiàn)簡潔而緊湊的語言結(jié)構(gòu)。
最簡單的Lambda表達式可由【逗號】分隔的參數(shù)列表、【 -> 】符號和【語句塊】組成。

例1:
Arrays.asList( "a", "b", "d" ).forEach( e -> {System.out.print(e); System.out.print("|");} );
例2:
 Lambda表達式有返回值,返回值的類型也由編譯器推理得出。如果Lambda表達式中的語句塊只有一行,則可以不用使用return語句,下列兩個代碼片段效果相同:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
和
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
````
Lambda的設(shè)計者們?yōu)榱俗尙F(xiàn)有的功能與Lambda表達式良好兼容,考慮了很多方法,于是產(chǎn)生了**[函數(shù)接口](http://www.javacodegeeks.com/2013/03/introduction-to-functional-interfaces-a-concept-recreated-in-java-8.html)**這個概念。函數(shù)接口指的是只有一個函數(shù)的接口,這樣的接口可以隱式轉(zhuǎn)換為Lambda表達式。**java.lang.Runnable**和**java.util.concurrent.Callable**是函數(shù)式接口的最佳例子。在實踐中,函數(shù)式接口非常脆弱:只要某個開發(fā)者在該接口中添加一個函數(shù),則該接口就不再是函數(shù)式接口進而導(dǎo)致編譯失敗。為了克服這種代碼層面的脆弱性,并顯式說明某個接口是函數(shù)式接口,Java 8 提供了一個特殊的注解**@FunctionalInterface**(Java 庫中的所有相關(guān)接口都已經(jīng)帶有這個注解了),舉個簡單的函數(shù)式接口的定義:
````
@FunctionalInterface
public interface Functional { 
      void method();
}
````
不過有一點需要注意,[默認方法和靜態(tài)方法](https://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html#Interface_Default)不會破壞函數(shù)式接口的定義,因此如下的代碼是合法的。
````
@FunctionalInterface
public interface FunctionalDefaultMethods { 
    void method(); 
    default void defaultMethod() { } 
}
````

#2. 接口的默認方法和靜態(tài)方法
Java 8使用兩個新概念擴展了接口的含義:默認方法和靜態(tài)方法。[默認方法](http://blog.csdn.net/yczz/article/details/50896975)使得接口有點類似traits,不過要實現(xiàn)的目標不一樣。默認方法使得開發(fā)者可以在 不破壞二進制兼容性的前提下,往現(xiàn)存接口中添加新的方法,即不強制那些實現(xiàn)了該接口的類也同時實現(xiàn)這個新加的方法。
默認方法和抽象方法之間的區(qū)別在于抽象方法需要實現(xiàn),而默認方法不需要。接口提供的默認方法會被接口的實現(xiàn)類繼承或者覆寫,例子代碼如下:
````
private interface Defaulable {
 // Interfaces now allow default methods, the implementer may or  
// may not implement (override) them. 
      default String notRequired() { 
      return "Default implementation";
    } 
}
private static class DefaultableImpl implements Defaulable {}
private static class OverridableImpl implements Defaulable { 
        @Override
        public String notRequired() { 
          return "Overridden implementation"; 
        }
}
````
**Defaulable**接口使用關(guān)鍵字**default**定義了一個默認方法**notRequired()**。**DefaultableImpl**類實現(xiàn)了這個接口,同時默認繼承了這個接口中的默認方法;**OverridableImpl**類也實現(xiàn)了這個接口,但覆寫了該接口的默認方法,并提供了一個不同的實現(xiàn)。
Java 8帶來的另一個有趣的特性是在接口中可以定義靜態(tài)方法,例子代碼如下:
````
private interface DefaulableFactory { 
   // Interfaces now allow static methods 
      static Defaulable create( 
      Supplier< Defaulable > supplier ) {
         return supplier.get(); 
      }
}
````
下面的代碼片段整合了默認方法和靜態(tài)方法的使用場景:
````
public static void main( String[] args ) { 
      Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); 
      System.out.println( defaulable.notRequired() ); 
      defaulable = DefaulableFactory.create( OverridableImpl::new ); 
      System.out.println( defaulable.notRequired() );
}
````
這段代碼的輸出結(jié)果如下:
Default implementationOverridden implementation
由于JVM上的默認方法的實現(xiàn)在字節(jié)碼層面提供了支持,因此效率非常高。默認方法允許在不打破現(xiàn)有繼承體系的基礎(chǔ)上改進接口。該特性在官方庫中的應(yīng)用是:
給**java.util.Collection**接口添加新方法,如**stream()**、**parallelStream()**、**forEach()**和**removeIf()**等等。

#3. 方法引用
方法引用使得開發(fā)者可以直接引用現(xiàn)存的方法、Java類的構(gòu)造方法或者實例對象。方法引用和Lambda表達式配合使用,使得java類的構(gòu)造方法看起來緊湊而簡潔,沒有很多復(fù)雜的模板代碼。<br />例子中,Car類是不同方法引用的例子,可以幫助讀者區(qū)分四種類型的方法引用。
````
public static class Car {
    public static Car create( final Supplier< Car > supplier ) {
        return supplier.get();
    }              
    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }
    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }
    public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
}
````
第一種方法引用的類型是   **構(gòu)造器**引用,語法是**Class::new**,或者更一般的形式:**Class<T>::new**。注意:這個構(gòu)造器沒有參數(shù)。
````
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
````
第二種方法引用的類型是   **靜態(tài)方法**引用,語法是**Class::static_method**。注意:這個方法接受一個Car類型的參數(shù)。
````
cars.forEach( Car::collide );
````
第三種方法引用的類型是某個類的**成員方法**的引用,語法是**Class::method**,注意,這個方法沒有定義入?yún)ⅲ?````
cars.forEach( Car::repair );
````
第四種方法引用的類型是某個**實例對象**的**成員方法**的引用,語法是**instance::method**。注意:這個方法接受一個Car類型的參數(shù):
````
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
````
運行上述例子,可以在控制臺看到如下輸出(Car實例可能不同):
````
Collided  com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired  com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d    
````
#4. 重復(fù)注解
自從Java 5中引入[注解](http://www.javacodegeeks.com/2012/08/java-annotations-explored-explained.html)以來,這個特性開始變得非常流行,并在各個框架和項目中被廣泛使用。不過,注解有一個很大的限制是:在同一個地方不能多次使用同一個注解。Java 8打破了這個限制,引入了重復(fù)注解的概念,允許在同一個地方多次使用同一個注解。
在Java 8中使用**@Repeatable**注解定義重復(fù)注解,實際上,這并不是語言層面的改進,而是編譯器做的一個trick,底層的技術(shù)仍然相同??梢岳孟旅娴拇a說明:
````
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Filters {
        Filter[] value();
    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(Filters.class)
    public @interface Filter {
        String value();
    };

    @Filter("filter1")
    @Filter("filter2")
    public interface Filterable {
    }

    public static void main(String[] args) {
        for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
            System.out.println(filter.value());
        }
    }
}
````
正如我們所見,這里的**Filter**類使用@Repeatable(Filters.class)注解修飾,而**Filters**是存放**Filter**注解的容器,編譯器盡量對開發(fā)者屏蔽這些細節(jié)。這樣,**Filterable**接口可以用兩個**Filter**注解注釋(這里并沒有提到任何關(guān)于Filters的信息)。
另外,反射API提供了一個新的方法:**getAnnotationsByType()**,可以返回某個類型的重復(fù)注解,例如Filterable.class.getAnnoation(Filters.class)
將返回兩個Filter實例,輸出到控制臺的內(nèi)容如下所示:
filter1<br />filter2

#5. 更好的類型推斷

Java 8編譯器在類型推斷方面有很大的提升,在很多場景下編譯器可以推導(dǎo)出某個參數(shù)的數(shù)據(jù)類型,從而使得代碼更為簡潔。例子代碼如下:
````
package com.javacodegeeks.java8.type.inference;

public class Value< T > {
    public static< T > T defaultValue() { 
        return null; 
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
````
下列代碼是**Value<String>**類型的應(yīng)用:
````
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value< String > value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
````
參數(shù)***Value.defaultValue()***的類型由編譯器推導(dǎo)得出,不需要顯式指明。<br />在Java 7中這段代碼會有編譯錯誤,除非使用***Value.<String>defaultValue()****。
#6. 拓寬注解的應(yīng)用場景

Java 8拓寬了注解的應(yīng)用場景?,F(xiàn)在,注解幾乎可以使用在任何元素上:**局部變量**、**接口類型**、**超類**和**接口實現(xiàn)類**,甚至可以用在函數(shù)的**異常**定義上。下面是一些例子:
````
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {        
    }

    public static class Holder< @NonEmpty T > extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {            
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder< String > holder = new @NonEmpty Holder< String >();        
        @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();        
    }
}
````
***ElementType.TYPE_USER***和***ElementType.TYPE_PARAMETER***是Java 8新增的兩個注解,用于描述注解的使用場景。Java 語言也做了對應(yīng)的改變,以識別這些新增的注解。
#7. Optional
Java應(yīng)用中最常見的bug就是[空值異常](http://examples.javacodegeeks.com/java-basics/exceptions/java-lang-nullpointerexception-how-to-handle-null-pointer-exception/)。在Java 8之前,[Google Guava](http://code.google.com/p/guava-libraries/)引入了**Optionals**類來解決**NullPointerException**,從而避免源碼被各種**null**檢查污染,以便開發(fā)者寫出更加整潔的代碼。Java 8也將**Optional**加入了官方庫。
**Optional**僅僅是一個容易:存放T類型的值或者null。它提供了一些有用的接口來避免顯式的null檢查,可以參考[Java 8官方文檔](http://docs.oracle.com/javase/8/docs/api/)了解更多。
接下來看一點使用**Optional**的例子:可能為空的值或者某個類型的值:
````
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() ); 
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
````
如果**Optional**實例持有一個非空值,則**isPresent()**方法返回 true,否則返回 false;**orElseGet()**方法,**Optional**實例持有 null,則可以接受一個 lambda表達式生成的默認值;**map()**方法可以將現(xiàn)有的**Opetional**實例的值轉(zhuǎn)換成新的值;**orElse()**方法與**orElseGet()**方法類似,但是在持有null的時候返回傳入的默認值。
上述代碼的輸出結(jié)果如下:
````
Full Name is set? falseFull Name: [none]Hey Stranger!
````
再看下另一個簡單的例子:
````
Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
````
這個例子的輸出是:
````
First Name is set? trueFirst Name: TomHey Tom!
````
#8. Streams
新增的[Stream API](http://www.javacodegeeks.com/2014/05/the-effects-of-programming-with-java-8-streams-on-algorithm-performance.html)(java.util.stream)將生成環(huán)境的函數(shù)式編程引入了Java庫中。這是目前為止最大的一次對Java庫的完善,以便開發(fā)者能夠?qū)懗龈佑行А⒏雍啙嵑途o湊的代碼。
Steam API極大得簡化了集合操作(后面我們會看到不止是集合),首先看下這個叫Task的類:
````
public class Streams {
    private enum Status {OPEN, CLOSED};
    private static final class Task {
        private final Status status;
        private final Integer points;
        Task(final Status status, final Integer points) {
            this.status = status;
            this.points = points;
        }
        public Integer getPoints() {
            return points;
        }
        public Status getStatus() {
            return status;
        }
        @Override
        public String toString() {
            return String.format("[%s, %d]", status, points);
        }
    }
}
````
Task類有一個分數(shù)(或偽復(fù)雜度)的概念,另外還有兩種狀態(tài):**OPEN**或者**CLOSED**。現(xiàn)在假設(shè)有一個task集合:
````
final Collection< Task > tasks = 
             Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) );
````
首先看一個問題:在這個task集合中一共有多少個**OPEN**狀態(tài)的點?在Java 8之前,要解決這個問題,則需要使用**foreach**循環(huán)遍歷task集合;但是在Java 8中可以利用steams解決:包括一系列元素的列表,并且支持順序和并行處理。
````
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks.stream().filter( 
               task -> task.getStatus() == Status.OPEN ) .mapToInt(Task::getPoints ).sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );
````
運行這個方法的控制臺輸出是:
````
Total points: 18
````
這里有很多知識點值得說。
**第一**,tasks集合被轉(zhuǎn)換成steam表示;
**第二**,在steam上的**filter**操作會過濾掉所有CLOSED的task;
**第三**,**mapToInt**操作基于每個task實例的**Task::getPoints**方法將task流轉(zhuǎn)換成Integer集合;
**第四**,通過**sum**方法計算總和,得出最后的結(jié)果。
在學(xué)習(xí)下一個例子之前,還需要記住一些steams([點此更多細節(jié)](http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps))的知識點。Steam之上的操作可分為**中間**操作和**晚期**操作。
**中間操作**:會返回一個新的steam——執(zhí)行一個中間操作(例如**filter**)并不會執(zhí)行實際的過濾操作,而是創(chuàng)建一個新的steam,并將原steam中符合條件的元素放入新創(chuàng)建的steam。
**晚期操作**:(例如**forEach**或者**sum**),會遍歷steam并得出結(jié)果或者附帶結(jié)果;在執(zhí)行晚期操作之后,steam處理線已經(jīng)處理完畢,就不能使用了。在幾乎所有情況下,晚期操作都是立刻對steam進行遍歷。
steam的另一個價值是創(chuàng)造性地支持并行處理(parallel processing)。對于上述的tasks集合,我們可以用下面的代碼計算所有任務(wù)的點數(shù)之和:
````
// Calculate total points of all tasks
final double totalPoints = tasks
          .stream()
          .parallel() 
          .map( task -> task.getPoints() ) // or map( Task::getPoints ) 
          .reduce( 0, Integer::sum );
System.out.println( "Total points (all tasks): " + totalPoints );
````
這里我們使用**parallel**方法并行處理所有的task,并使用**reduce**方法計算最終的結(jié)果??刂婆_輸出如下:
````
Total points(all tasks): 26.0
````
對于一個集合,經(jīng)常需要根據(jù)某些條件對其中的元素分組。利用steam提供的API可以很快完成這類任務(wù),代碼如下:
````
// Group tasks by their status
final Map< Status, List< Task > > map = tasks
             .stream()
             .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
````
控制臺的輸出如下:
````
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
````
最后一個關(guān)于tasks集合的例子問題是:如何計算集合中每個任務(wù)的點數(shù)在集合中所占的比重,具體處理的代碼如下:
````
// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
               .stream() // Stream< String >
               .mapToInt( Task::getPoints ) // IntStream
               .asLongStream() // LongStream
               .mapToDouble( points -> points / totalPoints ) // DoubleStream
               .boxed() // Stream< Double >
               .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
               .mapToObj( percentage -> percentage + "%" ) // Stream< String>
               .collect( Collectors.toList() ); // List< String > 
System.out.println( result );
````
控制臺輸出結(jié)果如下:
````
[19%, 50%, 30%]
````
最后,正如之前所說,Steam API不僅可以作用于Java集合,傳統(tǒng)的IO操作(從文件或者網(wǎng)絡(luò)一行一行得讀取數(shù)據(jù))可以受益于steam處理,這里有一個小例子:
````
final Path path = new File( filename ).toPath();
try( 
       Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 )
) { 
       lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
````
Stream的方法**onClose** 返回一個等價的有額外句柄的Stream,當Stream的 **close()** 方法被調(diào)用的時候這個句柄會被執(zhí)行。Stream API、Lambda表達式還有接口默認方法和靜態(tài)方法支持的方法引用,是Java 8對軟件開發(fā)的現(xiàn)代范式的響應(yīng)。
#9. Date/Time API(JSR 310)
Java 8引入了[新的Date-Time API(JSR 310)](https://jcp.org/en/jsr/detail?id=310)來改進時間、日期的處理。時間和日期的管理一直是最令Java開發(fā)者痛苦的問題。**java.util.Date**和后來的**java.util.Calendar**一直沒有解決這個問題(甚至令開發(fā)者更加迷茫)。
因為上面這些原因,誕生了第三方庫[Joda-Time](http://www.joda.org/joda-time/),可以替代Java的時間管理API。Java 8中新的時間和日期管理API深受Joda-Time影響,并吸收了很多Joda-Time的精華。新的java.time包包含了所有關(guān)于日期、時間、時區(qū)、Instant(跟日期類似但是精確到納秒)、duration(持續(xù)時間)和時鐘操作的類。新設(shè)計的API認真考慮了這些類的不變性(從java.util.Calendar吸取的教訓(xùn)),如果某個實例需要修改,則返回一個新的對象。
我們接下來看看java.time包中的關(guān)鍵類和各自的使用例子。首先,**Clock**類使用時區(qū)來返回當前的納秒時間和日期。**Clock**可以替代**System.currentTimeMillis()**和**TimeZone.getDefault()**。
````
// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
````
這個例子的輸出結(jié)果是:
````
2014-04-12T15:19:29.282Z1397315969360
````
第二,關(guān)注下**LocalDate**和**LocalTime**類。**LocalDate**僅僅包含ISO-8601日歷系統(tǒng)中的日期部分;**LocalTime**則僅僅包含該日歷系統(tǒng)中的時間部分。這兩個類的對象都可以使用Clock對象構(gòu)建得到。
````
// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );
System.out.println( dateFromClock );
// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time );
System.out.println( timeFromClock );
````
上述例子的輸出結(jié)果如下:
````
2014-04-122014-04-1211:25:54.56815:25:54.568
````
**LocalDateTime**類包含了LocalDate和LocalTime的信息,但是不包含ISO-8601日歷系統(tǒng)中的時區(qū)信息。這里有一些[關(guān)于LocalDate和LocalTime的例子](https://www.javacodegeeks.com/2014/04/java-8-date-time-api-tutorial-localdatetime.html):
````
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime );
System.out.println( datetimeFromClock );
````
上述這個例子的輸出結(jié)果如下:
````
2014-04-12T11:37:52.3092014-04-12T15:37:52.309
````
如果你需要特定時區(qū)的data/time信息,則可以使用**ZoneDateTime**,它保存有ISO-8601日期系統(tǒng)的日期和時間,而且有時區(qū)信息。下面是一些使用不同時區(qū)的例子:
````
// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
````
這個例子的輸出結(jié)果是:
````
2014-04-12T11:47:01.017-04:00[America/New_York]2014-04-12T15:47:01.017Z2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
````
最后看下**Duration**類,它持有的時間精確到秒和納秒。這使得我們可以很容易得計算兩個日期之間的不同,例子代碼如下:
````
// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
````
這個例子用于計算2014年4月16日和2015年4月16日之間的天數(shù)和小時數(shù),輸出結(jié)果如下:
````
Duration in days: 365Duration in hours: 8783
````
#10. Base64
[對Base64編碼的支持](http://www.javacodegeeks.com/2014/04/base64-in-java-8-its-not-too-late-to-join-in-the-fun.html)已經(jīng)被加入到Java 8官方庫中,這樣不需要使用第三方庫就可以進行Base64編碼,例子代碼如下:
````
package com.javacodegeeks.java8.base64;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64s { 
    public static void main(String[] args) { 
              final String text = "Base64 finally in Java 8!"; 
              final String encoded = Base64 .getEncoder()
                     .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) ); 
              System.out.println( encoded ); 
              final String decoded = new String( Base64.getDecoder().decode( encoded ),     
                                                              StandardCharsets.UTF_8 );
              System.out.println( decoded ); 
    }
}
````
這個例子的輸出結(jié)果如下:
````
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==Base64 finally in Java 8!
````
新的Base64API也支持URL和MINE的編碼解碼。**Base64.*getUrlEncoder*()** / **Base64.*getUrlDecoder*()**, **Base64.*getMimeEncoder*()** / **Base64.*getMimeDecoder*()**。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容