Java 简明教程

Java 8 - New Features

JAVA 8 是 JAVA programming language开发的一次重大功能发布。其初始版本于 2014 年 3 月 18 日发布。随着 Java 8 的发布,Java 提供对函数式编程、新 JavaScript 引擎、日期时间操作的新 API、新的流式传输 API 等的支持。

以下是 Java 8 中支持的新功能的列表:

Lambda Expressions

Lambda expression是 Java 中引入的最大功能之一。一个 lambda 表达式促进了 Java 中的函数式编程。一个 lambda 表达式基于函数式接口的原理运作。一个函数式接口是一个只实现一个方法的接口。一个 lambda 表达式提供了函数式接口方法的一个实现。

lambda 表达式极大地简化了函数式编程,并且无需样板代码就能让代码变得可读。lambda 表达式可以推断出所用参数的类型,并且可以在没有 return 关键字的情况下返回一个值。对于简单的单语句方法,甚至可以消除大括号。

Example - Using Lambda Expressions

下面的示例展示了 lambda 表达式的用法。lambda 表达式最适合与函数接口(具有单个抽象方法的接口)配合使用。我们定义了一个带有单个方法 operate 的接口 Calculator,该方法可以接受两个参数并返回一个值。在 main 方法中,我们使用匿名函数先实现 Calculator 接口,然后再使用 lambda 表达式实现。在两种情况下都调用 operate() 方法打印结果,并打印结果。

package com.tutorialspoint;

public class Tester {

   public static void main(String[] args) {
      // Interface implementation using anonymous class
      Calculator sum = new Calculator() {
         @Override
         public int operate(int a, int b) {
            return a + b;
         }
      };
      int result = sum.operate(2,3);
      System.out.println(result);

      // Interface implementation using lambda expression
      Calculator sum1 = (a,b) -> a + b;
      result = sum1.operate(2,3);
      System.out.println(result);
   }

