[转载] Java9发布回顾Java 8的十大新特性

参考链接: Java中的DoubleStream mapToObj()

java9已经在北京时间9月22日正式发布,开发者可以在oracle jdk官网上下载到最新的jdk9。 

今天,我们先来一起复习一下2014年发布的Java 8的十大新特性。先来喝杯java~~~ 

  

按照java升级的传统,偶数版的(468)改动较小,奇数版的(579)都是大改动。但对于java8而言是一次变化巨大的更新,耗费了工程师大量的时间,还借鉴了很多其它语言和类库。这里为大家列举十个新特性。   

  

 

 

 Lambda表达式  

 Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据:函数式程序员对这一概念非常熟悉。在JVM平台上的很多语言(Groovy,Scala,……)从一开始就有Lambda,但是Java程序员不得不使用毫无新意的匿名类来代替lambda。  关于Lambda设计的讨论占用了大量的时间与社区的努力。可喜的是,最终找到了一个平衡点,使得可以使用一种即简洁又紧凑的新方式来构造Lambdas。在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–>符号与函数体三部分表示。例如:  

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

 

 

请注意参数e的类型是由编译器推测出来的。同时,你也可以通过把参数类型与参数包括在括号中的形式直接给出参数的类型: 

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );

 

在某些情况下lambda的函数体会更加复杂,这时可以把函数体放到在一对花括号中,就像在Java中定义普通函数一样。例如: 

Arrays.asList( "a", "b", "d" ).forEach( e -> {

    System.out.print( e );

    System.out.print( e );

} );

 Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更高)。例如,下面两个代码片段是等价的:

 

String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach( 

    ( String e ) -> System.out.print( e + separator ) );

final String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach( 

    ( String e ) -> System.out.print( e + separator ) );

 

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。最终采取的方法是:增加函数式接口的概念。函数式接口就是一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为lambda表达式。java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的两个例子。在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java 8增加了一种特殊的注解@FunctionalInterface(Java 8中所有类库的已有接口都添加了@FunctionalInterface注解)。让我们看一下这种函数式接口的定义:

 

@FunctionalInterface

public interface Functional {

    void method();

}

 

需要记住的一件事是:

默认方法与静态方法

并不影响函数式接口的契约,可以任意使用:

 

@FunctionalInterface

public interface FunctionalDefaultMethods {

    void method();

         

    default void defaultMethod() {            

    }        

}

 

Lambda是Java 8最大的卖点。它具有吸引越来越多程序员到Java平台上的潜力,并且能够在纯Java语言环境中提供一种优雅的方式来支持函数式编程。更多详情可以参考官方文档。 

  

  

 

 接口的默认方法与静态方法  

 Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法使接口有点像Traits(Scala中特征(trait)类似于Java中的Interface,但它可以包含实现代码,也就是目前Java8新增的功能),但与传统的接口又有些不一样,它允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性。 

 默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求。相反,每个接口都必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要的话,可以覆盖这个默认实现)。让我们看看下面的例子:  

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接口用关键字default声明了一个默认方法notRequired(),Defaulable接口的实现者之一DefaultableImpl实现了这个接口,并且让默认方法保持原样。Defaulable接口的另一个实现者OverridableImpl用自己的方法覆盖了默认方法。 

 Java 8带来的另一个有趣的特性是接口可以声明(并且可以提供实现)静态方法。例如:  

private interface DefaulableFactory {

    // Interfaces now allow static methods

    static Defaulable create( Supplier< Defaulable > supplier ) {

        return supplier.get();

    }

}

 

下面的一小段代码片段把上面的默认方法与静态方法黏合到一起: 

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() );

}

这个程序的控制台输出如下:

 

Default implementation

Overridden implementation

 

在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。这方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),…… 

 

尽管默认方法非常强大,但是在使用默认方法时我们需要小心注意一个地方:在声明一个默认方法前,请仔细思考是不是真的有必要使用默认方法,因为默认方法会带给程序歧义,并且在复杂的继承体系中容易产生编译错误。更多详情请参考

