注意:本文不是介绍每个版本所有的新特性,只介绍个人感觉重要的新特性。
先上图:
除了Java 7经过了五年,Java 8经过了三年。在此之后,就是每六个月发布一次新版本。但不是每个版本都是 LTS(Long-Term-Support)。按照Oracle的计划,每三年会有一个LTS版本。最近的三个LTS版本是 Java 8、Java 11和Java 17。所以大家生产环境时,请选择Java8、Java11或Java17其中的一个。
JDK和OpenJDK有啥区别呢?其实OpenJDK是2007年由Sun(现在是Oracle)发布的,是Oracle JDK的开源实现版本,以GPL协议发布。早在JDK 7的时候,Sun JDK就是在Open JDK7基础上发布的,只是替换了少量的源码。在Sun被Oracle收购之后,Sun SDK被称为Oracle JDK,是基于Oracle Binary Code License Agreement协议。本质上,Oracle JDK是基于OpenJDK构建的,技术上基本上没有差异。
下面通过代码来整体说明下:
1、Lambda与Stream
Java 7之后个人感觉最重要的新特性,一定要会,需要掌握。Java 8推出后,其后多个版本对其进行了功能增强,比如Java 11中Lambda可以使用var,Java 16也对其进行了一些写法的增强,其中也要注意双冒号的使用。
StreamTest.java
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StreamTest { public static void main(String[] args) { // lambdaBasic(); // streamMap(); // streamReduce(); // streamFilter(); streamOtherDemo(); } /** * lambda基本使用 */ private static void lambdaBasic() { // innerClass new Thread(new Runnable() { @Override public void run() { System.out.println("The old runable now is using!"); } }).start(); new Thread(() -> System.out.println("lambda instead")).start(); // Lambda List List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API"); for (String feature : features) { System.out.println(feature); } System.out.println("-----------------"); features.forEach(feature -> System.out.println(feature)); System.out.println("-----------------"); // 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示 features.forEach(System.out::println); } /** * Stream Map */ private static void streamMap() { List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); // for (Integer cost : costBeforeTax) { // double price = cost + 0.12 * cost; // System.out.println(price); // } System.out.println("-----------------"); // map函数可以说是函数式编程里最重要的一个方法了。map的作用是将一个对象变换为另外一个 costBeforeTax.stream().map(n->n+n*15).forEach(System.out::println); System.out.println("-----------------"); // reduce与map一样,也是函数式编程里最重要的几个方法之一。。。map的作用是将一个对象变为另外一个,而reduce实现的则是将所有值合并为一个 double result = costBeforeTax.stream().map(m -> m * 1.1).reduce((sum, m) -> sum + m).get(); // 110 + 220 + 330 + 440 + 550 System.out.println(result); System.out.println("-----------------"); List<String> mapTest = Arrays.asList("abcDefg", "higKLsfsd", "test"); String joinTest = mapTest.stream().map(str -> str.toUpperCase()).collect(Collectors.joining(", ")); System.out.println(joinTest); } /** * Stream Reduce */ private static void streamReduce() { // List<Double> cost = Arrays.asList(10.0, 20.0, 30.0); // double sum = 0; // for (double each : cost) { // each += each * 0.05; // sum += each; // } // System.out.println(sum); // Reduce List<Double> cost = Arrays.asList(10.0, 20.0, 30.0); double allCost = cost.stream().map(x -> x + x * 0.05).reduce((sum, x) -> sum + x).get(); System.out.println(allCost); } /** * stream filter demo */ private static void streamFilter() { List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); // for (int value : costBeforeTax) { // if (value >= 300) { // System.out.println(value); // } // } costBeforeTax.stream().filter(n -> n >= 300).forEach(System.out::println); } /** * stream Other Demo */ private static void streamOtherDemo() { // 转大写后合并 List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.", "Canada"); String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); System.out.println(G7Countries); // 去重 List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4); List<Integer> distinct = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList()); System.out.printf("Original List : %s, Square Without duplicates : %s", numbers, distinct); } }
2、接口的默认实现、静态方法、私有方法
Java 7之后第二个重要的新特性,也需要掌握。Java 8中,接口可以有默认的实现和静态方法,Java 9中新增了接口里可以有私有的方法。
TestInterface.java
public interface TestInterface { /** * 接口的默认实现方法 * @return */ default String defaultMethod() { System.out.println(privateMethod()); return "Default Method"; } /** * 接口的静态方法 * @return */ static String staticMethod() { return "Static Method"; } /** * 接口的私有方法 (Java 9 特性) * @return */ private String privateMethod() { return "Private Method"; } /** * 一般的接口声明 * @return */ String normalMethod(); }
TestImplement.java
public class TestImplement implements TestInterface{ public static void main(String[] args) { // 静态方法调用 System.out.println(TestInterface.staticMethod()); TestInterface test = new TestImplement(); // 默认方法调用 System.out.println(test.defaultMethod()); // 正常方法调用 System.out.println(test.normalMethod()); } @Override public String normalMethod() { return "Normal Method"; } }
3、Jigsaw模块化
Java 9推出的新特性,个人感觉有点类似于OSGI那种感觉。平常使用的并不多,但是解决了Java内部很多关于包管理的历史残留问题,意义很大。
4、其他特性
其他特性可能不如上面那些重要,主要是技巧性的。私下里大家可以多使用,提高代码的健壮性和使代码变得更优雅。
4.1、Optional解决空指针问题,Java 8新特性,主要是Optional一些API的使用
OptionalTest.java
// 常用方法 orElseGet 如果value为空,则抛出自定义异常。 System.out.println(new OptionalTest().getDefaultCity(null)); // 常用方法 orElseThrow 如果value为空,则调用Supplier实例返回一个默认值。 // System.out.println(Optional.ofNullable(null).orElseThrow(NullPointerException::new)); // Optional写法 System.out.println(new OptionalTest().getCityUsingOptional(new Person())); } public String getCityUsingOptional(Person person) { // 注意,这里的map是Optional的,不是Stream的map String city = Optional.ofNullable(person) .map(Person::getHouse) .map(House::getAddress) .map(Address::getCity).orElse("Unknown city Using Optional"); return city; } public String getDefaultCity(String city) { // 如果city为空,则调用getDefaultCity方法 return Optional.ofNullable(city).orElseGet(this::getDefaultCity2); } private String getDefaultCity2() { return "beijing"; } /** * 引发空指针 * @return */ public String getCity() { String city = new Person().getHouse().getAddress().getCity(); return city; } /** * 繁琐的写法 * @param person * @return */ public String getCity2(Person person) { if (person != null) { House house = person.getHouse(); if (house != null) { Address address = house.getAddress(); if (address != null) { String city = address.getCity(); return city; } } } return "unknown2"; } /** * 稍微优雅的写法 * @param person * @return */ public String getCity3(Person person) { String city = "unknown3"; if (person == null) { return city; } House house = person.getHouse(); if (house == null) { return city; } Address address = house.getAddress(); if (address == null) { return city; } return address.getCity(); } } class Person { private String name; private int age; private House house; public House getHouse() { return house; } } class House { private long price; private Address address; public Address getAddress() { return address; } } class Address { private String country; private String city; public String getCity() { return city; } }
4.2、日期时间API,Java8新特性,主要是LocalDate、LocalTime、LocalDateTime、Period等的使用
LocalDate.java
import java.time.*; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Locale; public class DateApi { public static void main(String[] args) { // LocalDateMethod(); // LocalTimeMethod(); // LocalDateTimeMethod(); // durationMethod(); // periodMethod(); // DateModifyMethod(); DateFormatMethod(); } /** * LocalDate */ public static void LocalDateMethod() { LocalDate localDate = LocalDate.of(2017, 1, 4); // 初始化一个日期:2017-01-04 int year = localDate.getYear(); System.out.println(year); // 年份:2017 Month month = localDate.getMonth(); System.out.println(month); // 月份:JANUARY int dayOfMonth = localDate.getDayOfMonth(); System.out.println(dayOfMonth); // 月份中的第几天:4 DayOfWeek dayOfWeek = localDate.getDayOfWeek(); System.out.println(dayOfWeek); // 一周的第几天:WEDNESDAY int length = localDate.lengthOfMonth(); System.out.println(length); // 月份的天数:31 boolean leapYear = localDate.isLeapYear(); System.out.println(leapYear); // 是否为闰年:false // 获取当前日期 // LocalDate now = LocalDate.now(); } /** * LocalTime */ public static void LocalTimeMethod() { LocalTime localTime = LocalTime.of(17, 23, 52); // 初始化一个时间:17:23:52 int hour = localTime.getHour(); System.out.println(hour); // 时:17 int minute = localTime.getMinute(); System.out.println(minute); // 分:23 int second = localTime.getSecond(); System.out.println(second); // 秒:52 } /** * LocalDateTime LocalDate和LocalTime的结合体 */ public static void LocalDateTimeMethod() { LocalDateTime ldt1 = LocalDateTime.of(2017, Month.JANUARY, 4, 17, 23, 52); // 组合 LocalDate localDate = LocalDate.of(2017, Month.JANUARY, 4); LocalTime localTime = LocalTime.of(17, 23, 52); LocalDateTime ldt2 = localDate.atTime(localTime); // 拆分 LocalDate date = ldt1.toLocalDate(); LocalTime time = ldt1.toLocalTime(); } /** * 时间差 */ public static void durationMethod() { LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 5, 10, 7, 0); // 2017-01-05 10:07:00 LocalDateTime to = LocalDateTime.of(2017, Month.FEBRUARY, 5, 10, 7, 0); // 2017-02-05 10:07:00 Duration duration = Duration.between(from, to); // 表示从 2017-01-05 10:07:00 到 2017-02-05 10:07:00 这段时间 long days = duration.toDays(); // 这段时间的总天数 System.out.println(days); long hours = duration.toHours(); // 这段时间的小时数 System.out.println(hours); long minutes = duration.toMinutes(); // 这段时间的分钟数 System.out.println(minutes); long seconds = duration.getSeconds(); // 这段时间的秒数 System.out.println(seconds); long milliSeconds = duration.toMillis(); // 这段时间的毫秒数 System.out.println(milliSeconds); long nanoSeconds = duration.toNanos(); // 这段时间的纳秒数 System.out.println(nanoSeconds); } /** * 时间差 和Duration类似,区别在于Period是以年月日来衡量一个时间段 */ public static void periodMethod() { Period period = Period.between( LocalDate.of(2017, 1, 9), LocalDate.of(2021, 4, 5)); System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); } /** * 日期加减 */ public static void DateModifyMethod() { LocalDate date = LocalDate.of(2017, 1, 5); LocalDate date1 = date.withYear(2016); System.out.println(date1); // 修改为 2016-01-05 LocalDate date2 = date.withMonth(2); System.out.println(date2); // 修改为 2017-02-05 LocalDate date3 = date.withDayOfMonth(1); System.out.println(date3); // 修改为 2017-01-01 LocalDate date4 = date.plusYears(1); System.out.println(date4); // 增加一年 2018-01-05 LocalDate date5 = date.minusMonths(2); System.out.println(date5); // 减少两个月 2016-11-05 LocalDate date6 = date.plus(5, ChronoUnit.DAYS); System.out.println(date6); // 增加5天 2017-01-10 } /** * 日期格式化 */ public static void DateFormatMethod() { LocalDateTime dateTime = LocalDateTime.now(); String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE); System.out.println(strDate1); // 20220509 String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE); System.out.println(strDate2); // 2022-05-09 String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME); System.out.println(strDate3); // 22:02:04.875902 String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); System.out.println(strDate4); // 2022-05-09 String strDate5 = dateTime.format(DateTimeFormatter.ofPattern("今天是:YYYY年 MMMM dd日 E", Locale.CHINESE)); System.out.println(strDate5); // 今天是:2022年 五月 09日 周一 } }
4.3、Java 9的创建不可变集合,Java 10的var推导类型,Java 11的新增一些字符串处理方法
Java11Demo.java
import java.util.List; public class Java11Demo { public static void main(String[] args) { // Java 9,创建不可变集合 List list = List.of("A","B","C"); list.forEach(System.out::println); // Java 10,推导类型,目前仅用于局部变量以及 for 循环变量声明中 var test = "123"; System.out.println(test); // Java11,新增一些字符串处理方法 System.out.println(" ".isBlank()); // strip和trim区别:trim()可以去除字符串前后的半角空白字符,strip()可以去除字符串前后的全角和半角空白字符 System.out.println(" Javastack ".stripTrailing()); // " Javastack" 去后空格 System.out.println(" Javastack ".stripLeading()); // "Javastack " 去前空格 System.out.println(" Javastack ".strip()); // "Javastack" 去空格 } }
4.4、record 记录类,Java 14新特性,有点像Lombok插件,但是又不完全一样,主要用于在对象之间传递不可变数据,常用语编写POJO类
User1.java
public class User1 { String name; Integer age; public User1(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User1[" + "name='" + name + '\'' + ", age=" + age + ']'; } }
User2.java
package java17.record; /** * Java 14: record 记录类, 也可以重写equals() hashCode() toString()方法 * * 在许多 java 应用程序中,在对象之间传递不可变数据是很常见的,常用语编写POJO类。 * 在Java14之前,我们需要创建一个带有字段和方法的类, * 随着Java14的发布,我们现在可以使用 record 关键字来解决这些问题。 * * * 1. 每个字段是private并且是final的 * 2. 每个字段需要有个getter方法 * 3. 需要有个构造方法并且参数包含所有字段 * 4. 还需要包含所有字段的equals方法,以及对应的hashcode方法和toString方法 */ public record User2(String name, Integer age) { // @Override // public String toString() { // return "User2[" + // "name='" + name + '\'' + // ", age=" + age + // ']'; // } // @Override // public boolean equals(Object obj) { // return false; // } // @Override // public int hashCode() { // return 0; // } }
RecordTest.java
public class RecordTest { public static void main(String[] args) { //传统的方式 User1 user1 = new User1("大阳", 1990); System.out.println(user1); // Java 14 record 记录类 User2 user2 = new User2("月亮", 2000); System.out.println(user2.age()); System.out.println(user2); } }
4.5、Text Blocks文本块,Java 13新特性,无需多说,直接上代码
TextBlocksDemo.java
public class TextBlocksDemo { public static void main(String[] args) { /* 使用特殊的语法进行文本块的开始和结束:""" 注意:在第一个开引号"""后需要进行换行操作,否则编译会报错。 */ String htmlContent = """ <!DOCTYPE html> <html> <head> <title>Example</title> </head> <body> <p>This is an example of a simple HTML page with one paragraph.</p> </body> </html> """; System.out.println(htmlContent); String sqlContent = """ SELECT NAME, AGE FROM EMP WHERE NAME='CLX'; """; System.out.println(sqlContent); } }
4.6、Switch与yield,Java 12与13的新特性,引入了 yield 语句来从块中返回值,而不是使用 break。这意味着,switch 表达式(返回值)应该使用 yield,而 switch 语句(不返回值)应该使用 break
SwitchDemo.java
public class SwitchDemo { public static void main(String[] args) { /* Java 12与13的新特性,它引入了 yield 语句来从块中返回值,而不是使用 break。 这意味着,switch 表达式(返回值)应该使用 yield,而 switch 语句(不返回值)应该使用 break */ System.out.println(switchMethod1(1)); System.out.println(switchMethod1(5)); System.out.println(switchMethod2(2)); System.out.println(switchMethod2(10)); } private static String switchMethod1(int value) { return switch(value) { case 1: yield "value1"; case 2: yield "value2"; default: { yield "test"; } }; } private static String switchMethod2(int value) { String returnValue = switch (value) { case 1 -> { yield "value1"; } case 2 -> { yield "value2"; } default -> { yield "test"; } }; return returnValue; } }
4.7、instanceof模式识别,Java 14新特性,instanceof 如果是String类型,直接将其值赋给变量str,该str的作用域仅限于if语句
InstanceOfDemo.java
public class InstanceOfDemo { public static void main(String[] args) { Object obj1 = "好好学习,天天向上"; // Object obj1 = 1; // 传统代码 if (obj1 instanceof String) { String str = (String) obj1; System.out.println(str); } else { System.out.println("不是字符串类型"); } // Java 14: instanceof 如果是String类型,直接将其值赋给变量str,该str的作用域仅限于if语句 if (obj1 instanceof String str) { System.out.println(str); } else { System.out.println("不是字符串类型"); } } }
4.8、sealed密封类,用的很少,Java 15新特性,密封类可以控制哪些模型、类等可以实现或扩展该类/接口。
Cat.java
final class Cat extends Animal { }
Dog.java
non-sealed class Dog extends Animal { }
Animal.java
/** * Java 15 sealed class: 密封类可以控制哪些模型、类等可以实现或扩展该类/接口。 * 允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。 * 而且 sealed 修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰, * 且只能是 final 、sealed 、non-sealed 三者之一 * * 在Java中如果想让一个类不能被继承和修改,这时我们应该使用final关键字对类进行修饰, * 不过这种要么可以继承,要么不能继承的机制不够灵活, * 有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承。 */ public sealed class Animal permits Cat, Dog { }
Tiger.java
/** * 报错,无权限继承 */ //non-sealed class Tiger extends Animal { //}
4.9、JShell,Java9新特性,提供命令行输出,也不怎么常用。
还有一些没有特殊说明,大家可以结合上面思维导图去看。整体上就介绍那么多,希望对大家有所帮助。
【文章转自高防服务器 http://www.558idc.com 复制请保留原URL】