当前位置:   article > 正文

java8(jdk1.8)都有哪些新功能?_java1.8是哪个版本

java1.8是哪个版本

目录

什么是java8?

Java8的新特性(新功能):

一、Lambda表达式和函数式接口

二、接口的默认方法和静态方法

三、方法引用

四、重复注解

五、扩展注解的支持

六、Optional

七、Stream(流)

八、Date/Time API (JSR 310)

九、JavaScript引擎Nashorn

十、Base64

除了这十大新特性之外,还有另外的一些新特性:


什么是java8?

Java8又称为jdk1.8,是Java语言开发的一个主要版本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。Oracle公司于2014年3月18日发布Java 8版本,它支持函数式编程,新的JavaScript引擎,新的日期API等。

Java8的新特性(新功能):

一、Lambda表达式和函数式接口

Lambda表达式也称为闭包,是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理,这就是典型的函数式开发。最简单的Lambda表达式可由逗号分隔的参数列表、-> 符号和语句块组成。

示例一:一行代码,走遍天下

  1. // Java8之前:
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. System.out.println("在Java8之前,代码太多,而做的事情太少!");
  6. }
  7. }).start();
  8. // Java8方式:
  9. new Thread( () -> System.out.println("在Java8中,Lambda表达式炒鸡棒棒哒!") ).start();

示例二:更简单的事件监听代码

  1. // Java8之前:
  2. JButton btn = new JButton("Show");
  3. btn .addActionListener(new ActionListener() {
  4. @Override
  5. public void actionPerformed(ActionEvent e) {
  6. System.out.println("正在监听执行过程");
  7. }
  8. });
  9. // Java8方式:
  10. JButton btn = new JButton("Show");
  11. btn .addActionListener((e) -> {
  12. System.out.println("正在监听执行过程");
  13. });

 示例三:迭代输出

  1. // Java8之前:
  2. List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
  3. for (String feature : features) {
  4. System.out.println(feature);
  5. }
  6. // Java8之后:
  7. List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
  8. features.forEach(n -> System.out.println(n));
  9. // 使用Java8的“方法引用”更方便,方法引用由::双冒号操作符标示,如下:
  10. features.forEach(System.out::println);

二、接口的默认方法和静态方法

默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的实现类也同时实现这个新添加的方法。

简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

1问:为什么java8要有默认方法这个特性?

java8之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口的时候,需要修改全部实现该接口的类, java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法的目的是为了解决接口的修改与现有的实现不兼容的问题。

2问:默认方法和抽象方法之间的区别是什么?

  • 抽象方法需要实现,而默认方法不需要;
  • 接口提供的默认方法会被接口的实现类继承或者覆写;

 我们只需在方法名前面加个 default 关键字即可实现默认方法,默认方法语法格式如下:

  1. public interface Vehicle {
  2. // 定义一个默认方法
  3. default void print(){
  4. System.out.println("我是一辆车");
  5. }
  6. // 定义一静态方法
  7. static void blowHorn(){
  8. System.out.println("按喇叭");
  9. }
  10. }

 默认方法和静态方法的完整实例:

  1. public class Java8Test {
  2. public static void main(String args[]){
  3. MobilePhone mobilePhone = new Phone();
  4. mobilePhone.print();
  5. }
  6. }
  7. //接口1
  8. interface MobilePhone {
  9. //默认方法
  10. default void print(){
  11. System.out.println("我是手机");
  12. }
  13. }
  14. //接口2
  15. interface HuaWei {
  16. //默认方法
  17. default void print(){
  18. System.out.println("我是华为手机");
  19. }
  20. //静态方法
  21. static void foldingScreen(){
  22. System.out.println("我是华为折叠屏手机");
  23. }
  24. }
  25. //实现类
  26. class Phone implements MobilePhone, HuaWei {
  27. public void print(){
  28. MobilePhone.super.print();
  29. HuaWei.super.print();
  30. HuaWei.foldingScreen();
  31. }
  32. }

三、方法引用

