Java 7~14各个版本新特性详解

 

Java 7

特性列表

  • switch中添加对String类型的支持
  • 数字字面量的改进 / 数值可加下划
  • 异常处理(捕获多个异常) try-with-resources
  • 增强泛型推断
  • JSR203 NIO2.0(AIO)新IO的支持
  • JSR292与InvokeDynamic指令
  • Path接口、DirectoryStream、Files、WatchService(重要接口更新)
  • fork/join framework

1、switch中添加对String类型的支持

 String title = ""; 
 switch (gender) { 
 case "男": 
 title = name + " 先生"; 
 break; 
 case "女": 
 title = name + " 女士"; 
 break; 
 default: 
 title = name; 
 } 
 return title; 
}

 

编译器在编译时先做处理:

①case仅仅有一种情况。直接转成if。

②假设仅仅有一个case和default,则直接转换为if…else…。

③有多个case。先将String转换为hashCode,然后相应的进行处理,JavaCode在底层兼容Java7曾经版本号。

2、数字字面量的改进

Java7前支持十进制(123)、八进制(0123)、十六进制(0X12AB)

Java7添加二进制表示(0B11110001、0b11110001)

数字中可加入分隔符

Java7中支持在数字量中间添加’_'作为分隔符。更直观,如(12_123_456)。下划线仅仅能在数字中间。编译时编译器自己主动删除数字中的下划线。

int one_million = 1_000_000;

3、异常处理(捕获多个异常) try-with-resources

 

catch子句能够同一时候捕获多个异常

public void testSequence() { 
 try { 
 Integer.parseInt("Hello"); 
 } 
 catch (NumberFormatException | RuntimeException e) { //使用'|'切割,多个类型,一个对象e 
 } 
 } 

try-with-resources语句

Java7之前须要在finally中关闭socket、文件、数据库连接等资源;

Java7中在try语句中申请资源,实现资源的自己主动释放(资源类必须实现java.lang.AutoCloseable接口,一般的文件、数据库连接等均已实现该接口,close方法将被自己主动调用)。

public void read(String filename) throws IOException { 
 try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { 
 StringBuilder builder = new StringBuilder(); 
 String line = null; 
 while((line=reader.readLine())!=null){ 
 builder.append(line); 
 builder.append(String.format("%n")); 
 } 
 return builder.toString(); 
 } 
 } 

4、增强泛型推断

Map<String, List<String>> map = new HashMap<String, List<String>>();

Java7之后可以简单的这么写

Map<String, List<String>> anagrams = new HashMap<>(); 

5、NIO2.0(AIO)新IO的支持

bytebuffer

public class ByteBufferUsage {
 public void useByteBuffer() {
 ByteBuffer buffer = ByteBuffer.allocate(32);
 buffer.put((byte)1);
 buffer.put(new byte[3]);
 buffer.putChar('A');
 buffer.putFloat(0.0f);
 buffer.putLong(10, 100L);
 System.out.println(buffer.getChar(4));
 System.out.println(buffer.remaining());
 }
 public void byteOrder() {
 ByteBuffer buffer = ByteBuffer.allocate(4);
 buffer.putInt(1);
 buffer.order(ByteOrder.LITTLE_ENDIAN);
 buffer.getInt(0); //值为16777216
 }
 public void compact() {
 ByteBuffer buffer = ByteBuffer.allocate(32);
 buffer.put(new byte[16]);
 buffer.flip();
 buffer.getInt();
 buffer.compact();
 int pos = buffer.position();
 }
 public void viewBuffer() {
 ByteBuffer buffer = ByteBuffer.allocate(32);
 buffer.putInt(1);
 IntBuffer intBuffer = buffer.asIntBuffer();
 intBuffer.put(2);
 int value = buffer.getInt(); //值为2
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) {
 ByteBufferUsage bbu = new ByteBufferUsage();
 bbu.useByteBuffer();
 bbu.byteOrder();
 bbu.compact();
 bbu.viewBuffer();
 }
}

filechannel

