前言
Java8出來這么多年后,已經(jīng)成為企業(yè)最成熟穩(wěn)定的版本,相信絕大部分公司用的還是這個版本,但是一眨眼今年Java19都出來了,相信很多Java工程師忙于學習工作對新特性沒什么了解,有的話也僅限于某一塊。
本篇就是博主對自己感覺有用的新特性做了一個案例驗證及簡要說明,整合起來分享給大家。
特別說明:Java17是繼Java8之后的一個重要里程碑,像SpringBoot3.0、IDEA2022、Jenkins新版、Kafka4.0等等很多生態(tài)都強制綁定支持這個版本,等于是給它背書,博主建議大家有必要花時間去了解一下。如果沒時間看,可以先
收藏一下,閑下來一邊喝茶一邊品讀。
你的收獲
首先,你能通過一篇簡單、連續(xù)、直觀的文章就明白Java8之后Java未來整體發(fā)展的趨勢,為之后幾年適應(yīng)Java相關(guān)工作打下基礎(chǔ);
其次,你可以通過了解Java9-17的新特性,為以后的面試加分,畢竟一個愛學習有態(tài)度的程序員會更受企業(yè)青睞;
最后,你可以看看博主對Java未來發(fā)展趨勢的粗淺看法,也許能給迷茫的你帶來收獲。
準備工作
首先,你要安裝Java17版本,環(huán)境變量配置還是和以前沒區(qū)別,這是我的版本。

其次,建議安裝IDEA2022.3,新版IDEA占用內(nèi)存比以前少很多,而且有一些增強支持。
安裝好后,需要做一個對Java17的配置,看圖。
setting配置

Project Structure配置

這里特別說明一下,最好選擇預(yù)覽版本,因為Java17包含一些預(yù)覽功能,這里不選預(yù)覽版本會編譯報錯。


新特性
一共分為了8個,按照版本順序來講述的,最后一個是因為幾個版本連續(xù)有增強,所以單獨拿出來。
1、接口private
1)、說明
Java9新特性,在接口中聲明private方法,不會被外部實現(xiàn)。
2)、案例
聲明一個接口,一個default方法,兩個private方法。
/**
* <p>
* 用戶信息接口
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 15:03
*/
public interface UserInterface {
private void getUsername() {
System.err.println("你好,我是王小飛!");
}
private void getPassword() {
System.err.println("你好,我是徐西圓!");
}
default void getData() {
getUsername();
getPassword();
}
}
實現(xiàn)這個接口,可以看到只能實現(xiàn)default方法,無法實現(xiàn)private方法。
/**
* <p>
* JDK9新特性:接口private方法
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 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可以查看要實現(xiàn)的方法

3)、注意
private接口自動是default的,所以要有方法體,否則編譯不通過。
2、類型推斷
1)、說明
Java11新特性,在方法內(nèi)部用var關(guān)鍵字聲明一個屬性或?qū)ο?,可以被編譯器自動判斷類型。
2)、案例
案例包含兩個測試,一個是直接測試var聲明的變量是否能自己推斷類型,一個是在循環(huán)中使用的效果。
/**
* <p>
* JDK11新特性:類型推斷
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 11:52
*/
public class TypeInferenceDemo {
public static void main(String[] args) {
TypeInferenceDemo demo = new TypeInferenceDemo();
// 測試類型推斷
// demo.testVar();
// 循環(huán)中使用
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)、只能在方法內(nèi)部使用(局部變量);
2)、必須有初始化值且不能為null;
3)、不能定義為方法的返回類型。
3、空指針優(yōu)化
1)、說明
Java15新特性,就是把NullPointerException異常的日志做了優(yōu)化打印的更人性化。
2)、案例
可以看到,提示會更有指向性,意味著以后在復(fù)雜的生產(chǎn)環(huán)境排錯過程中,你很可能不會再被空指針異常所困擾。