方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来更加紧凑简洁,减少冗余代码。

方法引用通过方法的名字来指向一个方法

方法引用使用一对冒号 :: 

 4 种不同方法的引用:

  1. @FunctionalInterface
  2. public interface Supplier<T> {
  3. T get();
  4. }
  5. class Car {
  6. //Supplier是jdk1.8的接口,这里和Lambda表达式一起使用
  7. public static Car create(final Supplier<Car> supplier) {
  8. return supplier.get();
  9. }
  10. public static void collide(final Car car) {
  11. System.out.println("Collided " + car.toString());
  12. }
  13. public void follow(final Car another) {
  14. System.out.println("Following the " + another.toString());
  15. }
  16. public void repair() {
  17. System.out.println("Repaired " + this.toString());
  18. }
  19. }
  • 构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:

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

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

  • 静态方法引用:它的语法是Class::static_method,实例如下:

    cars.forEach( Car::collide );

  • 特定类的任意对象的方法引用:它的语法是Class::method实例如下:

    cars.forEach( Car::repair );

  • 特定对象的方法引用:它的语法是instance::method实例如下:

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

    cars.forEach( police::follow );

示例: 

  1. public class Java8Test {
  2. public static void main(String args[]){
  3. List<String> students = new ArrayList();
  4. students.add("张三");
  5. students.add("李四");
  6. students.add("王五");
  7. students.add("赵六");
  8. students.add("田七");
  9. // 将System.out::println方法作为静态方法来引用
  10. students.forEach(System.out::println);
  11. }
  12. }

四、重复注解

对注解概念不太清楚的朋友,建议先看这篇文章【Java的注解】:https://blog.csdn.net/qq15577969/article/details/119673933

在Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次,Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次;Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。

重复注解机制本身需要用@Repeatable注解,示例代码:

  1. package com.javacodegeeks.java8.repeatable.annotations;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Repeatable;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. public class RepeatingAnnotations {
  8. @Target( ElementType.TYPE )
  9. @Retention( RetentionPolicy.RUNTIME )
  10. public @interface Filters {
  11. Filter[] value();
  12. }
  13. @Target( ElementType.TYPE )
  14. @Retention( RetentionPolicy.RUNTIME )
  15. @Repeatable( Filters.class )
  16. public @interface Filter {
  17. String value();
  18. };
  19. @Filter( "filter1" )
  20. @Filter( "filter2" )
  21. public interface Filterable {
  22. }
  23. public static void main(String[] args) {
  24. for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
  25. System.out.println( filter.value() );
  26. }
  27. }
  28. }

这里的Filter类使用@Repeatable(Filters.class)注解修饰,而Filters是存放Filter注解的容器,编译器尽量对开发者屏蔽这些细节。这样,Filterable接口可以用两个Filter注解注释。

另外,反射API提供了一个新的方法:getAnnotationsByType(),可以返回某个类型的重复注解,例如Filterable.class.getAnnoation(Filters.class)将返回两个Filter实例,输出到控制台的内容如下所示:

  1. filter1
  2. filter2

五、扩展注解的支持

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

六、Optional

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Java应用中最常见的bug就是空指针异常,Optional 类的引入很好的解决空指针异常,从而避免源码被各种null检查污染,以便开发者写出更加整洁的代码。

1、类声明

以下是一个 java.util.Optional<T> 类的声明:

  1. public final class Optional<T>
  2. extends Object

2、类方法这些方法是从 java.lang.Object 类继承来的)

序号方法 & 描述
1static <T> Optional<T> empty()

返回空的 Optional 实例。

2boolean equals(Object obj)

判断其他对象是否等于 Optional。

3Optional<T> filter(Predicate<? super <T> predicate)

如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。

4<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)

如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional

5T get()

如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException

6int hashCode()

返回存在值的哈希码,如果值不存在 返回 0。

7void ifPresent(Consumer<? super T> consumer)

如果值存在则使用该值调用 consumer , 否则不做任何事情。