public class FileChannelUsage {
 public void openAndWrite() throws IOException {
 FileChannel channel = FileChannel.open(Paths.get("my.txt"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
 ByteBuffer buffer = ByteBuffer.allocate(64);
 buffer.putChar('A').flip();
 channel.write(buffer);
 }
 public void readWriteAbsolute() throws IOException {
 FileChannel channel = FileChannel.open(Paths.get("absolute.txt"), StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
 ByteBuffer writeBuffer = ByteBuffer.allocate(4).putChar('A').putChar('B');
 writeBuffer.flip();
 channel.write(writeBuffer, 1024);
 ByteBuffer readBuffer = ByteBuffer.allocate(2);
 channel.read(readBuffer, 1026);
 readBuffer.flip();
 char result = readBuffer.getChar(); //值为'B'
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) throws IOException {
 FileChannelUsage fcu = new FileChannelUsage();
 fcu.openAndWrite();
 fcu.readWriteAbsolute();
 }
}

6、JSR292与InvokeDynamic

JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform,支持在JVM上运行动态类型语言。在字节码层面支持了InvokeDynamic。

public class ThreadPoolManager {
 private final ScheduledExecutorService stpe = Executors
 .newScheduledThreadPool(2);
 private final BlockingQueue<WorkUnit<String>> lbq;
 public ThreadPoolManager(BlockingQueue<WorkUnit<String>> lbq_) {
 lbq = lbq_;
 }
 public ScheduledFuture<?> run(QueueReaderTask msgReader) {
 msgReader.setQueue(lbq);
 return stpe.scheduleAtFixedRate(msgReader, 10, 10, TimeUnit.MILLISECONDS);
 }
 private void cancel(final ScheduledFuture<?> hndl) {
 stpe.schedule(new Runnable() {
 public void run() {
 hndl.cancel(true);
 }
 }, 10, TimeUnit.MILLISECONDS);
 }
 /**
 * 使用传统的反射api
 */
 public Method makeReflective() {
 Method meth = null;
 try {
 Class<?>[] argTypes = new Class[]{ScheduledFuture.class};
 meth = ThreadPoolManager.class.getDeclaredMethod("cancel", argTypes);
 meth.setAccessible(true);
 } catch (IllegalArgumentException | NoSuchMethodException
 | SecurityException e) {
 e.printStackTrace();
 }
 return meth;
 }
 /**
 * 使用代理类
 * @return
 */
 public CancelProxy makeProxy() {
 return new CancelProxy();
 }
 /**
 * 使用Java7的新api,MethodHandle
 * invoke virtual 动态绑定后调用 obj.xxx
 * invoke special 静态绑定后调用 super.xxx
 * @return
 */
 public MethodHandle makeMh() {
 MethodHandle mh;
 MethodType desc = MethodType.methodType(void.class, ScheduledFuture.class);
 try {
 mh = MethodHandles.lookup().findVirtual(ThreadPoolManager.class,
 "cancel", desc);
 } catch (NoSuchMethodException | IllegalAccessException e) {
 throw (AssertionError) new AssertionError().initCause(e);
 }
 return mh;
 }
 public static class CancelProxy {
 private CancelProxy() {
 }
 public void invoke(ThreadPoolManager mae_, ScheduledFuture<?> hndl_) {
 mae_.cancel(hndl_);
 }
 }
}

调用invoke

public class ThreadPoolMain {
 /**
 * 如果被继承,还能在静态上下文寻找正确的class
 */
 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 private ThreadPoolManager manager;
 public static void main(String[] args) {
 ThreadPoolMain main = new ThreadPoolMain();
 main.run();
 }
 private void cancelUsingReflection(ScheduledFuture<?> hndl) {
 Method meth = manager.makeReflective();
 try {
 System.out.println("With Reflection");
 meth.invoke(hndl);
 } catch (IllegalAccessException | IllegalArgumentException
 | InvocationTargetException e) {
 e.printStackTrace();
 }
 }
 private void cancelUsingProxy(ScheduledFuture<?> hndl) {
 CancelProxy proxy = manager.makeProxy();
 System.out.println("With Proxy");
 proxy.invoke(manager, hndl);
 }
 private void cancelUsingMH(ScheduledFuture<?> hndl) {
 MethodHandle mh = manager.makeMh();
 try {
 System.out.println("With Method Handle");
 mh.invokeExact(manager, hndl);
 } catch (Throwable e) {
 e.printStackTrace();
 }
 }
 private void run() {
 BlockingQueue<WorkUnit<String>> lbq = new LinkedBlockingQueue<>();
 manager = new ThreadPoolManager(lbq);
 final QueueReaderTask msgReader = new QueueReaderTask(100) {
 @Override
 public void doAction(String msg_) {
 if (msg_ != null)
 System.out.println("Msg recvd: " + msg_);
 }
 };
 ScheduledFuture<?> hndl = manager.run(msgReader);
 cancelUsingMH(hndl);
 // cancelUsingProxy(hndl);
 // cancelUsingReflection(hndl);
 }
}

7、Path接口(重要接口更新)

Path

public class PathUsage {
 public void usePath() {
 Path path1 = Paths.get("folder1", "sub1");
 Path path2 = Paths.get("folder2", "sub2");
 path1.resolve(path2); //folder1sub1older2sub2
 path1.resolveSibling(path2); //folder1older2sub2
 path1.relativize(path2); //....older2sub2
 path1.subpath(0, 1); //folder1
 path1.startsWith(path2); //false
 path1.endsWith(path2); //false
 Paths.get("folder1/./../folder2/my.text").normalize(); //folder2my.text
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) {
 PathUsage usage = new PathUsage();
 usage.usePath();
 }
}

DirectoryStream

public class ListFile {
 public void listFiles() throws IOException {
 Path path = Paths.get("");
 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.*")) {
 for (Path entry: stream) {
 //使用entry
 System.out.println(entry);
 }
 }
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) throws IOException {
 ListFile listFile = new ListFile();
 listFile.listFiles();
 }
}

Files

public class FilesUtils {
 public void manipulateFiles() throws IOException {
 Path newFile = Files.createFile(Paths.get("new.txt").toAbsolutePath());
 List<String> content = new ArrayList<String>();
 content.add("Hello");
 content.add("World");
 Files.write(newFile, content, Charset.forName("UTF-8"));
 Files.size(newFile);
 byte[] bytes = Files.readAllBytes(newFile);
 ByteArrayOutputStream output = new ByteArrayOutputStream();
 Files.copy(newFile, output);
 Files.delete(newFile);
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) throws IOException {
 FilesUtils fu = new FilesUtils();
 fu.manipulateFiles();
 }
}

WatchService

public class WatchAndCalculate {
 public void calculate() throws IOException, InterruptedException {
 WatchService service = FileSystems.getDefault().newWatchService();
 Path path = Paths.get("").toAbsolutePath();
 path.register(service, StandardWatchEventKinds.ENTRY_CREATE);
 while (true) {
 WatchKey key = service.take();
 for (WatchEvent<?> event : key.pollEvents()) {
 Path createdPath = (Path) event.context();
 createdPath = path.resolve(createdPath);
 long size = Files.size(createdPath);
 System.out.println(createdPath + " ==> " + size);
 }
 key.reset();
 }
 }
 /**
 * @param args the command line arguments
 */
 public static void main(String[] args) throws Throwable {
 WatchAndCalculate wc = new WatchAndCalculate();
 wc.calculate();
 }
}

8、fork/join计算框架

Java7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

该框架为Java8的并行流打下了坚实的基础

 

Java 8

Java 8可谓是自Java 5以来最具革命性的版本了,她在语言、编译器、类库、开发工具以及Java虚拟机等方面都带来了不少新特性。

一、Lambda表达式

Lambda表达式可以说是Java 8最大的卖点,她将函数式编程引入了Java。Lambda允许把函数作为一个方法的参数,或者把代码看成数据。

一个Lambda表达式可以由用逗号分隔的参数列表、–>符号与函数体三部分表示。例如:

Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e -> System.out.println( e ) );

为了使现有函数更好的支持Lambda表达式,Java 8引入了函数式接口的概念。函数式接口就是只有一个方法的普通接口。java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的例子。为此,Java 8增加了一种特殊的注解@FunctionalInterface

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

我们可以在接口中定义默认方法,使用default关键字,并提供默认的实现。所有实现这个接口的类都会接受默认方法的实现,除非子类提供的自己的实现。例如:

public interface DefaultFunctionInterface {
 default String defaultFunction() {
 return "default function";
 }
 }

我们还可以在接口中定义静态方法,使用static关键字,也可以提供实现。例如:

public interface StaticFunctionInterface {
 static String staticFunction() {
 return "static function";
 }
 }

接口的默认方法和静态方法的引入,其实可以认为引入了C++中抽象类的理念,以后我们再也不用在每个实现类中都写重复的代码了。

三、方法引用(含构造方法引用)

通常与Lambda表达式联合使用,可以直接引用已有Java类或对象的方法。一般有四种不同的方法引用:

构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;

静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;

特定类的任意对象方法引用。它的语法是Class::method。要求方法是没有参数的;

特定对象的方法引用,它的语法是instance::method。要求方法接受一个参数,与3不同的地方在于,3是在列表元素上分别调用方法,而4是在某个对象上调用方法,将列表元素作为参数传入;

四、重复注解

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

五、扩展注解的支持(类型注解)

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

private @NotNull String name;

六、Optional

Java 8引入Optional类来防止空指针异常,Optional类最先是由Google的Guava项目引入的。Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了。

七、Stream

Stream API是把真正的函数式编程风格引入到Java中。其实简单来说可以把Stream理解为MapReduce,当然Google的MapReduce的灵感也是来自函数式编程。她其实是一连串支持连续、并行聚集操作的元素。从语法上看,也很像linux的管道、或者链式编程,代码写起来简洁明了,非常酷帅!

八、Date/Time API (JSR 310)

Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。

九、JavaScript引擎Nashorn

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

Java 9

经过4次跳票,历经曲折的java 9 终于终于在2017年9月21日发布(距离上个版本足足3年半时间)java 9 提供了超过 150 项新功能特性,包括备受期待的模块化系统、可交互的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。可以说 Java 9 是一个庞大的系统工程,完全做了一个整体改变。但本博文只介绍最重要的十大新特性

特性列表