官方文档

 

 

 

 

 Stream  

 最新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。 

 Stream 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类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子: 

final Collection< Task > tasks = Arrays.asList(

    new Task( Status.OPEN, 5 ),

    new Task( Status.OPEN, 13 ),

    new Task( Status.CLOSED, 8 ) 

);

我们下面要讨论的第一个问题是所有状态为OPEN的任务一共有多少分数?在Java 8以前,一般的解决方式用foreach循环,但是在Java 8里面我们可以使用stream:一串支持连续、并行聚集操作的元素。 

// 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 

 

 这里有几个注意事项。第一,task集合被转换化为其相应的stream表示。然后,filter操作过滤掉状态为CLOSED的task。下一步,mapToInt操作通过Task::getPoints这种方式调用每个task实例的getPoints方法把Task的stream转化为Integer的stream。最后,用sum函数把所有的分数加起来,得到最终的结果。 

 在继续讲解下面的例子之前,关于stream有一些需要注意的地方(详情在这里).stream操作被分成了中间操作与最终操作这两种。 

 中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream 中符合给定谓词的所有元素。 

 像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。 

 stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。  

// 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 );

这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用reduce方法来算最终的结果。

 

下面是这个例子在控制台的输出: 

Total points (all tasks): 26.0

经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子: 

// 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]]}

让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子。 

// 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 );

下面是这个例子的控制台输出: 

[19%, 50%, 30%]

最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。 

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()方法时,与关闭相关的处理器就会执行。 

 Stream API、Lambda表达式与方法引用在接口默认方法与静态方法的配合下是Java 8对现代软件开发范式的回应。更多详情请参考官方文档。 

   

 

 

 Date/Time API (JSR 310)  

 Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。对日期与时间的操作一直是Java程序员最痛苦的地方之一。标准的 java.util.Date以及后来的java.util.Calendar一点没有改善这种情况(可以这么说,它们一定程度上更加复杂)。 

 这种情况直接导致了Joda-Time——一个可替换标准日期/时间处理且功能非常强大的Java API的诞生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影响,并且吸取了其精髓。新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。在设计新版API时,十分注重与旧版API的兼容性:不允许有任何的改变(从java.util.Calendar中得到的深刻教训)。如果需要修改,会返回这个类的一个新实例。 

 让我们用例子来看一下新版API主要类的使用方法。第一个是Clock类,它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。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() );

下面是程序在控制台上的输出:

 

2014-04-12T15:19:29.282Z

1397315969360

我们需要关注的其他类是LocaleDate与LocalTime。LocaleDate只持有ISO-8601格式且无时区信息的日期部分。相应的,LocaleTime只持有ISO-8601格式且无时区信息的时间部分。LocaleDate与LocalTime都可以从Clock中得到。

 

// 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 );

下面是程序在控制台上的输出:

 

2014-04-12

2014-04-12

11:25:54.568

15:25:54.568

LocaleDateTime把LocaleDate与LocaleTime的功能合并起来,它持有的是ISO-8601格式无时区信息的日期与时间。下面是一个

快速入门

的例子。

 

// Get the local date/time

final LocalDateTime datetime = LocalDateTime.now();

final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

         

System.out.println( datetime );

System.out.println( datetimeFromClock );

下面是程序在控制台上的输出:

 

2014-04-12T11:37:52.309

2014-04-12T15:37:52.309

如果你需要特定时区的日期/时间,那么ZonedDateTime是你的选择。它持有ISO-8601格式具具有时区信息的日期与时间。下面是一些不同时区的例子:

 

// 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 );

下面是程序在控制台上的输出:

 

2014-04-12T11:47:01.017-04:00[America/New_York]

2014-04-12T15:47:01.017Z

2014-04-12T08:47:01.017-07:00[America/Los_Angeles]

最后,让我们看一下Duration类:在秒与纳秒级别上的一段时间。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号与2014年4月16号之间的过程。下面是程序在控制台上的输出:

 

Duration in days: 365

Duration in hours: 8783 