8boolean isPresent()

如果值存在则方法会返回true,否则返回 false。

9<U>Optional<U> map(Function<? super T,? extends U> mapper)

如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。

10static <T> Optional<T> of(T value)

返回一个指定非null值的Optional。

11static <T> Optional<T> ofNullable(T value)

如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。

12T orElse(T other)

如果存在该值,返回值, 否则返回 other。

13T orElseGet(Supplier<? extends T> other)

如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。

14<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常

15String toString()

返回一个Optional的非空字符串,用来调试

3、Optional简单实例

  1. import java.util.Optional;
  2. public class Java8Test {
  3. public static void main(String args[]){
  4. Java8Test java8Test = new Java8Test();
  5. // Optional.ofNullable - 允许传递为 null 参数
  6. Optional<Integer> a = Optional.ofNullable(null);
  7. // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
  8. Optional<Integer> b = Optional.of(new Integer(10));
  9. java8Test.print(a,b);
  10. }
  11. public void print(Optional<Integer> a, Optional<Integer> b){
  12. // 1、Optional.isPresent - 判断值是否存在
  13. System.out.println("第一个参数值存在: " + a.isPresent());
  14. System.out.println("第二个参数值存在: " + b.isPresent());
  15. // 2、Optional.orElse - 如果值存在,返回它,否则返回默认值
  16. Integer value1 = a.orElse(new Integer(0));
  17. System.out.println("第一个值: " + value1);
  18. //3、Optional.get - 获取值,值需要存在
  19. Integer value2 = b.get();
  20. System.out.println( "第二个值:"+ value2);
  21. }
  22. }

七、Stream(流)

1、什么是 Stream?

Stream API(java.util.stream)是把真正的函数式编程风格引入到Java库中,这是目前为止最大的一次对Java库的完善,以便开发者能够写出高效率、干净、简洁的代码。其实简单来说可以把Stream理解为MapReduce,当然Google的MapReduce的灵感也是来自函数式编程,它其实是一连串支持连续、并行聚集操作的元素,从语法上看,也很像linux的管道、或者链式编程,代码写起来简洁明了!

Stream(流)是一个来自数据源的元素队列并支持聚合操作,Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。

数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。

聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。

内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

流程图:

  1. +--------------------+ +------+ +------+ +---+ +-------+
  2. | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
  3. +--------------------+ +------+ +------+ +---+ +-------+

以上的流程转换为 Java 代码为:

  1. List<Integer> transactionsIds =
  2. widgets.stream()
  3. .filter(b -> b.getColor() == RED)
  4. .sorted((x,y) -> x.getWeight() - y.getWeight())
  5. .mapToInt(Widget::getWeight)
  6. .sum();

2、java8生成流的两种方法:

  • stream() − 为集合创建串行流。

forEach

迭代流中的每个数据,以下代码片段使用 forEach 输出了10个随机数:

  1. Random random = new Random();
  2. random.ints().limit(10).forEach(System.out::println);
map

用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

  1. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  2. // 获取对应的平方数
  3. List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter

用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

  1. List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
  2. // 获取空字符串的数量
  3. long count = strings.stream().filter(string -> string.isEmpty()).count();
limit

用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

  1. Random random = new Random();
  2. random.ints().limit(10).forEach(System.out::println);

sorted

用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

  1. Random random = new Random();
  2. random.ints().limit(10).sorted().forEach(System.out::println);

Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

  1. List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
  2. List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
  3. System.out.println("筛选列表: " + filtered);
  4. String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
  5. System.out.println("合并字符串: " + mergedString);

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

  1. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  2. IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
  3. System.out.println("列表中最大的数 : " + stats.getMax());
  4. System.out.println("列表中最小的数 : " + stats.getMin());
  5. System.out.println("所有数之和 : " + stats.getSum());
  6. System.out.println("平均数 : " + stats.getAverage());
  • parallelStream() − 为集合创建并行流。

parallel