  • 平台级modularity(原名:Jigsaw) 模块化系统
  • Java 的 REPL 工具: jShell 命令
  • 多版本兼容 jar 包(这个在处理向下兼容方面,非常好用)
  • 语法改进:接口的私有方法
  • 语法改进:UnderScore(下划线)使用的限制
  • 底层结构:String 存储结构变更(这个很重要)
  • 集合工厂方法:快速创建只读集合
  • 增强的 Stream API
  • 全新的 HTTP 客户端 API
  • 其它特性
  • 它的新特性来自于100于项JEP和40于项JSR

详见:https://blog.csdn.net/jek123456/article/details/79693546

 

Java 10

2018年3月20日,Java 10 正式发布,这一次没有跳票。它号称有109项新特性,包含12个JEP。需要注意的是,本次Java10并不是Oracle的官方LTS版本,还是坐等java11的发布再考虑在生产中使用

特性列表

  • 局部变量的类型推断 var关键字
  • GC改进和内存管理 并行全垃圾回收器 G1
  • 垃圾回收器接口
  • 线程-局部变量管控
  • 合并 JDK 多个代码仓库到一个单独的储存库中
  • 新增API:ByteArrayOutputStream
  • 新增API:List、Map、Set
  • 新增API:java.util.Properties
  • 新增API: Collectors收集器
  • 其它特性

1、局部变量的类型推断 var关键字

这个新功能将为Java增加一些语法糖 - 简化它并改善开发者体验。新的语法将减少与编写Java相关的冗长度,同时保持对静态类型安全性的承诺。

这可能是Java10给开发者带来的最大的一个新特性。下面主要看例子:

public static void main(String[] args) {
 var list = new ArrayList<String>();
 list.add("hello,world!");
 System.out.println(list);
 }

这是最平常的使用。注意赋值语句右边,最好写上泛型类型,否则会有如下情况:

public static void main(String[] args) {
 var list = new ArrayList<>();
 list.add("hello,world!");
 list.add(1);
 list.add(1.01);
 System.out.println(list);
 }

list什么都可以装,非常的不安全了。和js等语言不同的是,毕竟Java还是强类型的语言,所以下面语句是编译报错的:

public static void main(String[] args) {
 var list = new ArrayList<String>();
 list.add("hello,world!");
 System.out.println(list);
 list = new ArrayList<Integer>(); //编译报错
 }

注意:下面几点使用限制

局部变量初始化

for循环内部索引变量

传统的for循环声明变量

public static void main(String[] args) {
 //局部变量初始化
 var list = new ArrayList<String>();
 //for循环内部索引变量
 for (var s : list) {
 System.out.println(s);
 }
 //传统的for循环声明变量
 for (var i = 0; i < list.size(); i++) {
 System.out.println(i);
 }
 }

下面这几种情况,都是不能使用var的

方法参数全局变量

public static var list = new ArrayList<String>(); //编译报错

public static List<String> list = new ArrayList<>(); //正常编译通过

构造函数参数方法返回类型字段捕获表达式(或任何其他类型的变量声明)

 

2、GC改进和内存管理 并行全垃圾回收器 G1

JDK 10中有2个JEP专门用于改进当前的垃圾收集元素。

Java 10的第二个JEP是针对G1的并行完全GC(JEP 307),其重点在于通过完全GC并行来改善G1最坏情况的等待时间。G1是Java 9中的默认GC,并且此JEP的目标是使G1平行。

3、垃圾回收器接口

这不是让开发者用来控制垃圾回收的接口;而是一个在 JVM 源代码中的允许另外的垃圾回收器快速方便的集成的接口。

4、线程-局部变量管控

这是在 JVM 内部相当低级别的更改,现在将允许在不运行全局虚拟机安全点的情况下实现线程回调。这将使得停止单个线程变得可能和便宜,而不是只能启用或停止所有线程。

5、合并 JDK 多个代码仓库到一个单独的储存库中

在 JDK9 中,有 8 个仓库: root、corba、hotspot、jaxp、jaxws、jdk、langtools 和 nashorn 。在 JDK10 中这些将被合并为一个,使得跨相互依赖的变更集的存储库运行 atomic commit (原子提交)成为可能。

6、新增API:ByteArrayOutputStream

String toString(Charset): 重载 toString(),通过使用指定的字符集解码字节,将缓冲区的内容转换为字符串。

7、新增API:List、Map、Set

这3个接口都增加了一个新的静态方法,copyOf(Collection)。这些函数按照其迭代顺序返回一个不可修改的列表、映射或包含给定集合的元素的集合。

8、新增API:java.util.Properties

增加了一个新的构造函数,它接受一个 int 参数。这将创建一个没有默认值的空属性列表,并且指定初始大小以容纳指定的元素数量,而无需动态调整大小。还有一个新的重载的 replace 方法,接受三个 Object 参数并返回一个布尔值。只有在当前映射到指定值时,才会替换指定键的条目。

9、新增API: Collectors收集器

toUnmodifiableList():

toUnmodifiableSet():

toUnmodifiableMap(Function, Function):

toUnmodifiableMap(Function, Function, BinaryOperator):

这四个新方法都返回 Collectors ,将输入元素聚集到适当的不可修改的集合中。

10、其它特性

线程本地握手(JEP 312)

其他Unicode语言 - 标记扩展(JEP 314)

基于Java的实验性JIT编译器

根证书颁发认证(CA)

删除工具javah(JEP 313)

从JDK中移除了javah工具,这个很简单并且很重要。

最后

JDK10的升级幅度其实主要还是以优化为主,并没有带来太多对使用者惊喜的特性。所以建议广大开发者还是坐等Java11吧,预计2018年9月份就会到来,最重要的是它是LTS版本哦,所以是可以运用在生产上的。

Java 11

2018年9月26日,Oracle 官方宣布 Java 11 正式发布。这是 Java 大版本周期变化后的第一个长期支持版本(LTS版本,Long-Term-Support,持续支持到2026年9月),非常值得关注。Java11 带来了 ZGC、Http Client 等重要特性,一共包含 17 个 JEP(JDK Enhancement Proposals,JDK 增强提案)。

Java 7~14各个版本新特性详解

 

特性列表

官方新特性:

Java 7~14各个版本新特性详解

 

本文针对于读者对关心、也是最实用的八大新特性做出一些讲解