3)、注意
沒什么可注意的
4、文本塊
1)、說明
JDK15新特性,就是替代了以前String中一堆換行符和雙引號的簡潔版寫法,相信你很難不喜歡。
2)、案例
可以看到,就是三引號取代了雙引號,因為雙引號的內(nèi)容會夾帶一堆換行符,而三引號里面就單純是內(nèi)容,很清晰,而且雙引號換行需要杠n,而三引號直接換行即可生效。
/**
* <p>
* JDK15新特性:文本塊
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 23:36
*/
public class TextBlockDemo {
private static final String str =
"本以為大S和汪小菲的事情就這樣告一段落,彼此都過著屬于自己的幸福小日子,但是,被罵9天后,汪小菲口中的“窩囊廢”,終于有所行動了!\n"
+ "\n"
+ "也許具俊曄再也忍受不了別人罵自己窩囊廢,更不愿意住在別人房子內(nèi),所以11月30日,據(jù)媒體爆料,具俊曄這次要男人一把,決定買一套屬于自己的房子,"
+ "屬于自己的床墊,搬出汪小菲買的豪宅,來證明自己的實力。";
private static final String newStr = """
本以為大S和汪小菲的事情就這樣告一段落,彼此都過著屬于自己的幸福小日子,但是,被罵9天后,汪小菲口中的“窩囊廢”,終于有所行動了!
也許具俊曄再也忍受不了別人罵自己窩囊廢,更不愿意住在別人房子內(nèi),所以11月30日,據(jù)媒體爆料,具俊曄這次要男人一把,決定買一套屬于自己的房子,
屬于自己的床墊,搬出汪小菲買的豪宅,來證明自己的實力。
""";
public static void main(String[] args) {
System.err.println(str);
System.err.println("------------------------------------------------");
System.err.println(newStr);
}
}
3)、注意
文本塊只有一個要注意的地方,就是默認三引號內(nèi)的內(nèi)容沒有縮進,想要縮進的話要以末尾三引號為參照物向后偏移來確定縮進量。
下面一張圖會直接展示給你效果:

5、智能轉(zhuǎn)型
1)、說明
Java16新特性,就是幫你對instanceof做了增強,智能轉(zhuǎn)換變量類型。
2)、案例
可以對比old和new兩種寫法,第二種就是將第一種簡化了,類型轉(zhuǎn)換+聲明變量+后續(xù)邏輯判斷一起搞定。
/**
* <p>
* JDK16新特性:智能轉(zhuǎn)型
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/3 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 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 21:52
*/
public record RecordDemo(int type, String typeName) {
private void test() {
System.err.println(type + " | " + typeName);
}
public static void main(String[] args) {
// 這里new的時候帶的參數(shù)其實就是類的屬性,等于聲明屬性+訪問構(gòu)造方法二合一。
RecordDemo recordDemo = new RecordDemo(100, "葡萄牙");
recordDemo.test();
}
}
上面的新寫法等同于下面這種老的寫法,一看就懂。
/**
* <p>
* RecordDemo的寫法等同于這個類
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客棧】
* @since 2022/12/2 21:55
*/
public final class RecordCustomDemo {
final int type;
final String typeName;
public int type() {
return type;
}
public String name() {
return typeName;
}
public RecordCustomDemo(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;
RecordCustomDemo that = (RecordCustomDemo) o;
return type == that.type && Objects.equals(typeName, that.typeName);
}
@Override
public int hashCode() {
return Objects.hash(type, typeName);
}
}
3)、注意
沒什么可注意的,就跟使用正常的類差不多,只是自動生成了以下內(nèi)容:
1)、括號里的參數(shù)就是該類的屬性,且是final類型;
2)、自動生成一個帶有該屬性的構(gòu)造器;
3)、自動生成該屬性的訪問器,如xx.type()、xx.typeName();
4)、生成了equals和hashCode方法。
如果還是不懂,就理解成lombok中的@Data注解即可,同樣的意思。
7、密封類和接口
1)、說明
Java17新特性,密封類和密封接口。
使用sealed關(guān)鍵字聲明的類,可以通過設(shè)置permits關(guān)鍵字來控制哪些子類可以繼承它。
使用sealed關(guān)鍵字聲明的接口,可以通過設(shè)置permits關(guān)鍵字來控制哪些類可以實現(xiàn)它。。
簡單來講,就是爸爸規(guī)定哪個兒子能繼承財產(chǎn)。
2)、案例
看下密封類的寫法
用sealed聲明一個類,設(shè)置permits授權(quán)哪幾個子類可以繼承它。
/**
* <p>
* JDK17新特性:密封類
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 17:20
*/
public sealed class Daddy permits Son1, Son2 {
}
第一個兒子可以繼承
/**
* <p>
*
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 17:22
*/
public final class Son1 extends Daddy {
}
第二個兒子可以繼承
/**
* <p>
*
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 17:23
*/
public final class Son2 extends Daddy {
}
第三個兒子估計是不孝子
可以看到IDEA是有錯誤提示的,意思是沒有被sealed聲明的父類所允許。