parallelStream 是流并行处理程序的代替方法。以下代码片段使用 parallelStream 来输出空字符串的数量:

  1. List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
  2. // 获取空字符串的数量
  3. long count = strings.parallelStream().filter(string -> string.isEmpty()).count();


八、日期时间 API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

  • 非线程安全 java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

  • 设计很差  Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

  • 时区处理麻烦 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java8 在 java.time 包下提供了很多新的 API,新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。以下仅介绍两个比较重要的 API:

  • Local(本地): 简化了日期时间的处理,没有时区的问题。

  1. import java.time.LocalDate;
  2. import java.time.LocalTime;
  3. import java.time.LocalDateTime;
  4. import java.time.Month;
  5. public class Java8Test {
  6. public static void main(String args[]){
  7. Java8Tester java8tester = new Java8Tester();
  8. java8tester.testLocalDateTime();
  9. }
  10. public void testLocalDateTime(){
  11. // 获取当前的日期时间
  12. LocalDateTime currentTime = LocalDateTime.now();
  13. System.out.println("当前时间: " + currentTime);
  14. LocalDate date1 = currentTime.toLocalDate();
  15. System.out.println("date1: " + date1);
  16. Month month = currentTime.getMonth();
  17. int day = currentTime.getDayOfMonth();
  18. int seconds = currentTime.getSecond();
  19. System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds);
  20. LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
  21. System.out.println("date2: " + date2);
  22. // 12 december 2014
  23. LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
  24. System.out.println("date3: " + date3);
  25. // 22 小时 15 分钟
  26. LocalTime date4 = LocalTime.of(22, 15);
  27. System.out.println("date4: " + date4);
  28. // 解析字符串
  29. LocalTime date5 = LocalTime.parse("20:15:30");
  30. System.out.println("date5: " + date5);
  31. }
  32. }
  • Zoned(时区) : 通过制定的时区处理日期时间。

  1. import java.time.ZonedDateTime;
  2. import java.time.ZoneId;
  3. public class Java8Test {
  4. public static void main(String args[]){
  5. Java8Tester java8tester = new Java8Tester();
  6. java8tester.testZonedDateTime();
  7. }
  8. public void testZonedDateTime(){
  9. // 获取当前时间日期
  10. ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
  11. System.out.println("date1: " + date1);
  12. ZoneId id = ZoneId.of("Europe/Paris");
  13. System.out.println("ZoneId: " + id);
  14. ZoneId currentZone = ZoneId.systemDefault();
  15. System.out.println("当期时区: " + currentZone);
  16. }
  17. }

九、JavaScript引擎Nashorn

从 JDK 1.8 开始,Nashorn取代Rhino(JDK 1.6, JDK1.7) 成为 Java 的嵌入式 JavaScript 引擎。Nashorn 完全支持 ECMAScript 5.1 规范以及一些扩展,它使用基于 JSR 292 的新语言特性,其中包含在 JDK 7 中引入的 invokedynamic,将 JavaScript 编译成 Java 字节码,与先前的 Rhino 实现相比,这带来了 2 到 10倍的性能提升。

Nashorn的主要用途:允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。

十、Base64

在Java 8中,Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解码器。

除了这十大新特性之外,还有另外的一些新特性:

  • 更好的类型推测机制:Java 8在类型推测方面有了很大的提高,这就使代码更整洁,不需要太多的强制类型转换了。

  • 编译器优化:Java 8将方法的参数名加入了字节码中,这样在运行时通过反射就能获取到参数名,只需要在编译时使用-parameters参数。

  • 并行(parallel)数组:支持对数组进行并行处理,主要是parallelSort()方法,它可以在多核机器上极大提高数组排序的速度。

  • 并发(Concurrency):在新增Stream机制与Lambda的基础之上,加入了一些新方法来支持聚集操作。

  • Nashorn引擎jjs:基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。

  • 类依赖分析器jdeps:可以显示Java类的包级别或类级别的依赖。

  • JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53004?site
推荐阅读
相关标签
  

闽ICP备14008679号