  • 本地变量类型推断
  • 字符串加强
  • 集合加强
  • Stream 加强
  • Optional 加强
  • InputStream 加强
  • HTTP Client API
  • 化繁为简,一个命令编译运行源代码

Java 7~14各个版本新特性详解

 

Java 7~14各个版本新特性详解

 

3、集合加强

自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。

示例1:

var list = List.of("Java", "Python", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); // true

示例2:

var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // false

示例1和2代码差不多,为什么一个为true,一个为false?

来看下它们的源码:

static <E> List<E> of(E... elements) {
 switch (elements.length) { // implicit null check of elements
 case 0:
 return ImmutableCollections.emptyList();
 case 1:
 return new ImmutableCollections.List12<>(elements[0]);
 case 2:
 return new ImmutableCollections.List12<>(elements[0], elements[1]);
 default:
 return new ImmutableCollections.ListN<>(elements);
 }
}
static <E> List<E> copyOf(Collection<? extends E> coll) {
 return ImmutableCollections.listCopy(coll);
}
static <E> List<E> listCopy(Collection<? extends E> coll) {
 if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
 return (List<E>)coll;
 } else {
 return (List<E>)List.of(coll.toArray());
 }
}

可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.

注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

public static void main(String[] args) {
 Set<String> names = Set.of("Fred", "Wilma", "Barney", "Betty");
 //JDK11之前我们只能这么写
 System.out.println(Arrays.toString(names.toArray(new String[names.size()])));
 //JDK11之后 可以直接这么写了
 System.out.println(Arrays.toString(names.toArray(size -> new String[size])));
 System.out.println(Arrays.toString(names.toArray(String[]::new)));
 }

Collection.toArray(IntFunction)

在java.util.Collection接口中添加了一个新的默认方法toArray(IntFunction)。此方法允许将集合的元素传输到新创建的所需运行时类型的数组。

public static void main(String[] args) {
 Set<String> names = Set.of("Fred", "Wilma", "Barney", "Betty");
 //JDK11之前我们只能这么写
 System.out.println(Arrays.toString(names.toArray(new String[names.size()])));
 //JDK11之后 可以直接这么写了
 System.out.println(Arrays.toString(names.toArray(size -> new String[size])));
 System.out.println(Arrays.toString(names.toArray(String[]::new)));
 }

4、Stream 加强

Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。

增加单个参数构造方法,可为null

Stream.ofNullable(null).count(); // 0
//JDK8木有ofNullable方法哦

源码可看看:

/**
 * @since 9
 */
 public static<T> Stream<T> ofNullable(T t) {
 return t == null ? Stream.empty()
 : StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
 }

2、增加 takeWhile 和 dropWhile 方法

Stream.of(1, 2, 3, 2, 1)
.takeWhile(n -> n < 3)
.collect(Collectors.toList()); // [1, 2]

takeWhile表示从开始计算,当 n < 3 时就截止。

Stream.of(1, 2, 3, 2, 1)
.dropWhile(n -> n < 3)
.collect(Collectors.toList()); // [3, 2, 1]

3)iterate重载

这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

 public static void main(String[] args) {
 // 这构造的是无限流 JDK8开始
 Stream.iterate(0, (x) -> x + 1);
 // 这构造的是小于10就结束的流 JDK9开始
 Stream.iterate(0, x -> x < 10, x -> x + 1);
 }

5、Optional 加强

Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

Optional.of("javastack").orElseThrow(); // javastack
Optional.of("javastack").stream().count(); // 1
Optional.ofNullable(null)
.or(() -> Optional.of("javastack"))
.get(); // javastac

or方法和stream方法显然都是新增的

6、InputStream 加强

InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。