然后,我們來看看密封接口。
其實和密封類差不多,但還可以結(jié)合前面講過的record來簡化代碼。
/**
* <p>
* JDK17新特性:密封接口
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 18:03
*/
public sealed interface DaddyInterface permits Son4, Son5 {
void test();
}
第四個兒子可以實現(xiàn)父親的愿望,用了record之后簡化了變量聲明及一些內(nèi)置方法的實現(xiàn)。
/**
* <p>
*
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 17:23
*/
public record Son4(int age, String name) implements DaddyInterface {
@Override
public void test() {
}
}
第五個兒子可以實現(xiàn)父親的愿望
/**
* <p>
*
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/2 17:23
*/
public record Son5(int age, String name) implements DaddyInterface {
@Override
public void test() {
}
}
第六個兒子有點傻,不能實現(xiàn)父親的愿望,可以看到錯誤提示和前面密封類一樣。

3)、注意
1)、sealed聲明的父類,它的子類只能用final、sealed、non-sealed來修飾;
2)、sealed聲明的父類,至少要有一個子類;
3)、沒有在permits中授權(quán)的子類,無法繼承父類;
4)、密封接口和密封類的注意點沒什么區(qū)別;
4)、密封接口結(jié)合record來完成可以少寫更多代碼變得更加簡潔。
這里特別說一點,sealed和record其實在Java新特性模式匹配中很有意義,但是我認為模式匹配對于從未了解過的Java程序員來講有些晦澀,結(jié)合sealed后有種套娃傳銷的感覺,如果難于理解就不是我們本來的目的,因此沒有專門花費章節(jié)贅述,結(jié)尾會簡單說一下我對模式匹配的看法。
8、switch增強
1)、說明
為什么把switch單獨放最后講,因為Java14-17分別對其做了增強,放在最后匯總起來講更直觀。
一共有三個改變:
1)、支持箭頭語法;
2)、支持表達式;
3)、支持case null。
2)、案例
箭頭語法,其實就是把舊寫法中的冒號和break直接換成了箭頭來代替,更簡潔。
/**
* <p>
* JDK14新特性:switch箭頭語法
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/3 11:05
*/
public class SwitchDemo1 {
private static void foods(int i) {
switch (i) {
case 1:
System.err.println("青椒肉絲");
break;
case 2:
System.err.println("番茄炒蛋");
break;
default:
System.err.println("米飯");
}
}
private static void fruits(int i) {
switch (i) {
case 1 -> System.err.println("香蕉");
case 2 -> System.err.println("獼猴桃");
default -> System.err.println("蘋果");
}
}
public static void main(String[] args) {
foods(1);
fruits(3);
}
}
支持表達式,其實就是能寫單個或多個表達式來return。
/**
* <p>
* JDK14新特性:switch支持表達式
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/3 11:05
*/
public class SwitchDemo3 {
/**
* 單表達式
*/
private static String fruits(int i) {
return switch (i) {
case 1 -> "香蕉";
case 2 -> "獼猴桃";
default -> "蘋果";
};
}
/**
* 多表達式
*/
private static String fruitsNew(int i) {
return switch (i) {
case 1, 2 -> {
System.err.println("----------------------------------");
System.err.println("來一個香蕉");
yield "香蕉來咯";
}
case 3, 4 -> {
System.err.println("----------------------------------");
System.err.println("來一個獼猴桃");
yield"獼猴桃來咯";
}
default -> {
System.err.println("----------------------------------");
System.err.println("沒的選就來個蘋果吧");
yield "蘋果來咯";
}
};
}
public static void main(String[] args) {
// System.err.println(fruits(2));
// System.err.println(fruits(3));
System.err.println(fruitsNew(2));
System.err.println(fruitsNew(4));
System.err.println(fruitsNew(5));
}
}
支持case null,其實就是case可以接收null值了。
/**
* <p>
* JDK17新特性(預(yù)覽):switch支持case null
* </p>
*
* @author 程序員濟癲,公眾號:【Java分享客?!? * @since 2022/12/3 11:05
*/
public class SwitchDemo2 {
private static void foods(String s) {
if (s == null) {
return;
}
switch (s) {
case "1":
System.err.println("青椒肉絲");
break;
case "2":
System.err.println("番茄炒蛋");
break;
default:
System.err.println("米飯");
}
}
private static void fruits(String s) {
switch (s) {
case "1" -> System.err.println("香蕉");
case null -> System.err.println("null");
default -> System.err.println("蘋果");
}
}
public static void main(String[] args) {
foods("1");
fruits(null);
}
}
可以看下效果,直接傳null也不會報錯。

