java|史上最全jdk新特性总结,涵盖jdk8到jdk15

前言 在本文中,我将描述自第8版以来Java最重要且对开发人员友好的功能。为什么会有这样的主意?在Web上,您可以找到许多文章,其中包含每种Java版本的新功能列表。但是,由于缺少文章,因此无法简要概述自第8版以来最重要的更改。好的,但是为什么是第8版?令人惊讶的是,它仍然是最常用的Java版本。即使我们已经到了Java 16发行版的前夕果。如您所见,超过46%的响应者仍在生产中使用Java 8。相比之下,只有不到10%的响应者使用Java 12或更高版本。
java|史上最全jdk新特性总结,涵盖jdk8到jdk15
文章图片


java版本使用占比
那接下来咋们从JDK8到JDK15,给大家介绍新的JDK提供给咋们的新特性!
JDK8

  1. Lambda表达式
最直接作用就是减少代码,代码直接减少50%+,显得非常简洁
//使用java匿名内部类 Comparator cpt = new Comparator() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; TreeSet set = new TreeSet<>(cpt); System.out.println("========================="); //使用JDK8 lambda表达式 Comparator cpt2 = (x,y) -> Integer.compare(x,y); TreeSet set2 = new TreeSet<>(cpt2);

// java7中筛选产品为nike的 publicList filterProductByColor(List list){ List prods = new ArrayList<>(); for (Product product : list){ if ("nike".equals(product.getName())){ prods.add(product); } } return prods; }// 使用 lambda publicList filterProductByPrice(List list){ return list.stream().filter(p->"nike".equals(p.getName())).collect(Collectors.toList()); }

  1. 函数式接口
位于java.util.function包下,下面介绍最常用的几个
  • Predicate
接收一个值返回boolean
Predicate p = t->true;

  • Supplier
无接受参数返回一个值
Supplier s = () -> new T();

  • Consumer
接受一个参数无返回值
Consumer c = c -> System.out.println(s);

  • Function
接受参数T 返回参数R
Function f = c -> String.valueof(c);

  • 其他还有一些 BiFunction,BiConsumer,DoubleSupplier等大家有兴趣自己去阅读下源码
  1. 方法引用
  • 静态引用: 格式:Class::static_method
List list = Arrays.asList("a","b","c"); list.forEach(str -> System.out.print(str)); list.forEach(System.out::print)

  • 构造器调用 构造器方法引用格式:Class::new,调用默认构造器
List list = Arrays.asList("a","b","c"); List list.stream().map(Test::new).collect(Collectors.toList()); public class Test{ private final String desc; public Test(String desc){ this.desc=desc; } }

  • 方法调用 格式:instance::method
List list = Arrays.asList("a","b","c"); Test test = new Test(); List list.stream().map(test::toAdd).collect(Collectors.toList()); public class Test{ private final String desc; public Test(String desc){ this.desc=desc; }public String toAdd(String desc){ return desc+"add"; } }

  1. Stream API
// 使用jdk1.8中的Stream API进行集合的操作 @Test public void test(){// 循环过滤元素 proList.stream() .fliter((p) -> "红色".equals(p.getColor())) .forEach(System.out::println); // map处理元素然后再循环遍历 proList.stream() .map(Product::getName) .forEach(System.out::println); // map处理元素转换成一个List proList.stream() .map(Product::getName) .collect(Collectors.toList()); }

  1. 接口中的默认方法和静态方法
public interface ProtocolAdaptor {ProtocolAdaptor INSTANCE = DynamicLoader.findFirst(ProtocolAdaptor.class).orElse(null); default ProtocolAdaptor proxy() { return (ProtocolAdaptor) Proxy.newProxyInstance(ProtocolAdaptor.class.getClassLoader(), new Class[]{ProtocolAdaptor.class}, (proxy, method, args) -> intercept(method, args)); } }

  1. Optional
用于处理对象空指针异常:
public String getDesc(Test test){ return Optional.ofNullable(test) .map(Test::getDesc).else(""); }

JDK9
  • 收集工厂方法
借助Java 9的一项新功能,即集合工厂方法,您可以轻松地使用预定义的数据创建不可变的集合。您只需要在特定集合类型上使用of方法。
List fruits = List.of("apple", "banana", "orange"); Map numbers = Map.of(1, "one", 2,"two", 3, "three");

在Java 9之前,您可以使用Collections,但这绝对是一种更复杂的方法。
public List fruits() { List fruitsTmp = new ArrayList<>(); fruitsTmp.add("apple"); fruitsTmp.add("banana"); fruitsTmp.add("orange"); return Collections.unmodifiableList(fruitsTmp); }public Map numbers() { Map numbersTmp = new HashMap<>(); numbersTmp.put(1, "one"); numbersTmp.put(2, "two"); numbersTmp.put(3, "three"); return Collections.unmodifiableMap(numbersTmp); }