var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("javastack.txt");
var javastack = File.createTempFile("javastack2", "txt");
try (var outputStream = new FileOutputStream(javastack)) {
 inputStream.transferTo(outputStream);
}

7、HTTP Client API(重磅)

在java9及10被标记incubator的模块jdk.incubator.httpclient,在java11被标记为正式,改为java.net.http模块。这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。

来看一下 HTTP Client 的用法:

上面的 .GET() 可以省略,默认请求方式为 Get!

更多使用示例可以看这个 API,后续有机会再做演示。

现在 Java 自带了这个 HTTP Client API,我们以后还有必要用 Apache 的 HttpClient 工具包吗?我觉得没啥必要了

8、化繁为简,一个命令编译运行源代码

看下面的代码。

// 编译
javac Javastack.java
// 运行
java Javastack

在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示。

java Javastack.java

移除项

移除了com.sun.awt.AWTUtilities

移除了sun.misc.Unsafe.defineClass,

使用 java.lang.invoke.MethodHandles.Lookup.defineClass来替代

移除了Thread.destroy()以及 Thread.stop(Throwable)方法移除了

sun.nio.ch.disableSystemWideOverlappingFileLockCheck、sun.locale.formatasdefault属性

移除了jdk.snmp模块

移除了javafx,openjdk估计是从java10版本就移除了,oracle jdk10还尚未移除javafx,而java11版本则oracle的jdk版本也移除了javafx

移除了Java Mission Control,从JDK中移除之后,需要自己单独下载

移除了这些Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom

废弃项

废弃了Nashorn JavaScript Engine

废弃了-XX+AggressiveOpts选项

-XX:+UnlockCommercialFeatures以及

-XX:+LogCommercialFeatures选项也不- 再需要

废弃了Pack200工具及其API

说到最后

java11是java改为6个月发布一版的策略之后的第一个LTS(Long-Term Support)版本(oracle版本才有LTS),这个版本最主要的特性是:在模块方面移除Java EE以及CORBA模块,在JVM方面引入了实验性的ZGC,在API方面正式提供了HttpClient类。

从java11版本开始,不再单独发布JRE或者Server JRE版本了,有需要的可以自己通过jlink去定制runtime image

备注:ZGC作为实验性功能包含在内。要启用它,因此需要将-XX:+ UnlockExperimentalVMOptions选项与-XX:+ UseZGC选项结合使用。

ZGC的这个实验版具有以下限制:

  • 它仅适用于Linux / x64。
  • 不支持使用压缩的oops和/或压缩的类点。默认情况下禁用-XX:+UseCompressedOops和-XX:+UseCompressedClassPointers选项。启用它们将不起作用。
  • 不支持类卸载。默认情况下禁用-XX:+ ClassUnloading和-XX:+ - - ClassUnloadingWithConcurrentMark选项。启用它们将不起作用。
  • 不支持将ZGC与Graal结合使用。

java12

1 Switch 表达式

使用Java 12,switch不仅可以作为语句也可以作为表达式。 无论作为语句或者作为表达式,switch都可以使用传统/简化的作用域和控制流行为。 这将有助于简化代码,并为在switch中使用模式匹配铺平道路。

Java开发人员正在增强Java编程语言,以使用模式匹配来解决当前switch语句的几个问题。 这包括:switch块的默认控制流行为,switch块默认作用域(被视为单个作用域的块)和switch仅作为语句。

在Java 11中,switch语句追随C和C++,默认情况下使用fall-through语义。 虽然传统的控制流程在编写低级代码时很有用,但随着switch在更高级别的环境中采用,易出错会盖过其灵活性。

Java 11

Java 7~14各个版本新特性详解

Java 12

Java 7~14各个版本新特性详解

2 默认CDS归档

通过在64位平台上的默认类列表的帮助下生成CDS归档来改进JDK构建过程,从而有效地消除了运行java -Xshare:dump。 此功能的目标包括:1。)改进开箱即用的启动时间,以及2.)摆脱使用-Xshare:dump。

3 Shenandoah GC

Shenandoah是一种垃圾收集(GC)算法,旨在保证低延迟(10 - 500 ms的下限)。 它通过在运行Java工作线程的同时执行GC操作减少GC暂停时间。 使用Shenandoah,暂停时间不依赖于堆的大小。 这意味着无论堆的大小如何,暂停时间都是差不多的。

这是一个实验性功能,不包含在默认(Oracle)的OpenJDK版本中。

4 JMH 基准测试

此功能为JDK源代码添加了一套微基准测试(大约100个),简化了现有微基准测试的运行和新基准测试的创建过程。 它基于Java Microbenchmark Harness(JMH)并支持JMH更新。

此功能使开发人员可以轻松运行当前的微基准测试并为JDK源代码添加新的微基准测试。 可以基于Java Microbenchmark Harness(JMH)轻松测试JDK性能。 它将支持JMH更新,并在套件中包含一组(约100个)基准测试。

5 JVM 常量 API

JEP 334引入了一个API,用于建模关键类文件和运行时artifacts,例如常量池。 此API将包括ClassDesc,MethodTypeDesc,MethodHandleDesc和DynamicConstantDesc等类。此 API 对于操作类和方法的工具很有帮助。

6 G1的可中断 mixed GC

此功能通过将Mixed GC集拆分为强制部分和可选部分,使G1垃圾收集器更有效地中止垃圾收集过程。通过允许垃圾收集过程优先处理强制集,g1可以更多满足满足暂停时间目标。

G1是一个垃圾收集器,设计用于具有大量内存的多处理器机器。由于它提高了性能效率,g1垃圾收集器最终将取代cms垃圾收集器。

G1垃圾收集器的主要目标之一是满足用户设置的暂停时间。G1采用一个分析引擎来选择在收集期间要处理的工作量。此选择过程的结果是一组称为GC集的区域。一旦GC集建立并且GC已经开始,那么G1就无法停止。

如果G1发现GC集选择选择了错误的区域,它会将GC区域的拆分为两部分(强制部分和可选部分)来切换到处理Mix GC的增量模式。如果未达到暂停时间目标,则停止对可选部分的垃圾收集。