3)、注意
1)、箭頭語法,冒號和箭頭不能同時存在;
2)、表達式,多個表達式時要使用花括號并使用yield關(guān)鍵字返回;
3)、case null是預(yù)覽功能,在IDEA - Project Structure - Modules中選擇Language Level為17(Preview)才能編譯通過,參考前面的準備工作。
總結(jié)
1)、Java9-17的新特性不僅于此,還有一些挺有特點的內(nèi)容,比如不可變集合、模塊化、String和Stream的API增強等等,但是我個人認為不具有代表性,要么是工具能直接幫你轉(zhuǎn)換,要么就是你大概率用不到,所以就沒列出來;
2)、模式匹配,是不少Java程序員關(guān)注的內(nèi)容,本篇中record、switch、密封類和接口的內(nèi)容其實都是模式匹配的基礎(chǔ),但模式匹配對Java來講并不成熟,《Thinking In Java》的作者也說過可能要好幾年才會看到完整形式的Java模式匹配,所以沒必要現(xiàn)在就花太多心思去研究一個殘缺版本,這個特性和Python、Kotlin、Scala相比其實還差得遠。
Java發(fā)展趨勢
最后稍微說下不少人關(guān)心的這個問題,我覺得只要你了解過Java8之后這些版本的新特性和預(yù)覽特性,你一定可以發(fā)現(xiàn)Java在嘗試改變,這是一個很好的信號。
就比如上面的這些新特性,你甚至能找到不少Python、JavaScript等語言的影子,Go語言作為新語言就是站在巨人肩膀上發(fā)展起來的,吸納了很多語言的優(yōu)秀特點。
現(xiàn)在,Java也在走類似的路,能明顯看到它在將一些優(yōu)秀語言的亮點容納到自己的新版本中,這種趨勢代表著一個意義:Java在不斷進步。
網(wǎng)上一直充斥著一些看衰Java的言論,沒必要當真,你必須自己去體會生態(tài)和了解國內(nèi)的IT公司動態(tài)才能有所感受。
Java依然是國內(nèi)使用最廣泛的語言,并且具備最龐大的生態(tài),這不是一朝一夕可以替代的,是市場規(guī)律發(fā)展的結(jié)果。
SpringBoot3.0支持Java17,Jenkins新版支持Java17,Kafka4.0直接拋棄Java8,ElasticSearch8.x最低支持JDK17,還有IDEA2022默認支持Java17,等等之類的開源社區(qū)和生態(tài)都在給新版的Java背書,更有微軟宣布全面擁抱Java,這里面不單單是技術(shù)層面的提高,更有利益的訴求和捆綁。
從這一點來說,學習Java完全是值得的,作為一門成熟優(yōu)秀且嚴謹?shù)恼Z言,它就像一個白領(lǐng)一樣正襟危坐。
我還認為現(xiàn)在學習和掌握Java的工程師,未來轉(zhuǎn)去其他語言也不會有壓力,上面的新特性就說明了這種方向,它在吸納其他語言的亮點。
未來可以預(yù)期的是,你轉(zhuǎn)到其他語言會逐漸變的沒有那么陌生和晦澀,這是我看到的一個方向,未來的編程語言發(fā)展大方向就是大融合(大抄襲,咳咳),你中有我,我中有你。
原創(chuàng)文章純手打,一個一個字敲出來的,鍵盤上全是血,如果覺得有幫助麻煩點個贊和收藏吧~
本人致力于分享工作中的經(jīng)驗及趣事,喜歡的話可以進主頁關(guān)注一下哦~
更多最新技術(shù)文章可關(guān)注GZH:【Java分享客?!?/p>