三、網絡編程
1.網絡編程概述
Java 是 Internet 上的語言,它從語言級上提供了對網絡應用程序的支持,程序員能夠很容易開發常見的網絡應用程序。
2.網絡的基礎
(1)計算機網絡
把分布在不同地理區域的計算機與專門的外部設備用通訊線路互聯成一個規模大,功能強的網絡系統,從而使眾多的計算機可以方便地互傳信息,共享硬件,軟件,數據信息等資源。
(2)網絡編程的目的
直接或間接的通過網絡協議與其他計算機實現數據互換,進行通訊。
3.網絡通訊的要素概述
(1)通訊雙方的地址:IP 地址,端口號
(2)一定的規則,即網絡通訊的協議,有兩套參考模型:
① OSI 參考模型:模型過于理想化,未能在因特網上進行廣泛推廣
② TCP/IP 參考模型(TCP/IP協議):事實上的國際標準
(3)網絡通訊要素1:IP 地址和端口號
1)IP 地址:InetAddress
a. 唯一的標識 Internet 上的計算機(通訊實體)
b. 本地環回地址(127.0.0.1):127.0.0.1/localhost
c. IP地址的分類方式1:IPV4 和 IPV6
a) IPV4:4 個字節組成,4 個 0~255,大概 42 億,30 億都在北美,亞洲 4 億,2011 年初已經用完,以點分十進制表示,比如:192.16.0.211
b) IPV6: 128位(16個字節),寫成 8 個無符號整數,每個整數用四個十六進制位表示,數之間用冒號(:)分開,例如:3ffe:3201:1401:1280:c8ff:fe4d:db39:984
d. IP 地址分類方式 2:公網地址(萬維網使用)和私有地址(局域網使用)。其中 192.168. 開頭的就是私有地址,范圍是 192.168.0.0~192.168.255.255,專門為組織機構內部使用。
2)端口號標識正在計算機上運行的進程(程序)
a. 不同的進程有不同的端口號,這樣同一個操作系統中同時允許多個進程進行通訊。
b. 被規定為一個 16 位的整數:0~65535
c. 端口的分類
a) 公認端口:0~1023,被預先定義的服務通訊占用(如:HTTP 占用端口號 80,FTP 占用 21 端口,Telnet 占用23端口),程序員不能去占用這些端口,
b) 注冊端口:1024~49151,分配給用戶進程或應用程序(如 Tomcat 占用端口 8080,MySQL 占用端口3306,Oracle 占用端口 1521)
c) 動態/私有端口:49152~65535
d. 端口號和 IP 地址組合得到一個網絡套接字:Socket
3)InetAddress 類:Java 中表示 IP 地址的類
a. Internet 上的主機又有兩種表示方式:
a) 域名(hostName):www.baidu.com,每個域名都對應了一個或多個IP地址,通過域名服務器(DNS)將域名解析成 IP 地址的形成來通訊,域名比 IP 地址更容易記憶。
b) IP 地址(hostAddress):39.156.66.14
b. InetAddress 類主要表示 IP 地址,有兩個子類:Inet4Address,Inet6Address。
c. InetAddress 類對象包含有一個 Internet 主機地址的域名或IP地址:www.baidu.com或39.156.66.14
d. 域名容易記憶,當在連接網絡時輸入一個主機的域名后,域名服務器(DNS)負責將域名轉換為IP地址,這樣才能和主機建立連接。----域名解析
e. InetAddress 類沒有提供公共的構造器,而是提供了如下幾個靜態方法來獲取 InetAddress:
public static InetAddress getLocalHost(); //獲取本機對應的 InetAddress
public static InetAddress getByName(String host); //通過主機名(域名)或IP地址獲取InetAddress
f. InetAddress 常用的方法
public String getHostAddress():返回 IP 地址字符串(以文本表現形式)
public String getHostName():獲取此 IP 地址的主機名
public boolean isReachable(int timeout):測試是否可以達到該地址
示例:
(4)網絡通訊的要素2:網絡協議
1)網絡通訊協議
計算機網絡中實現通訊必須要有一些約定,即通信協議,對速率,傳輸代碼,代碼結構,傳輸控制步驟,出錯控制等指定標準。
2)問題:網絡協議太復雜
計算機網絡通訊涉及內部比較多,比如指定源地址和目的地地址,加密解密,壓縮解壓縮,差錯控制,路由控制,如何實現如此復雜的網絡協議呢
3)通信協議分層的思想
在指定協議時,把復雜成分分解成一些簡單的成分,再將它們復合起來,最常用的復合方式就是層次方式,即同層間可以通訊,上一層可以調用下一層,而與再一層不發生關系,各層互不影響,利于系統的開發和擴展。
4)TCP/IP 協議簇
a. 傳輸層協議中有兩個非常重要的協議:
傳輸層控制協議 TCP(Transmission Control Protocol)
用戶數據報協議 UDP(User Datagram Protocol)
b. TCP/IP 以其兩個主要協議:傳輸層控制協議(TCP)和網絡互聯協議(IP)而得名,實際上是一組協議,包括多個具有不同功能且互為關聯的協議
c. IP(Internet Protocol)協議是網絡層的主要協議,支持網間互聯的數據通訊。
d. TCP/IP 協議模型從更實用的角度出發,形成了高效的四層體系結構,即物理鏈路層,IP層,傳輸層,應用層。
5)TCP 和 UDP
a. TCP 協議:
使用 TCP 協議前,需要建立 TCP 連接,形成傳輸數據通道,
傳輸前,采用“三次握手”方式,點對點通信,是可靠的通信
TCP 協議進行通信的兩個應用程序:客戶端和服務器
在連接中可進行大數據量的傳輸
傳輸完畢,需要釋放已建立的連接,效率低。
b. UDP 協議
將數據,源地址,目的地封裝成數據報,不需要建立連接
每個數據報的大小限制在 64K 內
發送不管對方是否準備好,接收方收到也不確認,所以是不可靠
可以廣播發送
發送數據結束時無需釋放資源,開銷小,速度快。
(5)Socket 編程
1)利用套接字(Socket)開發網絡應用程序已被廣泛使用,以至于成為了事實上的標準
2)網絡上具有唯一標識的 IP 地址和端口號組合在一起才能構成唯一能識別的標識符套接字
3)通信兩端都要有 Socket,是兩臺機器通信的端點。
4)網絡通信其實就是 Socket 通訊
5)Socket 允許程序把網絡連接當成一個流,數據在兩個 Socket 間通過 IO 傳輸
6)一般主動發送通信的應用程序是客戶端,等待通信請求的是服務端。
7)Socket 分類
a. 流套接字(Stream Socket):使用 TCP 提供的字節流服務
b. 數據報套接字(Datagram Socket):使用 UDP 提供“盡力而為”的數據報服務
8)Socket 類的常用構造器:
public Socket(InetAddress address,int port):創建一個流套接字(TCP)并將其連接到指定IP地址和端口號。
public Socket(String host,int port):創建一個流套接字并將其連接到指定主機上的指定端口。
9)Socket類的常用方法:
public InputStream getInputStream():返回此套接字的輸入流。可以用于接收網絡消息。
public OutputStream getOutputStream():返回此套接字的輸出流。可以用于發送網絡消息
public InetAddress getInetAddress():此套接字連接到的遠程IP地址;如果套接字是未連接的,則返回null。
public InetAddress getLocalAddress():獲取套接字綁定的本地地址。即本地的IP地址
public int getPort():此套接字連接到的遠程端口號;如果套接字尚未連接,返回0
public int getLocalPort():返回此套接字綁定到的本地端口。如果尚未綁定套接字,則返回-1.
public void close():關閉此套接字。套接字被關閉后,便不可在以后的網絡中使用(即無法重新連接或重新綁定)。需要創建新的套接字對象。關閉此套接字也將會關閉該套接字的 InputStream 和 OutputStream
public void shutdownInput():如果在此套接字上調用該方法后從套接字輸入流讀取內容,則流將返回 EOF(文件結束符)。即不能從此套接字的輸入流中接收任何數據。
public void shutdownOutput():禁用此套接字的輸出流。即不能通過此套接字的輸出流發送任何數據。
(6)TCP 網絡編程:基于 Socket 的 TCP 編程
示例:實現服務器端可以同時接收多個客戶端上傳文件
服務端:
package com.edu.net;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//文件上傳多線程服務端
public class TCPServer {
?? ?public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//1. 創建一個帶緩沖的線程池
ExecutorService threadPool = Executors.newCachedThreadPool();
//2. 創建服務端的 Socket,監聽端口,等待客戶端連接上來
ServerSocket serverSocket = new ServerSocket(6666);
//實現多個客戶端連接服務器的操作
while(true) {
//3. 注意:此時返回的 socket 就是指當前連接上來的客戶端 Socket
Socket socket = serverSocket.accept();//該方法會阻塞,直到有客戶端連接上來為止
//4. 一旦有一個客戶端連接上來,我們就創建一個線程來處理該客戶端的請求
Runnable r = () -> {
try {
// 5. 顯示一下當前是哪個客戶端連接上來了
InetAddress ipAddr = socket.getInetAddress();//得到客戶端的 IP 地址
String ip = ipAddr.getHostAddress();//獲取 IP 地址字符串
System.out.println("客戶端:" + ip + ",連接到服務器...");
//6. 獲得 socket 的輸入流
InputStream in = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
// 7. 創建輸出流,指定上傳文件的目錄,比如我們需要在 F 盤中創建一個 upload 目錄
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:/upload/" + ip + "(" +?
System.currentTimeMillis() + ").jpg"));
byte[] buf = new byte[1024];
int len = -1;
while((len = bis.read(buf)) != -1) {
//寫入文件
bos.write(buf, 0, len);
}
//8. 完成之后輸出一些信息給客戶端
OutputStream out = socket.getOutputStream();
out.write("圖片上傳成功".getBytes());
//9. 關閉資源
out.close();
bos.close();
bis.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
//將上面的任務放到線程池中執行
threadPool.execute(r);
}
}
}
客戶端:
package com.edu.net;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
?? ?public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//1. 創建客戶端 Socket,指定服務器的 IP 地址和端口去連接服務器
Socket socket = new Socket("192.168.110.56", 6666);
//2. 獲取 Socket 輸出流,把圖片文件寫給服務器
OutputStream out = socket.getOutputStream();
//3. 創建輸入流,讀取本地圖片文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:/img.jpg"));
//4. 寫出圖片數據給服務器
byte[] buf = new byte[1024];
int len = -1;
while((len = bis.read(buf)) != -1) {
out.write(buf, 0, len);
}
//5. 寫完之后,關閉輸出流
socket.shutdownOutput();
//6. 讀取服務器反饋的信息
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int length = in.read(buffer);
System.out.println(new String(buffer, 0, length));
//7. 關閉資源
in.close();
bis.close();
out.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(7)UDP網絡編程:UDP網絡通訊
1)類 DatagramSocket 和 DatagramPacket 實現了基于 UDP 協議的網絡程序
2)UDP 數據報通過數據報套接字 DatagramSocket 發送和接收,系統不保證 UDP 數據報能夠安全的到達目的地,也不能確定什么時候能到達。
3)DatagramPacket 對象封裝了 UDP 數據報,在數據報中包含了發送端 IP 地址和端口號以及接收端的 IP 地址和端口。
4)UDP 協議中每個數據報都給出完整的地址信息,因此無需建立發送方和接收方的連接,類似與發快遞包裹一樣。
5)DatagramSocket 類的常用方法
public DatagramSocket(int port):創建數據報套接字并將其綁定在本機指定端口。套接字將被綁定到通配符地址,IP 地址由內核來選擇。
public DatagramSocket(int port, InetAddress Iaddr):創建數據報套接字,將其綁定到指定本地地址,本地端口必須在0到65535之間,如果 IP 地址是 0.0.0.0,套接字將被綁定到通配符地址,IP 地址由內核選擇。
public void close(): 關閉此數據報套接字。
public void send(DatagramPacket p) : 從此套接字發送數據報報。DatagramPacket 包含的信息指示:將要發送的數據、其長度、遠程主機的IP地址和遠程主機的端口號。
public void receive(DatagramPacket p):從此套接字接收數據報報。當此方法返回時,DatagramPacket 的緩沖區填充了接收的數據。數據報報也包含發送方的IP地址和端口號。此方法在接收到數據報前一直阻塞。數據報包對象的 length 字段包含所接收信息的長度。如果信息比報的長度長,該信息將被截斷。
public InetAddress getLocalAddress():獲取套接字綁定的本地地址。
public int getLocalPort():返回此套接字綁定的本地主機上的端口號。
public InetAddress getInetAddress():返回此套接字連接的地址。如果套接字未連接,則返回null。
public int getPort():返回此套接字的端口。如果套接字未連接,則返回-1.
public DatagramPacket(byte[] buf,int length):構造 DatagramPacket,用來接收長度為 length 的數據包。length 參數必須小于等于buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port):構造數據報報,用來將長度為length 的包發送到指定主機上的指定端口號。length 參數必須小于等于 buf.length。
public InetAddress getAddress():返回某臺機器的IP地址,此數據報將要發往該機器或者是從該機器接收到的。
public int getPort():返回某臺遠程主機的端口號,此數據報將要發往該主機或者是從該主機接收到的。
public byte[] getData():返回數據緩沖區。接收到的或將要發送的數據從緩沖區的偏移量 offset 處開始,持續length 長度。
public int getLength():返回將要發送或接收到的數據的長度。
6)流程:
a. 構造 DatagramSocket 和 DatagramPacket
b. 建立發送端和接收端
c. 建立數據報
d. 調用 Socket 發送,接收
e. 關閉Socket
發送端和接收端是兩個獨立的程序
示例:
發送端:
package com.edu.net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Sender {
public static void main(String[] args) {
try {
//構建一個用戶數據報套接字
DatagramSocket socket = new DatagramSocket();
//準備數據
String str = "UDP 數據報測試數據";
byte[] bytes = str.getBytes();
//這里不能用str.length(),因為字符串是2個字節表示一個字符,我們這里需要的是多少個byte字節
int length = str.getBytes().length;
//構建一個數據報 DataGramPacket,指定要發送的數據,長度,以及要去向的目的地的IP地址和端口
DatagramPacket packet = new DatagramPacket(bytes, length, InetAddress.getByName("192.168.110.565"), 8888);
//發送數據報
socket.send(packet);
//關閉
socket.close();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
接收端:
package com.edu.net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Receiver {
?? ?public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//創建DataGramSocket,監聽 8888 端口
DatagramSocket socket = new DatagramSocket(8888);
//創建 buf 來接收數據
byte[] buf = new byte[1024];
int len = buf.length;
//構建數據報
DatagramPacket packet = new DatagramPacket(buf, len);
//接收數據報
socket.receive(packet);//該方法會阻塞直到收到發送方的數據報
System.out.println("接收到的數據:" + new String(buf, 0, len));
//關閉
socket.close();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
結果:先啟動接收端,再啟動發送端:
(8)URL 類
1)URL(Uniform Resource Locator):統一資源定位符,它表示 Internet 上某一個資源的地址
2)它是一種具體的 URI ,即 URL 可以用來標識一個資源,而且還指明了如何 locate 這個資源。
3)通過 URL 我們可以訪問 Internet 上的各種網絡資源,比如常見的 www,ftp 站點,瀏覽器通過解析給定的URL 可以在網絡上查找相應的文件或其他資源。
4)URL 的基本結構由5部分組成:
<傳輸協議>://<主機名/域名/IP地址>:<端口號>/文件名#片段名?查詢名列表
例如:
http://192.168.1.100:8080/helloworld/index.jsp#a?username=xiaoming&password=123
#片段名:即錨點,例如看小說,直接定位到章節
查詢名列表/請求參數:參數名=參數值&參數名=參數值
5)URL 類構造器
a. 為了表示 URL,java.net 中實現了類 URL,我們可以通過下面的構造器來初始化 URL 對象:
public URL(String spec):通過一個表示 URL 地址的字符串來構造一個URL對象
例如:URL url = new URL(“http://www.163.com”);
public URL(URL conext,String spec):通過基URL和相對URL構造一個URL對象。
例如:URL url= new URL(url,”download.html”); // http://www.163.com/download.html
public URL(String protocol,String host,String file)
例如:URL url = new URL(“http”,”www.163.com”,”download.html”); // http://www.163.com/download.html
public URL(String protocol,String host,int port,String file)
例如:URL url = new URL(“http”,”www.163.com”,80,”download.html”)
b. URL 類的構造器都聲明了拋出非運行時異常,必須要對這個異常進行處理,通常是加上try...catch語句進行捕獲。
(9)針對于 HTTP 協議 URLConnection 類
1)URL 類的方法 openConnection():能夠從網絡上讀取數據
2)若希望輸出數據,例如向服務端(Tomcat 服務器(基于 HTTP 協議的服務器))發送一些數據,則必須先與URL建立連接,然后才能對其進行讀寫,此時需要使用 URLConnection.
3)URLConnection:表示到 URL 所引用的遠程對象的連接,當與一個 URL 建立連接時,首先要在一個 URL 對象上通過 openConnection() 生成對應的 URLConnection 對象,如果連接失敗,將產生IOException,例如:
URL url = new URL(“http://localhost:8080/index.jsp”);
URLConnection conn = url.openConnection();
4)通過 URLConnection 對象獲取輸入和輸出流,就可以和 Tomcat 服務器進行交互,常見的方法有:
public Object getContent() throws IOException
public int getConentLength()
public String getContentType()
public long getDate()
public long getLastModified()
public InputStream getInputStream() throws IOException
public OutputStream getOutputStream() throws IOException
(10)URI
是 Uniform Resource Identifier,統一資源標識符,用來唯一的標識一個資源,而 URL 是Uniform Resource Locator,統一資源定位符,它是一種具體的 URI,即 URL 可以用來標識一個資源,而且還指明了如何 locate 這個資源,而URN,Uniform Resource Name,統一資源命名,是通過名字來標識資源,比如:mailto:xiaoming@126.com,也就是說,URI 是一種抽象的,高層次概念定義統一資源標識,而 URL 和 URN 則是具體的資源標識的方式,URL 和 URN 都是一種 URI。
四、JDK17 新特性
1. JDK8 和 JDK17共存:保持先前的 JDK8 的環境變量配置不變
安裝 JDK17:保持默認的路徑一路向前下一步。
(1) 在系統環境變量中新增 JDK8 和 JDK17 兩個環境變量,分別引用 JDK8 和 JDK17 的安裝目錄
(2) 然后將之前配置的系統環境變量 JAVA_HOME 改成如下:比如我們改來使用 JDK17
以后我們要切換回JDK8,那么就將“變量值”改為“%JDK8%”
然后系統環境變量Path的值保持原來的配置,然后驗證一下:
(3) Eclipse 中配置 JDK17
新建一個工程:
從 Springboot3.0 開始,已經不?持 JDK8 了。 從 3.0 開始,轉變為 JDK17。
Java17 是長期支持版本(LTS),于 2021 年 9 月發布,是繼 Java 11 之后的又一個重要的穩定版本。Java17 提供了許多語言特性和 API 改進,同時也移除了一些過時的功能,顯著提高了語言表達能力和運行時性能。
2.下面我們來看一下Java17的新特性:
1. 密封類(Sealed Classes)
2.1 什么是密封類?
密封類是一種新特性,允許開發者嚴格控制子類的繼承。通過定義一個密封類,可以限制哪些類能夠繼承它。
2.2 使用場景
更精確地控制繼承關系。
更好的代碼安全性和可讀性。
避免了意外的類擴展。
3.3 示例
2.4 關鍵字說明
sealed:定義密封類。
permits:指定允許繼承密封類的子類。
non-sealed:允許子類取消密封。
final:子類不能再被繼承。
3. var關鍵字
從 Java10 開始,var 被引?
var name = "zhangsan";
var age = 10;
上述代碼中,編譯器會?動推斷出 name 是?個 String 類型,age 是?個 int 類型。
為什么使? var?
使? var 可以使代碼更簡潔。有時候,類型名稱可能會?常?,例如泛型。var 就像是?個簡化器,讓你不必反復寫出繁瑣的類型名。
使?注意事項:
1. 不能使? var 來聲明字段(類屬性)
2. 不能使? var 來聲明?法參數
3. 不能使? var 來聲明?法返回類型
4. var 聲明變量必須初始化,但是不能初始化為 null
例如:
4. 文本塊
這個更新非常實用。在沒有這個特性之前,編寫長文本非常痛苦。雖然 IDEA 等集成開發工具可以自動處理,但最終效果仍然丑陋,充滿拼接符號。現在,通過字符串塊,我們可以輕松編寫 JSON、HTML、SQL 等內容,效果更清爽。
示例:
5. NullPointerException 增強
空指針異常(NPE)一直是 Java 程序員的痛點,因為報錯信息無法直觀地指出哪個對象為空,只拋出一個 NullPointerException 和一堆堆棧信息,定位問題耗時且麻煩。尤其在遇到級聯調用的代碼時,逐行排查更是令人頭疼。如果在測試環境中,可能還需通過遠程調試查明空對象,費時費力。Java17 終于在這方面取得了突破,提供了更詳細的空指針異常信息,幫助開發者迅速定位問題源頭。
6. Records
在 Java 中,POJO(普通的Java對象,可以理解為就是 JavaBean)對象(如DO、PO、VO、DTO等)通常包含成員變量及相應的 Getter 和 Setter 方法。盡管可以通過工具或 IDE 生成這些代碼,但修改和維護仍然麻煩。為此,Java 引入了標準解決方案:Records。它通過簡潔的語法定義數據類,大大簡化了 POJO 類的編寫,如下所示。雖然 hashcode 和 equals 方法仍需手動編寫,但 IDE 能夠自動生成。這一特性有效解決了模板代碼問題,提升了代碼整潔度和可維護性。
示例:
7. 全新的switch表達式
在 Java12 的時候就引入了 switch 表達式,注意這里是表達式,而不是語句,原來的 switch 是語句。主要的差別就是就是表達式有返回值,而語句則沒有。再配合模式匹配,以及 yield 和 “->” 符號的加入,全新的switch 用起來爽到飛起來。
示例:
package com.edu.jdk17;
public class SwitchDemo {
/**
* 在JDK8中獲取 switch 返回值方式
*
* @param week
* @return
*/
public static int getByJDK8(Week week) {
int i = 0;
switch (week) {
case MONDAY, TUESDAY:
i = 1;
break;
case WEDNESDAY:
i = 3;
break;
case THURSDAY:
i = 4;
break;
case FRIDAY:
i = 5;
break;
case SATURDAY:
i = 6;
break;
case SUNDAY:
i = 7;
break;
default:
i = 0;
break;
}
? ? ? ? return i;
}
? ? /**
* 在JDK17中獲取switch返回值
*
* @param week
* @return
*/
public static int getByJDK17(Week week) {
// 1, 現在的switch變成了表達式,可以返回值了,而且支持yield和->符號來返回值
// 2, 再也不用擔心漏寫了break,而導致出問題了
// 3, case后面支持寫多個條件
return switch (week) {
case MONDAY -> 1;
case TUESDAY -> 2;
case WEDNESDAY -> 3;
case THURSDAY -> 4;
case FRIDAY -> 5;
case SATURDAY, SUNDAY -> 6;
default -> 0;
};
}
? ? private enum Week {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
public static void main(String[] args) {
System.out.println(getByJDK17(Week.SATURDAY));
}
}
8. 私有接口方法
從 Java8 開始,允許在 interface 里面添加默認方法,其實當時就有些小困惑,如果一個 default 方法體很大怎么辦,拆到另外的類去寫嗎?實在有些不太合理,所以在 Java17 里面,如果一個 default 方法體很大,那么可以通過新增接口私有方法來進行一個合理的拆分了。
9. 模式匹配
在 JDK17 中,模式匹配主要用于 instanceof 表達式。模式匹配增強了 instanceof 的語法和功能,使類型檢查和類型轉換更加簡潔和高效。在傳統的 Java 版本中,我們通常使用 instanceof 結合類型轉換來判斷對象類型并進行處理,這往往會導致冗長的代碼。
10. 集合類的工廠方法
在 Java8 的年代,即便創建一個很小的集合,或者固定元素的集合都是比較麻煩的,為了簡潔一些,有時我甚至會引入一些依賴。
11. JDK17 的其他特性
(1) 新的String方法
repeat:重復生成字符串
isBlank:不用在引入第三方庫就可以實現字符串判空了
strip:去除字符串兩邊的空格,支持全角和半角,之前的trim只支持半角
lines:能根據一段字符串中的終止符提取出行為單位的流
indent:給字符串做縮進,接受一個int型的輸入
transform:接受一個轉換函數,實現字符串的轉換
(2) Stream API的增強
增加 takeWhile, dropWhile, ofNullable, iterate 以及 toList 的API,越來越像一些函數式語言了。
用法舉例:
(3) Jshell
在新的 JDK 版本中,支持直接在命令行下執行 java 程序,類似于 python 的交互式 REPL。簡而言之,使用 JShell,你可以輸入代碼片段并馬上看到運行結果,然后就可以根據需要作出調整,這樣在驗證一些簡單的代碼的時候,就可以通過 Jshell 得到快速地驗證,非常方便。
(4) java 命令直接執行 java 文件
在現在可以直接通過執行java xxx.java,即可運行該 java 文件,無須先執行 javac,然后再執行 java,是不是又簡單了一步。
(5) ZGC
在 ParallelOldGC、CMS 和 G1 之后,JDK11 引入了全新的 ZGC(Z Garbage Collector)。官方宣稱 ZGC 的垃圾回收停頓時間不超過10ms,能支持高達 16TB 的堆空間,并且停頓時間不會隨著堆的增大而增加。
那么,ZGC 到底解決了什么問題?Oracle 官方介紹它是一個可伸縮的低延遲垃圾回收器,旨在降低停頓時間,盡管這可能會導致吞吐量的降低。不過,通過橫向擴展服務器可以解決吞吐量問題。官方已建議 ZGC 可用于生產環境,這無疑將成為未來的主流垃圾回收器。