7 G1归还不使用的内存

此功能的主要目标是改进G1垃圾收集器,以便在不活动时将Java堆内存归还给操作系统。 为实现此目标,G1将在低应用程序活动期间定期生成或持续循环检查完整的Java堆使用情况。

这将立即归还未使用的部分Java堆内存给操作系统。 用户可以选择执行FULL GC以最大化返回的内存量。

8 移除多余ARM64实现

Java 12将只有一个ARM 64位实现(aarch64)。 目标是删除所有与arm64实现相关的代码,同时保留32位ARM端口和64位aarch64实现。

这将把重点转移到单个64位ARM实现,并消除维护两个实现所需的重复工作。 当前的JDK 11实现中有两个64位ARM实现。


java13

1、switch优化更新

JDK11以及之前的版本:

switch (day) {
 case MONDAY: 
 case FRIDAY:
 case SUNDAY:
 System.out.println(6); 
 break; 
 case TUESDAY: 
 System.out.println(7); 
 break; case THURSDAY: 
 case SATURDAY: 
 System.out.println(8);
 break; 
 case WEDNESDAY:
 System.out.println(9);
 break; 
}

JDK12版本

switch (day) {
 case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); 
 case TUESDAY -> System.out.println(7); 
 case THURSDAY, SATURDAY -> System.out.println(8); 
 case WEDNESDAY -> System.out.println(9);
 }

JDK13版本

static void howMany(int k) {
 System.out.println(
 switch (k) {
 case 1 -> "one"
 case 2 -> "two"
 default -> "many"
 }
 );
}

2、文本块升级

2.1、html例子

JDK13之前

String html = "<html>\n" +
 " <body>\n" +
 " <p>Hello, world</p>\n" +
 " </body>\n" +
 "</html>\n";

JDK13优化的:

String html = """
 <html>
 <body>
 <p>Hello, world</p>
 </body>
 </html>
 """;

2.2、SQL变化

JDK13之前

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
 "WHERE `CITY` = 'INDIANAPOLIS'\n" +
 "ORDER BY `EMP_ID`, `LAST_NAME`;\n";

JDK13

String query = """ 
 SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
 WHERE `CITY` = 'INDIANAPOLIS'
 ORDER BY `EMP_ID`, `LAST_NAME`;
 """;

2.3、解释

文本块

"""
line 1
line 2
line 3
"""

相当于字符串文字:

"line 1\nline 2\nline 3\n"

3、动态CDS档案

目标:

提高应用程序类 - 数据共享(AppCDS)的可用性。消除了用户进行试运行以创建每个应用程序的类列表的需要。
-Xshare:dump
使用类列表由该选项启用的静态归档应继续工作。这包括内置类加载器和用户定义的类加载器的类。

4、取消使用未使用的内存

摘要:

 增强ZGC以将未使用的堆内存返回给操作系统。

动机:

 ZGC目前没有取消提交并将内存返回给操作系统,即使该内存长时间未使用。对于所有类型的应用程序和环境,此行为并非最佳,
尤其是那些需要关注内存占用的应用程序和环境 例如:通过使用支付资源的容器环境。应用程序可能长时间处于空闲状态并与许多其
他应用程序共享或竞争资源的环境。应用程序在执行期间可能具有非常不同的堆空间要求。
 例如,启动期间所需的堆可能大于稳态执行期间稍后所需的堆。HotSpot中的其他垃圾收集器,如G1和Shenandoah,今天提供
了这种功能,某些类别的用户发现它非常有用。将此功能添加到ZGC将受到同一组用户的欢迎。

5、重新实现旧版套接字API

摘要:

 使用更简单,更现代的实现替换java.net.Socket和java.net.ServerSocketAPI 使用的底层实现,易于维护和调试。新的实
现很容易适应用户模式线程,也就是光纤,目前正在Project Loom中进行探索。

动机:

 在java.net.Socket和java.net.ServerSocketAPI,以及它们的底层实现,可以追溯到JDK 1.0。实现是遗留Java和C代
码的混合,维护和调试很痛苦。该实现使用线程堆栈作为I/O缓冲区,这种方法需要多次增加默认线程堆栈大小。该实现使用本机数据
结构来支持异步关闭,这是多年来微妙可靠性和移植问题的根源。该实现还有几个并发问题,需要进行大修才能正确解决。在未来的光
纤世界环境中,而不是在本机方法中阻塞线程,当前的实现不适用于目的。

6、FileSystems.newFileSystem新方法

核心库/ java.nio中添加了FileSystems.newFileSystem(Path,Map <String,?>)方法

添加了三种新方法java.nio.file.FileSystems,以便更轻松地使用将文件内容视为文件系统的文件系统提供程序。

1、newFileSystem(Path)
2、newFileSystem(Path, Map<String, ?>)
3、newFileSystem(Path, Map<String, ?>, ClassLoader)
 添加为newFileSystem(Path, Map<String, ?>) 已使用现有2-arg newFileSystem(Path, ClassLoader)并指定类加载器
的代码创建源(但不是二进制)兼容性问题。null.例如,由于引用newFileSystem不明确,因此无法编译以下内容: FileSystem fs = FileSystems.newFileSystem(path, null);
为了避免模糊引用,需要修改此代码以将第二个参数强制转换为java.lang.ClassLoader。

7、nio新方法

核心库/ java.nio中新的java.nio.ByteBuffer批量获取/放置方法转移字节而不考虑缓冲区位置。

 java.nio.ByteBufferjava.nio现在,其他缓冲区类型定义绝对批量get和put传输连续字节序列的方法,而不考虑或影响缓冲
区位置。

8、核心库/ java.time

 新日本时代名称Reiwa,此更新中添加了代表新Reiwa时代的实例。与其他时代不同,这个时代没有公共领域。它可以通过调用
JapaneseEra.of(3)或获得JapaneseEra.valueOf("Reiwa")。JDK13及更高版本将有一个新的公共领域来代表这个时代。
 NewEra从2019年5月1日开始的日本时代的占位符名称“ ”已被新的官方名称取代。依赖占位符名称(请参阅JDK-8202088)获