同样,仅从ArrayList对象表创建即可使用Arrays.asList(...)method。
public List fruitsFromArray() { String[] fruitsArray = {"apple", "banana", "orange"}; return Arrays.asList(fruitsArray);

  • 接口中的私有方法
从Java 8开始,您可以在接口内部使用公共默认方法。但是仅从Java 9开始,由于接口中的私有方法,您将能够充分利用此功能。
ublic interface ExampleInterface {private void printMsg(String methodName) { System.out.println("Calling interface"); System.out.println("Interface method: " + methodName); }default void method1() { printMsg("method1"); }default void method2() { printMsg("method2"); } }

JDK10 从Java 9和Java 10开始,有几种用于Optional的有用方法。其中最有趣的两个是orElseThrow和ifPresentOrElse。如果没有值,则使用该orElseThrow方法抛出NoSuchElementException。否则,它返回一个值。
public Person getPersonById(Long id) { Optional personOpt = repository.findById(id); return personOpt.orElseThrow(); }

因此,您可以避免将带参数的if语句与isPresentmethod一起使用。
public Person getPersonByIdOldWay(Long id) { Optional personOpt = repository.findById(id); if (personOpt.isPresent()) return personOpt.get(); else throw new NoSuchElementException(); }

第二种有趣的方法是ifPresentOrElse。如果存在一个值,它将使用该值执行给定的操作。否则,它将执行给定的基于空的操作。
public void printPersonById(Long id) { Optional personOpt = repository.findById(id); personOpt.ifPresentOrElse( System.out::println, () -> System.out.println("Person not found") ); }

在Java 8中,我们可以if-else直接与isPresent方法一起使用。
public void printPersonByIdOldWay(Long id) { Optional personOpt = repository.findById(id); if (personOpt.isPresent()) System.out.println(personOpt.get()); else System.out.println("Person not found"); }

JDK 10 && JDK 11 从Java 10开始,您可以声明没有其类型的局部变量。您只需要定义var关键字而不是类型。从Java 11开始,您还可以将其与lambda表达式一起使用,如下所示。
public String sumOfString() { BiFunction func = (var x, var y) -> x + y; return func.apply("abc", "efg"); }

JDK 12 使用Switch表达式,您可以定义多个case标签并使用箭头返回值。此功能自JDK 12起可用。它使Switch表达式真正更易于访问。
public String newMultiSwitch(int day) { return switch (day) { case 1, 2, 3, 4, 5 -> "workday"; case 6, 7 -> "weekend"; default -> "invalid"; }; }

对于低于12的Java,相同的示例要复杂得多。
public String oldMultiSwitch(int day) { switch (day) { case 1: case 2: case 3: case 4: case 5: return "workday"; case 6: case 7: return "weekend"; default: return "invalid"; } }

JDK 13 文本块是多行字符串文字,它避免使用转义序列,并以可预测的方式自动设置字符串格式。它还使开发人员可以控制字符串的格式。从Java 13开始,文本块可用作预览功能。它们以三个双引号(""")开头。让我们看看我们如何轻松地创建和格式化JSON消息。
public String getNewPrettyPrintJson() { return """ { "firstName": "Piotr", "lastName": "Mińkowski" } """; }

创建Java 13之前的相同JSON字符串要复杂得多。
public String getOldPrettyPrintJson() { return "{\n" + "\"firstName\": \"Piotr\",\n" + "\"lastName\": \"Mińkowski\"\n" + "}"; }

JDK14 使用Records,您可以定义不可变的纯数据类(仅限getter)。它会自动创建toString,equals和hashCode方法。实际上,您只需要定义如下所示的字段即可。
public record Person(String name, int age) {}

具有类似功能的类如record包含字段,构造函数,getter和实施toString,equals以及hashCode方法。
public class PersonOld {private final String name; private final int age; public PersonOld(String name, int age) { this.name = name; this.age = age; }public String getName() { return name; }public int getAge() { return age; }@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PersonOld personOld = (PersonOld) o; return age == personOld.age && name.equals(personOld.name); }@Override public int hashCode() { return Objects.hash(name, age); }@Override public String toString() { return "PersonOld{" + "name='" + name + '\'' + ", age=" + age + '}'; }}

JDK15 使用密封类功能,您可以限制超类的使用。使用new关键字,sealed您可以定义哪些其他类或接口可以扩展或实现当前类。
public abstract sealed class Pet permits Cat, Dog {}

允许的子类必须定义一个修饰符。如果您不想允许任何其他扩展名,则需要使用final关键字。
public final class Cat extends Pet {}

另一方面,您可以打开扩展类。在这种情况下,应使用non-sealed修饰符。
public non-sealed class Dog extends Pet {}

【java|史上最全jdk新特性总结,涵盖jdk8到jdk15】当然,下面的可见声明是不允许的。
public final class Tiger extends Pet {}

    推荐阅读