对Java 8在日期/时间API的改进整体印象是非常非常好的。一部分原因是因为它建立在“久战杀场”的Joda-Time基础上,另一方面是因为用来大量的时间来设计它,并且这次程序员的声音得到了认可。更多详情请参考官方文档。 

  

 

 

 扩展注解的支持  

Java 8扩展了注解的上下文。现在几乎可以为任何东西添加注解:局部变量、泛型类、父类与接口的实现,就连方法的异常也能添加注解。下面演示几个例子: 

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_USE和ElementType.TYPE_PARAMETER是两个新添加的用于描述适当的注解上下文的元素类型。在Java语言中,注解处理API也有小的改动来识别新增的类型注解。 

  

  

 

 Optional  

 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。 

 Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考官方文档。 

 我们下面用两个小例子来演示如何使用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。为了防止Optional为空值,orElseGet()方法通过回调函数来产生一个默认值。map()函数对当前Optional的值进行转化,然后返回一个新的Optional实例。orElse()方法和orElseGet()方法类似,但是orElse接受一个默认值而不是一个回调函数。下面是这个程序的输出: 

Full Name is set? false

Full 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? true

First Name: Tom

Hey Tom!

 

更多详情请参考官方文档 

  

  

 

 方法引用  

 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。 

 下面,我们以定义了4个方法的Car这个类作为例子,区分Java中支持的4种不同的方法引用。  

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() );

    }

}

第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。 

final Car car = Car.create( Car::new );

final List< Car > cars = Arrays.asList( car );

第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数。 

cars.forEach( Car::collide );

第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。 

cars.forEach( Car::repair );

最后,第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数 

final Car police = Car.create( Car::new );

cars.forEach( police::follow );

运行上面的Java程序在控制台上会有下面的输出(Car的实例可能不一样): 

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

 

关于方法引用的更多详情请参考官方文档。 

  

  

 

 重复注解  

 自从Java 5引入了注解机制,这一特性就变得非常流行并且广为使用。然而,使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java 8打破了这条规则,引入了重复注解机制,这样相同的注解可以在同一地方声明多次。 

 重复注解机制本身必须用@Repeatable注解。事实上,这并不是语言层面上的改变,更多的是编译器的技巧,底层的原理保持不变。让我们看一个快速入门的例子:  

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() );

        }

    }

}

 

 

 正如我们看到的,这里有个使用@Repeatable( Filters.class )注解的注解类Filter,Filters仅仅是Filter注解的数组,但Java编译器并不想让程序员意识到Filters的存在。这样,接口Filterable就拥有了两次Filter(并没有提到Filter)注解。 

 同时,反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型(请注意Filterable.class.getAnnotation( Filters.class )经编译器处理后将会返回Filters的实例)。 

 程序输出结果如下:  

filter1

filter2

 

更多详情请参考官方文档 

  

  

 

 JavaScript引擎Nashorn  

 

 Nashorn,一个新的JavaScript引擎随着Java 8一起公诸于世,它允许在JVM上开发运行某些JavaScript应用。Nashorn就是javax.script.ScriptEngine的另一种实现,并且它们俩遵循相同的规则,允许Java与JavaScript相互调用。下面看一个例子:  

ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByName( "JavaScript" );

         

System.out.println( engine.getClass().getName() );

System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) ); 

 

 下面是程序在控制台上的输出: 

jdk.nashorn.api.scripting.NashornScriptEngine

Result: 2 

  

  

 

 Base64  

在Java 8中,Base64编码已经成为Java类库的标准。它的使用十分简单,下面让我们看一个例子: 

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 );

    }

}

程序在控制台上输出了编码后的字符与解码后的字符:

 

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==

Base64 finally in Java 8! 

Base64类同时还提供了对URL、MIME友好的编码器与解码器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。 

  

 

 

 结束语:  

本文写于java9发布之后的几天内,之所以想写这篇文章是因为自己确实还没有掌握清楚java 8的这些新特性,在编程语言呈现爆发的时段,java的份额越来越被挤压的当今,java 8的发布我认为这意味着这是java的一个转折点,意味着java语言再像其他语言学习,借鉴,这是java语言刚开始的那些年很像。不学习就要被淘汰,人如此,语言亦如此。

 

 