取新时代单例(JapaneseEra.valueOf("NewEra"))的应用程序将不再起作用。请参阅JDK-8205432

9、核心库/ java.util中:I18N

支持Unicode 12.1,此版本将Unicode支持升级到12.1,其中包括以下内容:

 java.lang.Character支持12.1级的Unicode字符数据库,其中12.0从11.0开始增加554个字符,总共137,928个
字符。这些新增内容包括4个新脚本,总共150个脚本,以及61个新的表情符号字符。U+32FF SQUARE ERA NAME REIWA从
12.0开始,12.1只添加一个字符。java.text.Bidi和java.text.Normalizer类分别支持12.0级的Unicode标准附件,
#9和#15。java.util.regexpackage支持基于12.0级Unicode标准附件#29的扩展字形集群。

10、热点/ GC

 10.1 JEP 351 ZGC取消提交未使用的存储器 
 10.2 添加了-XXSoftMaxHeapSize标志
 10.3 ZGC支持的最大堆大小从4TB增加到16TB

11、安全库/ java.security

 11.1 该com.sun.security.crl.readtimeout系统属性设置为CRL检索的最大读取超时,单位为秒。如果尚未设置该属性,
或者其值为负,则将其设置为默认值15秒。值0表示无限超时。
 11.2 新的keytool -showinfo -tls用于显示TLS配置信息的命令keytool -showinfo -tls添加了一个显示TLS配置信
息的新命令。
 11.3 SunMSCAPI提供程序现在支持以下一代加密(CNG)格式读取私钥。这意味着CNG格式的RSA和EC**可从Windows**
库加载,例如“Windows-MY”。与EC(签名算法SHA1withECDSA,SHA256withECDSA等等)也支持。

12、删除功能

删除的部分功能:

 12.1 核心库/java.net中,不再支持Pre-JDK 1.4 SocketImpl实现java.net.SocketImpl此版本已删除对为
JavaSE1.3及更早版本编译的自定义实现的支持。此更改对SocketImpl为Java SE 1.4(2002年发布)或更新版本编译
的实现没有影响。
 12.2 核心库/java.lang中,删除运行时跟踪方法,过时的方法traceInstructions(boolean),并
traceMethodCalls(boolean)已经从删除java.lang.Runtime类。这些方法对许多版本都不起作用,它们
的预期功能由Java虚拟机工具接口(JVMTI)提供。

java14

第14版包含的JEP(Java Enhancement Proposals,Java增强提案)比12版和13版加起来还要多。在这篇文章中,我将主要讨论以下几点:

  • 改进的switch表达式,第一次出现在Java 12和13中,在Java 14中获得了完全的支持
  • instanceof支持模式匹配(语言特性)
  • NullPointerException(JVM特性)

希望你在阅读完本文后,积极地代码中实验这些功能,为Java团队提供反馈,并为Java的发展做出贡献。

一、Switch表达式

