Java8-19新特性一览 ,认识全新的前沿技术
前言
Java8出来这么多年后,已经成为企业最成熟稳定的版本,相信绝大部分公司用的还是这个版本,但是一眨眼今年Java19都出来了,相信很多Java工程师忙于学习工作对新特性没什么了解,有的话也仅限于某一块。
本篇就是博主对自己感觉有用的新特性做了一个案例验证及简要说明,整合起来分享给大家。
特别说明:Java17是继Java8之后的一个重要里程碑,像SpringBoot3.0、IDEA2022、Jenkins新版、Kafka4.0等等很多生态都强制绑定支持这个版本,等于是给它背书,博主建议大家有必要花时间去了解一下。
你的收获
首先,你能通过一篇简单、连续、直观的文章就明白Java8之后Java未来整体发展的趋势,为之后几年适应Java相关工作打下基础;
其次,你可以通过了解Java9-17的新特性,为以后的面试加分,毕竟一个爱学习有态度的程序员会更受企业青睐;
最后,你可以看看博主对Java未来发展趋势的粗浅看法,也许能给迷茫的你带来收获。
Java发展趋势
最后稍微说下不少人关心的这个问题,我觉得只要你了解过Java8之后这些版本的新特性和预览特性,你一定可以发现Java在尝试改变,这是一个很好的信号。
就比如上面的这些新特性,你甚至能找到不少Python、JavaScript等语言的影子,Go语言作为新语言就是站在巨人肩膀上发展起来的,吸纳了很多语言的优秀特点。
现在,Java也在走类似的路,能明显看到它在将一些优秀语言的亮点容纳到自己的新版本中,这种趋势代表着一个意义:Java在不断进步。
网上一直充斥着一些看衰Java的言论,没必要当真,你必须自己去体会生态和了解国内的IT公司动态才能有所感受。
Java依然是国内使用最广泛的语言,并且具备最庞大的生态,这不是一朝一夕可以替代的,是市场规律发展的结果。
SpringBoot3.0支持Java17,Jenkins新版支持Java17,Kafka4.0直接抛弃Java8,ElasticSearch8.x最低支持JDK17,还有IDEA2022默认支持Java17,等等之类的开源社区和生态都在给新版的Java背书,更有微软宣布全面拥抱Java,这里面不单单是技术层面的提高,更有利益的诉求和捆绑。
从这一点来说,学习Java完全是值得的,作为一门成熟优秀且严谨的语言,它就像一个白领一样正襟危坐。
我还认为现在学习和掌握Java的工程师,未来转去其他语言也不会有压力,上面的新特性就说明了这种方向,它在吸纳其他语言的亮点。
未来可以预期的是,你转到其他语言会逐渐变的没有那么陌生和晦涩,这是我看到的一个方向,未来的编程语言发展大方向就是 大融合 ,你中有我,我中有你,学一门,通所有。
准备工作
首先,你要安装Java17版本,环境变量配置还是和以前没区别,这是我的版本。
其次,建议安装IDEA2022.3,新版IDEA占用内存比以前少很多,而且有一些增强支持。
安装好后,需要做一个对Java17的配置,看图。
setting配置
Project Structure配置
这里特别说明一下,最好选择预览版本,因为Java17包含一些预览功能,这里不选预览版本会编译报错。
新特性
一共分为了8个,按照版本顺序来讲述的,最后一个是因为几个版本连续有增强,所以单独拿出来。
1、接口private
1)、说明
Java9新特性,在接口中声明private方法,不会被外部实现。
2)、案例
声明一个接口,一个default方法,两个private方法。
/** * <p> * 用户信息接口 * </p> * * @author libin9ioak * @Date 2022/10/24 15:03 */ public interface UserInterface { private void getUsername() { System.err.println("你好,我是java!"); } private void getPassword() { System.err.println("你好,我是python!"); } default void getData() { getUsername(); getPassword(); } }
实现这个接口,可以看到只能实现default方法,无法实现private方法。
/** * <p> * JDK9新特性:接口private方法 * </p> * * @author libin9ioak * @Date 2022/10/24 15:10 */ public class UserImpl implements UserInterface { @Override public void getData() { UserInterface.super.getData(); } public static void main(String[] args) { UserImpl user = new UserImpl(); user.getData(); } }
Ctrl+Insert可以查看要实现的方法
3)、注意
private接口自动是default的,所以要有方法体,否则编译不通过。
2、类型推断
1)、说明
Java11新特性,在方法内部用var关键字声明一个属性或对象,可以被编译器自动判断类型。
2)、案例
案例包含两个测试,一个是直接测试var声明的变量是否能自己推断类型,一个是在循环中使用的效果。
/** * <p> * JDK11新特性:类型推断 * </p> * * @author libin9ioak * @Date 2022/10/24 11:52 */ public class TypeInferenceTest { public static void main(String[] args) { TypeInferenceTest Test = new TypeInferenceTest(); // 测试类型推断 // Test.testVar(); // 循环中使用 List<UserInfo> userList = new ArrayList<>(); UserInfo userInfo = new UserInfo(); userInfo.setUsername("张三"); userInfo.setPassword("123456"); userList.add(userInfo); for (var user : userList) { System.err.println(user.getUsername() + " | " + user.getPassword()); } } public void testVar() { var name = "张三"; var age = 33; var id = 1001L; var amount = BigDecimal.ZERO; var user = new UserInfo(); System.err.println("-------------------------------------------------"); System.err.println(name); System.err.println(age); System.err.println(id); System.err.println(amount); System.err.println(user); } public static class UserInfo { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }
3)、注意
1)、只能在方法内部使用(局部变量);
2)、必须有初始化值且不能为null;
3)、不能定义为方法的返回类型。
3、空指针优化
1)、说明
Java15新特性,就是把NullPointerException异常的日志做了优化打印的更人性化。
2)、案例
可以看到,提示会更有指向性,意味着以后在复杂的生产环境排错过程中,你很可能不会再被空指针异常所困扰。
3)、注意
没什么可注意的
4、文本块
1)、说明
JDK15新特性,就是替代了以前String中一堆换行符和双引号的简洁版写法,相信你很难不喜欢。
2)、案例
可以看到,就是三引号取代了双引号,因为双引号的内容会夹带一堆换行符,而三引号里面就单纯是内容,很清晰,而且双引号换行需要杠n,而三引号直接换行即可生效。
/** * <p> * JDK15新特性:文本块 * </p> * * @author libin9ioak * @Date 2022/10/24 23:36 */ public class TextBlockTest { private static final String str = "佛点击退还给我热负荷给你你国防科技你看你看都发给你开发理念赶快来过年回来寡!\n" + "\n" + "了金佛Joe大肥腿可劲儿,几天了人家烤肉饭 考虑到福奈特," + " 肯定会发给客户的风格,来证明自己的实力。"; private static final String newStr = """ 开发读后感我交电话费国际化等方面高端客户发光飞碟挺乖的发过火等方面, 你发的哭给你看多方面的索菲特荣, 卡密电脑版福克斯麻烦了圣诞老人new,没人,大四吗开发机会大煞风景, 点,很可能付款单。 """; public static void main(String[] args) { System.err.println(str); System.err.println("------------------------------------------------"); System.err.println(newStr); } }
3)、注意
文本块只有一个要注意的地方,就是默认三引号内的内容没有缩进,想要缩进的话要以末尾三引号为参照物向后偏移来确定缩进量。
5、智能转型
1)、说明
Java16新特性,就是帮你对instanceof做了增强,智能转换变量类型。
2)、案例
可以对比old和new两种写法,第二种就是将第一种简化了,类型转换+声明变量+后续逻辑判断一起搞定。
/** * <p> * JDK16新特性:智能转型 * </p> * * @author libin9ioak * @Date 2022/10/24 14:02 */ public class InstanceofMatching { /** * 旧写法 * @param obj 未知对象 */ static void testOld(Object obj) { if (obj instanceof String) { String s = (String) obj; if ("模式匹配".equals(s)) { System.err.println(s); } } } /** * 新写法 * @param obj 未知对象 */ static void testNew(Object obj) { if (obj instanceof String s && "模式匹配".equals(s)) { System.err.println(s); } } public static void main(String[] args) { testOld("Hello, Java!"); testNew("模式匹配"); } }
3)、注意
instanceof后面若需要增强判断必须要用&&,不能用||,因为instanceof本来就是做类型匹配的,Java可以确保&&后面的变量一定存在,但无法判断||后面的变量一定存在,所以会报错。
6、record类
1)、说明
Java16新特性,简单讲,就是有了它,声明一个final类变得更简洁和可读。
2)、案例
先来看下写法
/** * <p> * JDK16新特性:record类 * </p> * * @author libin9ioak * @Date 2022/10/24 21:52 */ public record RecordTest(int type, String typeName) { private void test() { System.err.println(type + " | " + typeName); } public static void main(String[] args) { // 这里new的时候带的参数其实就是类的属性,等于声明属性+访问构造方法二合一。 RecordTest recordTest = new RecordTest(100, "葡萄牙"); recordTest.test(); } }
上面的新写法等同于下面这种老的写法,一看就懂。
/** * <p> * RecordTest的写法等同于这个类 * </p> * * @author libin9ioak * @Date 2022/10/24 21:55 */ public final class RecordCustomTest { final int type; final String typeName; public int type() { return type; } public String name() { return typeName; } public RecordCustomTest(int type, String typeName) { this.type = type; this.typeName = typeName; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RecordCustomTest that = (RecordCustomTest) o; return type == that.type && Objects.equals(typeName, that.typeName); } @Override public int hashCode() { return Objects.hash(type, typeName); } }
3)、注意
没什么可注意的,就跟使用正常的类差不多,只是自动生成了以下内容:
1)、括号里的参数就是该类的属性,且是final类型;
2)、自动生成一个带有该属性的构造器;
3)、自动生成该属性的访问器,如xx.type()、xx.typeName();
4)、生成了equals和hashCode方法。
如果还是不懂,就理解成lombok中的@Data注解即可,同样的意思。
7、密封类和接口
1)、说明
Java17新特性,密封类和密封接口。
使用sealed关键字声明的类,可以通过设置permits关键字来控制哪些子类可以继承它。
使用sealed关键字声明的接口,可以通过设置permits关键字来控制哪些类可以实现它。。
简单来讲,就是爸爸规定哪个儿子能继承财产。
2)、案例
看下密封类的写法
用sealed声明一个类,设置permits授权哪几个子类可以继承它。
/** * <p> * JDK17新特性:密封类 * </p> * * @author libin9ioak * @Date 2022/10/24 17:20 */ public sealed class Daddy permits Son1, Son2 { }
第一个儿子可以继承
/** * <p> * * </p> * * @author libin9ioak * @Date 2022/10/24 17:22 */ public final class Son1 extends Daddy { }
第二个儿子可以继承
/** * <p> * * </p> * * @author libin9ioak * @Date 2022/10/24 17:23 */ public final class Son2 extends Daddy { }
第三个儿子估计是不孝子
可以看到IDEA是有错误提示的,意思是没有被sealed声明的父类所允许。
然后,我们来看看密封接口。
其实和密封类差不多,但还可以结合前面讲过的record来简化代码。
/** * <p> * JDK17新特性:密封接口 * </p> * * @author libin9ioak * @Date 2022/10/24 18:03 */ public sealed interface DaddyInterface permits Son4, Son5 { void test(); }
第四个儿子可以实现父亲的愿望,用了record之后简化了变量声明及一些内置方法的实现。
/** * <p> * * </p> * * @author libin9ioak * @Date 2022/10/24 17:23 */ public record Son4(int age, String name) implements DaddyInterface { @Override public void test() { } }
第五个儿子可以实现父亲的愿望
/** * <p> * * </p> * * @author libin9ioak * @Date 2022/10/24 17:23 */ public record Son5(int age, String name) implements DaddyInterface { @Override public void test() { } }
第六个儿子有点傻,不能实现父亲的愿望,可以看到错误提示和前面密封类一样。
3)、注意
1)、sealed声明的父类,它的子类只能用final、sealed、non-sealed来修饰;
2)、sealed声明的父类,至少要有一个子类;
3)、没有在permits中授权的子类,无法继承父类;
4)、密封接口和密封类的注意点没什么区别;
4)、密封接口结合record来完成可以少写更多代码变得更加简洁。
这里特别说一点,sealed和record其实在Java新特性模式匹配中很有意义,但是我认为模式匹配对于从未了解过的Java程序员来讲有些晦涩,结合sealed后有种套娃传销的感觉,如果难于理解就不是我们本来的目的,因此没有专门花费章节赘述,结尾会简单说一下我对模式匹配的看法。
8、switch增强
1)、说明
为什么把switch单独放最后讲,因为Java14-17分别对其做了增强,放在最后汇总起来讲更直观。
一共有三个改变:
1)、支持箭头语法;
2)、支持表达式;
3)、支持case null。
2)、案例
箭头语法,其实就是把旧写法中的冒号和break直接换成了箭头来代替,更简洁。
/** * <p> * JDK14新特性:switch箭头语法 * </p> * * @author libin9ioak * @Date 2022/10/24 11:05 */ public class SwitchTest1 { private static void pcs(int i) { switch (i) { case 1: System.err.println("Mac book Pro 2021"); break; case 2: System.err.println("Mac book Pro 2022"); break; default: System.err.println("Mac book "); } } private static void computer(int i) { switch (i) { case 1 -> System.err.println("Mac book Pro"); case 2 -> System.err.println("外星人"); default -> System.err.println("苹果"); } } public static void main(String[] args) { pcs(1); computer(3); } }
支持表达式,其实就是能写单个或多个表达式来return。
/** * <p> * JDK14新特性:switch支持表达式 * </p> * * @author libin9ioak * @Date 2022/10/24 11:05 */ public class SwitchTest3 { /** * 单表达式 */ private static String computer(int i) { return switch (i) { case 1 -> "Mac book Pro"; case 2 -> "外星人"; default -> "苹果"; }; } /** * 多表达式 */ private static String computerNew(int i) { return switch (i) { case 1, 2 -> { System.err.println("---------libin9ioak-----------"); System.err.println("来一个Mac book Pro"); yield "Mac book Pro来咯"; } case 3, 4 -> { System.err.println("---------libin9ioak-----------"); System.err.println("来一个外星人"); yield"外星人来咯"; } default -> { System.err.println("---------libin9ioak-----------"); System.err.println("没的选就来个苹果吧"); yield "苹果来咯"; } }; } public static void main(String[] args) { // System.err.println(computer(2)); // System.err.println(computer(3)); System.err.println(computerNew(2)); System.err.println(computerNew(4)); System.err.println(computerNew(5)); } }
支持case null,其实就是case可以接收null值了。
/** * <p> * JDK17新特性(预览):switch支持case null * </p> * * @author libin9ioak * @Date 2022/10/24 11:05 */ public class SwitchTest2 { private static void pcs(String s) { if (s == null) { return; } switch (s) { case "1": System.err.println("Mac book Pro 2021"); break; case "2": System.err.println("Mac book Pro 2022"); break; default: System.err.println("Mac book "); } } private static void computer(String s) { switch (s) { case "1" -> System.err.println("Mac book Pro"); case null -> System.err.println("null"); default -> System.err.println("苹果"); } } public static void main(String[] args) { pcs("1"); computer(null); } }
可以看下效果,直接传null也不会报错。
3)、注意
1)、箭头语法,冒号和箭头不能同时存在;
2)、表达式,多个表达式时要使用花括号并使用yield关键字返回;
3)、case null是预览功能,在IDEA - Project Structure - Modules中选择Language Level为17(Preview)才能编译通过,参考前面的准备工作。
总结
1)、Java9-19的新特性不仅于此,还有一些挺有特点的内容,比如不可变集合、模块化、String和Stream的API增强等等,但是我个人认为不具有代表性,要么是工具能直接帮你转换,要么就是你大概率用不到,所以就没列出来;
2)、模式匹配,是不少Java程序员关注的内容,本篇中record、switch、密封类和接口的内容其实都是模式匹配的基础,但模式匹配对Java来讲并不成熟,《Thinking In Java》的作者也说过可能要好几年才会看到完整形式的Java模式匹配,所以没必要现在就花太多心思去研究一个残缺版本,这个特性和Python、Kotlin、Scala相比其实还差得远。
结语
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。