更多更全的java 8新特性请访问 

Java 8新特性终极指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/540240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

窗体间传递数据

前言 做项目的时候&#xff0c;winfrom因为没有B/S的缓存机制&#xff0c;窗体间传递数据没有B/S页面传递数据那么方便&#xff0c;今天我们就说下winfrom中窗体传值的几种方式。 共有字段传递 共有字段传递实现起来很方便&#xff0c;就是在窗体类中加个共有字段属性&#xff…

[转载] c语言中检查命令行参数_C中的命令行参数

参考链接&#xff1a; Java中的命令行参数 c语言中检查命令行参数 Command line argument is a parameter supplied to the program when it is invoked. Command line argument is an important concept in C programming. It is mostly used when you need to control your …

MySQL关闭Enterprise Server源码

今天从MySQL官方网站上获知&#xff0c;MySQL宣布关闭Enterprise Server的源码&#xff0c;对于广大开源爱好者来说&#xff0c;这是一个沉重的打击。虽然免费的用户群体一般仅仅使用MySQL Community Server&#xff08;开源免费社区版&#xff09;&#xff0c;但关闭MySQL Ent…

[转载] Java中Scanner用法总结

参考链接&#xff1a; Java之Scanner类 最近在做OJ类问题的时候&#xff0c;经常由于Scanner的使用造成一些细节问题导致程序不通过&#xff08;最惨的就是网易笔试&#xff0c;由于sc死循环了也没发现&#xff0c;导致AC代码也不能通过。。。&#xff09;&#xff0c;因此对S…

os和shutil模块

import os //os模块基本实现了linux系统中所有的命令 os.system(终端命令)&#xff1a;在终端执行命令 os.getcwd():获取当前的工作路径 os.chdir():修改工作路径 os.chmod():修改权限 os.chown():修改属主属组 os.mkdir():创建单个目录&#xff0c;当目录存在时报异常&…

[转载] JAVA语言程序设计(基础篇)第十版课后题答案(第一章)

参考链接&#xff1a; Java中的Scanner和nextChar() JAVA语言程序设计&#xff08;基础篇&#xff09;第十版课后题答案 第一章 第二题 /** Created by ysy on 2018/7/6. */ public class text2 { public static void main(String[] args){ for(int i 0; i < 5; i) Syste…

java.util.Date和java.sql.Date 一点区别

最近无意中发现&#xff0c;在oracle中同一样的一个Date类型字段&#xff0c;存储的日期格式有两种不同的情况&#xff0c;第一种是2011-1-1 12:00:00&#xff0c;第二种是2011-1-1&#xff0c;仔细查找发现在向数据库中写数据的时候定义的变量的问题。 第一种是&#xff1a;ja…

[转载] java中关于用\t格式输出

参考链接&#xff1a; 用Java格式化输出 看了好多人关于\t的用法&#xff0c;感觉找不到自己想要的答案&#xff0c;所以索性就自己输出来看看&#xff0c;如图&#xff1a;这样可以一目了然的看出来&#xff0c;\t&#xff08;制表符&#xff09;的作用就是看前面输出满不满8…

微信抢房软件开发

2019独角兽企业重金招聘Python工程师标准>>> 这两年楼市真可谓是一个"火“字难以形容 经历了长沙两次开盘&#xff0c;都没有抢到&#xff0c;目前还没有买到房子&#xff0c;说说我的悲剧吧&#xff0c;让大伙都开心开心 第一次抢房是今年4月份长沙万科金域国…

[转载] Java——数组习题

参考链接&#xff1a; Java从控制台读取输入的方法 package chap02; import java.util.Scanner; /** * * author admin * date 2020-4-8 * description: * 题目内容&#xff1a; 编写程序&#xff0c; 从控制台读取下面的信息&#xff0c; 每月按22天工作日计算&#xff0c;…