java 14 中的switch表达式将会永久存在。如果你需要回忆一下什么是switch表达式,可以参考以前的这两篇文章(https://blogs.oracle.com/javamagazine/new-switch-expressions-in-java-12,https://blogs.oracle.com/javamagazine/inside-java-13s-switch-expressions-and-reimplemented-socket-api)。

在之前的发布中,switch表达式只是一个“预览”阶段的特性。我想提醒一下,“预览”阶段的特性的目的是为了收集反馈,这些特性可能会随时改变,根据反馈结果,这些特性甚至可能会被移除,但通常所有的预览特性最后都会在java中固定下来。
新的switch表达式的优点是,不再有缺省跳过行为(fall-through),更全面,而且表达式和组合形式更容易编写,因此出现bug的可能性就更低。例如,switch表达式现在可以使用箭头语法,如下所示:

var log = switch (event) {
   
case PLAY -> "User has triggered the play button";
   
case STOP, PAUSE -> "User needs a break";
   
default -> {
        String message = event.toString();
        LocalDateTime now = LocalDateTime.now();
        yield
"Unknown event " + message +
              
" logged on " + now;
    }
};

二、文本块

Java 13引入的一个预览功能是文本块。有了文本块,多行的字符串字面量就很容易编写了。这个功能在Java 14中进行第二次预览,而且发生了一些变化。例如,多行文本的格式化可能需要编写许多字符串连接操作和转义序列。下面的代码演示了一个HTML的例子:

String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

有了文本块,就可以简化这一过程,只需使用三引号作为文本块的起始和结束标记,就能编写出更优雅的代码:

String html = """
<HTML>
  <BODY>
    <H1>"
Java 14 is here!"</H1>
  </BODY>
</HTML>"""
;

与普通字符串字面量相比,文本块的表达性更好。更多的内容可以参考这篇文章    (https://blogs.oracle.com/javamagazine/text-blocks-come-to-java)。

Java 14引入了两个新的转义序列。第一,可以使用新的 \s 转义序列来表示一个空格。第二,可以使用反斜杠 \ 来避免在行尾插入换行字符。这样可以很容易地在文本块中将一个很长的行分解成多行来增加可读性。

例如,现在编写多行字符串的方式如下:

String literal =
        
"Lorem ipsum dolor sit amet, consectetur adipiscing " +
        
"elit, sed do eiusmod tempor incididunt ut labore " +
        
"et dolore magna aliqua.";

在文本块中使用 \ 转义序列,就可以写成这样:  

String text = """
                Lorem ipsum dolor sit amet, consectetur adipiscing \
                elit, sed do eiusmod tempor incididunt ut labore \
                et dolore magna aliqua.\
                """
;

三、instanceof的模式匹配

Java 14引入了一个预览特性,有了它就不再需要编写先通过instanceof判断再强制转换的代码了。例如,下面的代码:

if (obj instanceof Group) {
  Group group = (Group) obj;

  
// use group specific methods
  var entries = group.getEntries();
}

利用这个预览特性可以重构为:

if (obj instanceof Group group) {
  var entries = group.getEntries();
}

由于条件检查要求obj为Group类型,为什么还要像第一段代码那样在条件代码块中指明obj为Group类型呢?这可能会引发错误。

这种更简洁的语法可以去掉Java程序里的大多数强制类型转换。(2011年的一篇针对相关语言特性的研究论文(http://www.cs.williams.edu/FTfJP2011/6-Winther.pdf)指出,24%的类型转换都来自于instanceof后的条件语句。)

JEP 305解释了这项改变,并给出了Joshuoa Bloch的著作《Effective Java》中的一个例子,演示了下面两种等价的写法:

@Override public boolean equals(Object o) {
   
return (o instanceof CaseInsensitiveString) &&
            ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

这段代码中冗余的CaseInsensitiveString强制类型转换可以去掉,转换成下面的方式:

@Override public boolean equals(Object o) {
   
return (o instanceof CaseInsensitiveString cis) &&
            cis.s.equalsIgnoreCase(s);
}
     

这个预览特性很值得尝试,因为它打开了通向更通用的模式匹配的大门。模式匹配的思想是为语言提供一个便捷的语法,根据特定的条件从对象中提取出组成部分。这正是instanceof操作符的用例,因为条件就是类型检查,提取操作需要调用适当的方法,或访问特定的字段。

换句话说,该预览功能仅仅是个开始,以后该功能肯定能够减少更多的代码冗余,从而降低bug发生的可能性。

四、Record

另一个预览功能就是record。与前面介绍的其他预览功能一样,这个预览功能也顺应了减少Java冗余代码的趋势,能帮助开发者写出更精准的代码。Record主要用于特定领域的类,它的位移功能就是存储数据,而没有任何自定义的行为。

我们开门见山,举一个最简单的领域类的例子:BankTransaction,它表示一次交易,包含三个字段:日期,金额,以及描述。定义类的时候需要考虑多个方面:

  • 构造器
  • getter方法
  • toString()
  • hashCode()和equals()

这些部分的代码通常由IDE自动生成,而且会占用很大篇幅。下面是生成的完整的BankTransaction类:

public class BankTransaction {
   
private final LocalDate date;
   
private final double amount;
   
private final String description;

   
public BankTransaction(final LocalDate date,
                           
final double amount,
                          
final String description) {
       
this.date = date;
       
this.amount = amount;
       
this.description = description;
    }

   
public LocalDate date() {
       
return date;
    }

   
public double amount() {
       
return amount;
    }

   
public String description() {
       
return description;
    }

   
@Override
    public String toString() {
       
return "BankTransaction{" +
               
"date=" + date +
               
", amount=" + amount +
               
", description='" + description + '\'' +
               
'}';
    }

   
@Override
    public boolean equals(Object o) {
       
if (this == o) return true;
       
if (o == null || getClass() != o.getClass()) return false;
        BankTransaction that = (BankTransaction) o;
       
return Double.compare(that.amount, amount) == 0 &&
                date.equals(that.date) &&
                description.equals(that.description);
    }

   
@Override
    public int hashCode() {
       
return Objects.hash(date, amount, description);
    }
}
     

Java 14提供了一种方法可以解决这种冗余,可以更清晰地表达目的:这个类的唯一目的就是将数据整合在一起。Record会提供equals、hashCode和toString方法的实现。因此,BankTransaction类可以重构如下:

public record BankTransaction(LocalDate date,
                              
double amount,
                              String description) {}

通过record,可以“自动”地得到equals,hashCode和toString的实现,还有构造器和getter方法。

要想尝试这个例子,需要用preview标志编译该文件:

javac --enable-preview --release 14 BankTransaction.java

record的字段隐含为final。因此,record的字段不能被重新赋值。但要注意的是,这并不代表整个record是不可变的,保存在字段中的对象可以是可变的。

如果你有兴趣阅读更多关于record的内容,可以阅读Ben Evans最近在《Java Magazine》上发表的文章(https://blogs.oracle.com/javamagazine/records-come-to-java)。

请继续关注该功能。从培养新一代的Java开发者的视角来看,Record也很有意思。例如,如果你要培养初级开发者,那么record应该什么时候讲呢?是在讲OOP之前还是之后?

五、NullPointerException

一些人认为,抛出NullPointerException异常应该当做新的“Hello World”程序来看待,因为NullPointerException是早晚会遇到的。玩笑归玩笑,这个异常的确会造成困扰,因为它经常出现在生产环境的日志中,会导致调试非常困难,因为它并不会显示原始的代码。例如,如下代码:

var name = user.getLocation().getCity().getName();     

在Java 14之前,你可能会得到如下的错误:

Exception in thread "main" java.lang.NullPointerException
    at NullPointerExample.main(NullPointerExample.java:
5)

不幸的是,如果在第5行是一个包含了多个方法调用的赋值语句(如getLocation()和getCity()),那么任何一个都可能会返回null。实际上,变量user也可能是null。因此,无法判断是谁导致了NullPointerException。

在Java 14中,新的JVM特性可以显示更详细的诊断信息:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
    at NullPointerExample.main(NullPointerExample.java:5)

该消息包含两个明确的组成部分

  • 后果:Location.getCity()无法被调用
  • 原因:User.getLocation()的返回值为null

增强版本的诊断信息只有在使用下述标志运行Java时才有效:

-XX:+ShowCodeDetailsInExceptionMessages

下面是个例子: 

java -XX:+ShowCodeDetailsInExceptionMessages NullPointerExample

在以后的版本中,该选项可能会成为默认。

这项改进不仅对于方法调用有效,其他可能会导致NullPointerException的地方也有效,包括字段访问、数组访问、赋值等。

六、总结

Java 14提供了几个新的预览版语言特性和更新,能很好地帮助开发者完成日常工作。Java 14还引入了record,这是一种创建精确数据类的新方法。此外,NullPointerException的消息经过了改进,能显示明确的诊断信息。switch表达式也成了Java 14的一部分。文本块功能可以帮你处理多行字符串,这是在引入了两个新的转义序列之后的另一预览功能。还有一项改动就是JDK Flight Recorder的事件流。

————————————————
参考列表:

https://blog.csdn.net/oldshaui/article/details/88684158
https://www.cnblogs.com/Ranyuangang/p/9707712.html