   interface Calculator {
      int operate(int a, int b);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

5
5

Method References

Method reference是一种调用方法、静态方法甚至构造函数的简短且简洁的方式,没有任何冗长的语法。方法引用可通过其名称来指代方法,甚至无需指定参数。参数由 lambda 表达式传递。使用“::”符号描述一个方法引用。

可以使用以下语法引用静态方法:

<<class-name>>::methodName

可以使用以下语法引用实例方法:

<<object-name>>::methodName

我们可以使用以下语法调用构造函数:

<<class-name>>::new

Example - Using Method References

在此示例中,我们使用了一个静态方法 compare 和一个实例方法 compareTo 来对两个整数数组列表进行排序。我们使用方法引用来表示静态方法和实例方法。

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class Tester {
   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(1,2,4,9,8,7,3);
      System.out.println("Sorted using static method reference");
      // Use static method compare
      numbers = numbers.stream().sorted(Integer::compare).toList();
      System.out.println(numbers);

      numbers = Arrays.asList(1,2,4,9,8,7,3);
      System.out.println("Sorted using instance method reference" );
      // Use instance method compareTo
      numbers = numbers.stream().sorted(Integer::compareTo).toList();

      System.out.println(numbers);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

Sorted using static method reference
[1, 2, 3, 4, 7, 8, 9]
Sorted using instance method reference
[1, 2, 3, 4, 7, 8, 9]

Default Methods

在 Java 8 之前,一个接口只能具有抽象方法。随着 Java 8 的推出,lambda 表达式被引入。现在,为了向后兼容, default method功能被添加进来,这样旧的 interfaces可以在不修改其实现的情况下利用 lambda 表达式。

例如, ListCollection interfaces没有“forEach”方法声明。因此,添加这样的方法只会破坏集合框架的实现。Java 8 引入了默认方法,这样 List/Collection 接口就可以拥有 forEach 方法的默认实现,并且实现这些接口的类不需要实现相同的接口。

Syntax

以下是 Java 中接口中默认方法的语法 −

public interface vehicle {
   default void message() {
      System.out.println("I am a vehicle!");
   }
}

Example - Using Default Method

在此示例中,我们创建了一个带有默认方法的接口。在实现类中,此消息未实现,用于打印消息。

package com.tutorialspoint;

interface vehicle {
   // default method must have an implementation
   default void message() {
      System.out.println("I am a vehicle!");
   }
}

// implementing class need not to implement the default method
// of an interface.
public class Tester implements vehicle {
   public static void main(String args[]) {
      Tester tester = new Tester();
      // implementing class can access the default method as its own method
      tester.message();
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

I am a vehicle!

Stream API

Stream API是一个新的抽象层,在 Java 8 中引入用于以声明性方式处理数据。一个流表示一个元素序列。一个流按顺序提供一组特定类型的元素。一个流按需获取/计算元素。它从不存储元素。

与需要显式迭代的集合不同,流支持诸如筛选、映射、限制、归约、查找、匹配等聚合操作,可以在提供的源元素上内部进行迭代。

Syntax

以下是一般语法,用于使用流

<<collection-instance>>.stream().<<non-terminal-operation()>>.<<non-terminal-operation()>>.<<terminal-operation()>>

Example - Using Stream

在此示例中,我们创建了一个字符串列表,其中一些条目为空。现在使用 stream API,我们筛选空字符串并对其进行计数。

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class Tester {
   public static void main(String args[]) {
      List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

      // get stream from list using stream() method
      // then apply filter
      // lastly count the result of filter
      long count = strings.stream().filter(string->string.isEmpty()).count();
      System.out.println("Empty Strings: " + count);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

Empty Strings: 2

Optional Class

Optional class功能在 java 8 中引入,用于以编程方式处理空指针异常场景,使程序更简洁,更不容易出错。每当空对象引用被用来从其中获取值或调用其方法时,就会发生空指针异常。随着程序规模的增大,处理空指针异常可能发生的所有情况非常繁琐。

可选类实例提供了一个包含许多实用方法的包装器,例如,如果基础值为空,则获取替代值;检查对象引用是否为空,等等。

Example - Using Optional Class

在此示例中,我们创建了两个可选类实例,使用 oofNullable() 方法,该方法允许将基础对象传递为空,然后使用 orElse() 方法检索值,该方法如果基础对象为空则返回默认值。

package com.tutorialspoint;

import java.util.Optional;

public class Tester {
   public static void main(String args[]) {
      // case 1: Optional is having null as underlying value
      Optional<Integer> valueOptional = Optional.ofNullable(null);

      // case 2:  Optional is having not null as underlying value
      Optional<Integer> valueOptional1 = Optional.ofNullable(Integer.valueOf(10));

      // orElse will return -1 being default value
      Integer value = valueOptional.orElse(Integer.valueOf(-1));

      System.out.println(value);

      //  orElse will return the underlying value
      Integer value1 = valueOptional1.orElse(Integer.valueOf(-1));

      System.out.println(value1);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

-1
10

New Date Time API

Java 8 引入了 new Date Time API,它线程安全、时区就绪,并有多个用于处理日期操作的直接方法。早期的日期时间 API 不是线程安全的,并且在处理日期时可能会出现并发问题。新的日期时间 API 正在使用不可变构造函数,没有 setter 方法,因此使 API 更安全。新的 API 在考虑时区、特定于域的要求的情况下进行设计。

Java 8 在 java.time 包下引入了新的日期时间 API。以下是 java.time 包中引入的一些重要类。

  1. Local − 简化的日期时间 API,没有时区处理的复杂性。

  2. Zoned − 处理不同时区的专业日期-时间 API。

Example - Using Date Time APIs

在此示例中,我们创建了两个可选类实例,使用 oofNullable() 方法,该方法允许将基础对象传递为空,然后使用 orElse() 方法检索值,该方法如果基础对象为空则返回默认值。

package com.tutorialspoint;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZonedDateTime;

public class Tester {
   public static void main(String args[]) {
      // Get the current date and time
      LocalDateTime currentTime = LocalDateTime.now();
      System.out.println("Current DateTime: " + currentTime);

      LocalDate date1 = currentTime.toLocalDate();
      System.out.println("date1: " + date1);

      Month month = currentTime.getMonth();
      int day = currentTime.getDayOfMonth();
      int seconds = currentTime.getSecond();

      System.out.println("Month: " + month +", day: " + day +", seconds: " + seconds);

      ZonedDateTime date2 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
      System.out.println("date2: " + date2);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

Current DateTime: 2024-03-07T10:29:15.650806
date1: 2024-03-07
Month: MARCH, day: 7, seconds: 15
date2: 2007-12-03T09:45:30+05:00[Asia/Karachi]

Nashorn JavaScript Engine

Nashorn是一个非常强大、高效的 Javascript 引擎,引入了它来替代现有的 javascript 引擎 Rhino。Nashorn 引擎被吹捧为快 2 到 10 倍,因为它可以直接将 JavaScript 代码编译为字节码。Nashorn 引擎允许在 Java 文件中执行 JavaScript 代码,我们甚至可以在 JavaScript 代码片段中执行 java 代码。使用 Nashorn 引擎,引入了一个命令行工具 jjs 来在命令行工具中运行 javascript。

Execute JavaScript Directly in Command Prompt

打开控制台,输入 jjs 并按 Enter 键。jjs 工具将打开一个交互式会话。在 jjs 会话打开后,我们就可以执行 javascript 代码。完成后,输入 quit() 并按 Enter 键退出 jjs 交互式会话,返回命令提示符。

Example

C:\JAVA>jjs
jjs> print("Hello, World!")
Hello, World!
jjs> quit()
>>
C:\JAVA>

Example - Using Javascript code within Java Code

Java 从 Java 6 开始具有 ScriptEngineManager 类,在该示例中用于将 javascript 引擎加载为 ScriptEngine 实例。一旦引擎加载到 java 代码中,我们可以调用*eval()*方法来在 Java 中评估一个 JavaScript 代码。我们甚至可以在 javascript 代码片段中使用 Java variable(s)

package com.tutorialspoint;

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

public class Tester {

   public static void main(String args[]) {
      // create the script engine manager
      ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
      // load the Nashorn javascript engine
      ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");

      String message = "This is a message";
      String expression = "10 + 2";
      Integer result = null;

      try {
         // call the javascript function, pass a java variable
         nashorn.eval("print('" + message + "')");
         // call the javascript function and get the result back in java
         result = (Integer) nashorn.eval(expression);

      } catch(ScriptException e) {
         System.out.println("Error executing script: "+ e.getMessage());
      }
      System.out.println(result.toString());
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

This is a message
12

Nashorn 引擎在 Java 11 中已弃用,在 Java 15 中已移除,由 GraalVM javascript 引擎取代。