超全Linux备份工具集合,满足你的所有需要!

经常备份计算机上的数据是个好的做法&#xff0c;它可以手动完成&#xff0c;也可以设置成自动执行。许多备份工具拥有不同的功能特性&#xff0c;让用户可以配置备份类型、备份时间、备份对象、将备份活动记入日志及执行更多操作。 1.Rsync这是一款在Linux用户当中颇受欢迎的命…

[转载] Java内存管理-你真的理解Java中的数据类型吗(十)

参考链接&#xff1a; Java中的字符串类String 1 做一个积极的人 编码、改bug、提升自己 我有一个乐园&#xff0c;面向编程&#xff0c;春暖花开&#xff01; 推荐阅读 第一季 0、Java的线程安全、单例模式、JVM内存结构等知识梳理 1、Java内存管理-程序运行过程&#x…

Linux系统安全加固脚本

闲来无事&#xff0c;整理一个系统安全加固脚本&#xff0c;每个公司的要求不一样&#xff0c;所以仅供参考&#xff1a; #!/bin/sh echo "00 */1 * * * /usr/sbin/ntpdate 192.168.1.1 >>/var/log/ntpdate.log" > mycrontab crontab mycrontab rm -rf my…

[转载] 整理下java中stringBuilder和stringBuffer两个类的区别

参考链接&#xff1a; Java中的StringBuffer类 StringBuilder和StringBuffer这两个类在动态拼接字符串时常用&#xff0c;肯定比String的效率和开销小&#xff0c;这是因为String的对象不会回收哦。 其实我一直用StringBuilder这个类&#xff0c;因为可以简写为sb的变量在程序…

11.13 模10计数器设计

.新建一个工程 Family&#xff1a;FLEX10K Available device&#xff1a;EPF10K20TC144-3 2.设置lpm_counter宏单元参数并连接引脚 连接引脚的时候要注意的是&#xff0c;向量线的连接。 3.时序仿真 检查无误后进行下一步 4.载入7448并进行引脚连接 5.分配管脚 再次编译&#x…

[转载] java对象在内存中的结构

参考链接&#xff1a; 了解Java中的类和对象 今天看到一个不错的PPT&#xff1a;Build Memory-efficient Java Applications&#xff0c;开篇便提出了一个问题&#xff0c;在Hotspot JVM中&#xff0c;32位机器下&#xff0c;Integer对象的大小是int的几倍&#xff1f; 我们…

使用valueOf前必须进行校验

每个枚举都是java.lang.Enum的子类,都可以访问Enum类提供的方法,比如hashCode(),name(),valueOf()等..... 其中valueOf()方法会把一个String类型的名称转变为枚举项,也就是枚举项中查找出字面值与该参数相等的枚举项,虽然这个方法很简单,但是JDK却做了一个对于开发人员来说并不…

[转载] 【Java】Java基础知识及其扩展笔记(8千字)

参考链接&#xff1a; Java中的StringBuilder类及其示例 Java基础知识及其扩展笔记 零 l 写在前面一 l JVM1、【1.1.2.1】java程序运行的一般流程2、【1.1.2.1】JVM一般运行流程3、【1.1.2.1】JIT&#xff08;just in time 即时编译编译器&#xff09;4、堆与栈 二 l Java …

多IDC GSLB的部署

之前已经介绍过GSLB的实现原理&#xff0c;这里再向大家讲述一下GSLB经常遇到的部署方式&#xff0c;多IDC的部署。很多大型的企业或业务容灾要求非常高的客户都会部署有多个异地的数据中心&#xff0c;以保证其业务的“全天候”不间断的正常运行&#xff0c;而要整合多个IDC的…

[转载] Controller报错:java.lang.NoSuchMethodException: java.util.List.<init>()

参考链接&#xff1a; Java8中的java.util.StringJoiner 报错详情&#xff1a; java.lang.NoSuchMethodException: java.util.List.<init>() 以及 No primary or default constructor found for interface java.util.List 示例&#xff1a; /** * 此接口会产生以…