Java 8 之后,还有哪些进化的功能?
mhr18 2024-10-09 12:12 22 浏览 0 评论
通过本文了解Java8以来这门语言的发展。
作者 | Dávid Csákvári
译者 | 弯月,责编 | 郭芮
出品 | CSDN(ID:CSDNnews)
以下为译文:
当年Java 8引进的Stream和Lambda是一项重大改进,编写函数式编程风格的代码不再需要写大量的样板代码。虽然近期的版本并没有引入如此重大的改进,但Java还是引入了很多小改进。
这篇文章总结了Java 8之后引入的语言改进。如果你想了解新平台背后的JEP,请参考这篇文章:
https://advancedweb.hu/2019/02/19/post_java_8/
局部变量类型推断
var关键字可能是Java 8之后最重要的语言改进了。该关键字最初在Java 10引入,在Java 11得到了大幅改进。
有了它,我们就可以在定义局部变量时省略类型定义,减少繁文缛节:
var greetingMessage = "Hello!";
尽管看上去这很像JavaScript的var关键字,但它并不是动态类型。
引用如下JEP的一段话:
我们希望通过减少编写Java代码时的繁文缛节来改善编程的体验,同时维持Java的静态类型安全。
这样定义的变量的类型会在编译时进行推断,上述示例中推断的类型为String。使用var而不是显式指定类型,可以让代码更加简洁,故而可以提高代码的可读性。
下面是类型推断的另一个例子:
MyAwesomeClass awesome = new MyAwesomeClass;
显然,知许多情况下这个特性都可以改进代码质量。但是,有时候还是使用显式类型定义更好。我们来看看一些不宜使用var替换类型定义的情况。
随时考虑可读性
第一种情况就是从源代码中删除类型定义可能会降低可读性的情况。
当然这种情况还可以借助IDE,但在代码审核过程中,或者需要快速阅读代码的情况下,这样做就可能影响可读性。比如工厂模式,你只能去寻找负责生成对象的代码来确定生成的对象类型。
下面是一个小测验。下面的代码使用了Java 8的日期和时间API。猜一猜下面代码中的变量类型:
var date = LocalDate.parse("2019-08-13");
var dayOfWeek = date.getDayOfWeek;
var dayOfMonth = date.getDayOfMonth;
做完了?答案如下所示。
第一行很直观,parse方法返回LocalDate对象。但是后两个你必须对API有一定了解才能得出正确答案:dayOfWeek返回java.time.DayOfWeek,而dayOfMonth返回int。
使用var的另一个潜在问题是,阅读者不得不进一步依赖注释。考虑下面的代码:
private void horriblyLongMethod {
// ...
// ...
// ...
var dayOfWeek = date.getDayOfWeek;
// ...
// ...
// ...
}
有了上一个例子的经验,我打赌你肯定会猜它是java.time.DayOfWeek。但这次是个整型,因为本例中的date是Joda时间。这是个不同的API,行为也略有不同,但你没有发现,因为这个方法非常长,而你并没有阅读所有代码。
如果这里给出了显式类型定义,那么确定dayOfWeek就非常容易。而使用var时,阅读者首先要找到date变量的类型,并检查其getDayOfWeek的行为。在IDE中很容易理解,但快速阅读代码时就没那么容易了。
注意保留重要的类型信息
第二种情况是,使用var会丧失所有类型信息,甚至导致无法推断。大多数情况下这个问题会被Java编译器捕获。例如,var不能推断lambda或方法引用,因为在这些特性中,编译器依赖左侧的表达式来确定类型。
但是有一些例外。例如,var不能很好地用于菱形操作符。在创建泛型的实例时,菱形操作符可以让表达式右侧不那么繁琐:
Map<String, String> myMap = new HashMap<String, String>; // Pre Java 7
Map<String, String> myMap = new HashMap<>; // Using Diamond operator
由于该运算符只处理泛型类型,所以我们依然可以去掉一些冗余。我们可以通过var进一步简化:
var myMap = new HashMap<>;
这个例子是合法的,而且Java 11编译器甚至都不会发出警告。但是,我们没有为泛型类型指定任何类型,导致所有类型都必须推断,所以最后的类型是Map<Object, Object>。
当然,只需去掉菱形运算符就可以解决这个问题:
var myMap = new HashMap<String, String>;
另一个问题是在基本数据类型上使用var:
byte b = 1;
short s = 1;
int i = 1;
long l = 1;
float f = 1;
double d = 1;
如果不给出显式类型定义,那么所有变量都会被推断为int。所以,使用基本数据类型时要使用类型字面量(例如1L),或者不要使用var。
务必阅读官方的风格指南
何时使用类型推断、怎样做不会破坏易读性和正确性,这些问题最终都需要你自己判断。经验法则是:遵循优秀的编程实践,比如良好的命名规则、尽力减小局部变量作用域等都会有很大帮助。请务必阅读官方有关var的风格指南(https://openjdk.java.net/projects/amber/LVTIstyle.html)和FAQ(https://openjdk.java.net/projects/amber/LVTIFAQ.html)。
虽然var有如此多的陷阱,但很幸运它的引入相当保守,现在只能用于作用域有限的局部变量。
而且,var的引入也十分谨慎,var并不是新的关键字,而是保留类型名。这就意味着,只有当作为类型名使用时才有特殊含义。任何其他位置出现的var依然只是个合法的标识符。
目前,var没有相应的不可修改版本(如val或const)来定义常量并推断类型。希望以后的版本能够添加这个关键字,在那之前我们可以先使用final var。
参考资料:
与Java 10的var的第一次亲密接触(https://blog.codefx.org/java/java-10-var-type-inference/)
Java局部变量类型推断详解(https://dzone.com/articles/var-work-in-progress)
Java 10:局部变量推断(https://www.journaldev.com/19871/java-10-local-variable-type-inference)
Project Coin带来的多项改进
Project Coin(JSR 334,https://jcp.org/en/jsr/detail?id=334)是JDK 7的一部分,它带来了许多方便的语言改进:
菱形运算符
try-with-resources语句
多catch和更精确的重新throw
在switch语句中使用字符串
二进制整形字面量和数值字面量中的下划线
简化的varargs方法调用
Java 9继续做出了许多小改进。
接口支持私有方法
从Java 8起可以给接口添加默认方法。在Java 9中,这些默认方法甚至可以调用私有方法,这样无需公开就可以复用代码。
尽管算不上重大改进,但能够让默认方法中的代码更简洁。
匿名内层类的菱形操作符
Java 7引入了菱形操作符(<>),让编译器推断构造函数的参数类型,来减少繁琐:
List<Integer> numbers = new ArrayList<>;
但是,以前该功能不能用于匿名内层类上。根据项目的邮件列表中的讨论(http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html)可知,该功能没有作为菱形运算符的最初特性实现的原因是它需要JVM做出重大变更。
在Java 9中这个边缘情况终于解决了,因此现在的菱形运算符更通用:
List<Integer> numbers = new ArrayList<> {
// ...
}
try-with-resources语句中允许使用没有发生实质性改变的变量
Java 7引入的另一项改进就是try-with-resources语句,从此程序员无需再担心释放资源的问题。
我们来演示一下这个功能。首先,在Java 7之前如果想正确关闭资源,需要这样写:
BufferedReader br = new BufferedReader(...);
try {
return br.readLine;
} finally {
if (br != ) {
br.close;
}
}
有了try-with-resources语句,资源就可以自动释放,省却了许多繁文缛节:
try (BufferedReader br = new BufferedReader(...)) {
return br.readLine;
}
尽管这个功能非常强大,但它有几个缺点(Java 9解决了这些缺点)。
虽然这种方法能处理多个资源,但很容易让代码丧失可读性。像这样在try关键字之后以列表的方式定义变量,看起来非常不符合常见的Java编程习惯:
try (BufferedReader br1 = new BufferedReader(...);
BufferedReader br2 = new BufferedReader(...)) {
System.out.println(br1.readLine + br2.readLine);
}
而且,在Java 7之前,如果你想用这种写法来处理已有的变量,就必须定义一个临时变量。(例如JDK-8068948中的例子:https://bugs.openjdk.java.net/browse/JDK-8068948。)
为了解决这些问题,Java增强了try-with-resources,现在不仅能够处理新创建的变量,还能够处理局部常量,或者实际上不可变的局部变量:
BufferedReader br1 = new BufferedReader(...);
BufferedReader br2 = new BufferedReader(...);
try (br1; br2) {
System.out.println(br1.readLine + br2.readLine);
}
在这个例子中,变量初始化不需要跟try-with-resources的初始化部分写在一起。
不过需要注意的一个陷阱是,现在允许访问已经被try-with-resources释放的资源,绝大部分情况下这种访问都会失败:
BufferedReader br = new BufferedReader(...);
try (br) {
System.out.println(br.readLine);
}
br.readLine; // Boom!
下划线不再是有效的标识符
在Java 8中,如果使用下划线作为标识符,编译器就会发出警告。Java 9更进一步,禁止仅使用下划线作为标识符,将其留给未来的特殊语义使用。
int _ = 10; // Compile error
改进的警告
最后,我们提一下新版Java中有关编译器警告的改进。
现在可以用@SafeVarargs给私有方法添加注释,来避免错误的Type safety: Potential heap pollution via varargs parameter警告。(实际上,这个改动是之前提到过的JEP 213: Milling Project Coin中的一部分)。
有关Varargs的更详细内容可以看这里(https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html)。组合使用官方文档中提到的这些功能可能会造成泛型(https://docs.oracle.com/javase/8/docs/technotes/guides/language/generics.html)及其潜在问题(https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html)。
此外,从Java 9开始,编译器不会再因为导入被弃用的类型的import语句而产生警告。这些警告没有提供有用的信息,而且完全是多余的,因为在实际使用被弃用的类型成员时必然会产生警告。
文本讨论了Java 8之后的版本中这门语言本身的改进。随时关注Java平台很重要,因为现在的发布节奏很快,每六个月就会发布一个新版本,平台和语言也会发生相应的变化。
原文:
https://advancedweb.hu/2019/08/08/post_java_8_language_features
作者:Dávid Csákvári,全栈工程师,在Java和Web技术方面有十多年的经验。
【END】
相关推荐
- 甲骨文签署多项大型云协议,其一未来可贡献超300亿美元年收入
-
IT之家7月1日消息,根据甲骨文Oracle当地时间6月30日向美国证券交易委员会(SEC)递交的FORM8-K文件,该企业在始于2025年6月1日的202...
- 甲骨文获TEMU巨额合同,后者大部分基础设施将迁移至Oracle云
-
IT之家6月23日消息,Oracle甲骨文创始人、董事长兼首席技术官LarryEllison(拉里埃里森)在本月早些时候的2025财年第四财季和全财年财报电话会议上表示,Oracle...
- Spring Boot 自定义数据源设置,这些坑你踩过吗?
-
你在使用SpringBoot进行后端开发的过程中,是不是也遇到过这样的问题:项目上线后,数据库连接总是不稳定,偶尔还会出现数据读取缓慢的情况,严重影响了用户体验。经过排查,发现很大一部分原因竟然...
- 一个开箱即用的代码生成器(一个开箱即用的代码生成器是什么)
-
今天给大家推荐一个好用的代码生成器,名为renren-generator,该项目附带前端页面,可以很方便的选择我们所需要生成代码的表。首先我们通过git工具克隆下来代码(地址见文末),导入idea。...
- 低代码建模平台-数据挖掘平台(低代码平台的实现方式)
-
现在来看一下数据连接。·这里是管理数据连接的空间,点击这里可以新增一个数据连接。·输入连接名称,然后输入url,是通过gdbc的方式去连接的数据库,目前是支持mysql、oracle以及国产数据库达梦...
- navicat 17.2.7连接oracle数据库提示加载oracle库失败
-
系统:macOS15.5navicat版本:navicatpremiumlite17.2.7连接oracle测试报错:加载oracle库失败【解决办法】:放达里面找到程序,显示简介里面勾选“使...
- 开源“Windows”ReactOS更新:支持全屏应用
-
IT之家6月17日消息,ReactOS团队昨日(6月16日)在X平台发布系列推文,公布了该系统的最新进展,包括升级Explorer组件,支持全屏应用,从Wine项目引入了...
- SSL 推出采用全模拟内置混音技术的模拟调音台Oracle
-
英国调音台传奇品牌SolidStateLogic宣布推出Oracle——一款采用全模拟内置混音技术的调音台,在紧凑的AWS尺寸机箱内集成了大型调音台的功能。该调音台提供24输入和...
- 47道网络工程师常见面试题,看看有没有你不会的!
-
你们好,我的网工朋友。网络工程师面试的时候,都会被问到什么?这个问题其实很泛,一般来说,你肯定要先看明白岗位需求写的是什么。基本上都是围绕公司需要的业务去问的。但不可否认的是,那些最基础的概念,多少也...
- 汉得信息:发布EBS系统安装启用JWS的高效解决方案
-
e公司讯,从汉得信息获悉,近日,微软官方宣布InternetExplorer桌面应用程序将于2022年6月15日正式停用。目前大部分客户都是使用IE浏览器打开EBS的Form界面,IE停用后,只能使...
- 36.9K star ! 推荐一个酷炫低代码开发平台!功能太强!
-
前言最近在逛github,看看能不能搜罗到一些对自己有帮助的开源软件。不经意间看到一个高star的java开源项目:jeecg-boot。进入在线演示版一看,感叹实在是太牛了!此开源项目不管是给来学习...
- Linux新手入门系列:Linux下jdk安装配置
-
本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...
- 手把手教你在嵌入式设备中使用SQLite3
-
摘要:数据库是用来存储和管理数据的专用软件,使得管理数据更加安全,方便和高效。数据库对数据的管理的基本单位是表(table),在嵌入式linux中有时候它也需要用到数据库,听起来好难,其实就是几个函数...
- JAVA语言基础(java语言基础知识)
-
一、计算机的基本概念什么是计算机?计算机(Computer)全称:电子计算机,俗称电脑。是一种能够按照程序运行、自动高速处理海量数据的现代化智能电子设备。由硬件和软件组成、没有安装过任何软件的计算机称...
- 再见 Navicat!一款开源的 Web 数据库管理工具!
-
大家好,我是Java陈序员。在日常的开发工作中,常常需要与各种数据库打交道。而为了提高工作效率,常常会使用一些可视化工具进行操作数据库。今天,给大家介绍一款开源的数据库管理工具,无需下载安装软件,基...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 甲骨文签署多项大型云协议,其一未来可贡献超300亿美元年收入
- 甲骨文获TEMU巨额合同,后者大部分基础设施将迁移至Oracle云
- Spring Boot 自定义数据源设置,这些坑你踩过吗?
- 一个开箱即用的代码生成器(一个开箱即用的代码生成器是什么)
- 低代码建模平台-数据挖掘平台(低代码平台的实现方式)
- navicat 17.2.7连接oracle数据库提示加载oracle库失败
- 开源“Windows”ReactOS更新:支持全屏应用
- SSL 推出采用全模拟内置混音技术的模拟调音台Oracle
- 47道网络工程师常见面试题,看看有没有你不会的!
- 汉得信息:发布EBS系统安装启用JWS的高效解决方案
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)