中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java IO知識點有哪些

發布時間:2021-11-18 10:12:54 來源:億速云 閱讀:164 作者:iii 欄目:編程語言

本篇內容介紹了“java IO知識點有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

一、概覽

Java 的 I/O 大概可以分成以下幾類:

  • 磁盤操作:File

  • 字節操作:InputStream 和 OutputStream

  • 字符操作:Reader 和 Writer

  • 對象操作:Serializable

  • 網絡操作:Socket

  • 新的輸入/輸出:NIO

二、磁盤操作

File 類可以用于表示文件和目錄的信息,但是它不表示文件的內容。

遞歸地列出一個目錄下所有文件:

public static void listAllFiles(File dir) {
   
   
   if (dir == null || !dir.exists()) {
   
   
   return;}if (dir.isFile()) {
   
   
   System.out.println(dir.getName());return;}for (File file : dir.listFiles()) {
   
   
   listAllFiles(file);}}

從 Java7 開始,可以使用 Paths 和 Files 代替 File。

三、字節操作

實現文件復制

public static void copyFile(String src, String dist) throws IOException {
   
   
   FileInputStream in = new FileInputStream(src);FileOutputStream out = new FileOutputStream(dist);byte[] buffer = new byte[20 * 1024];int cnt;// read() 最多讀取 buffer.length 個字節// 返回的是實際讀取的個數// 返回 -1 的時候表示讀到 eof,即文件尾while ((cnt = in.read(buffer, 0, buffer.length)) != -1) {
   
   
   out.write(buffer, 0, cnt);}in.close();out.close();}

裝飾者模式

Java I/O 使用了裝飾者模式來實現。以 InputStream 為例,

  • InputStream 是抽象組件;

  • FileInputStream 是 InputStream 的子類,屬于具體組件,提供了字節流的輸入操作;

  • FilterInputStream 屬于抽象裝飾者,裝飾者用于裝飾組件,為組件提供額外的功能。例如 BufferedInputStream 為 FileInputStream 提供緩存的功能。

java IO知識點有哪些


實例化一個具有緩存功能的字節流對象時,只需要在 FileInputStream 對象上再套一層 BufferedInputStream 對象即可。

FileInputStream fileInputStream = new FileInputStream(filePath);BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

DataInputStream 裝飾者提供了對更多數據類型進行輸入的操作,比如 int、double 等基本類型。

四、字符操作

編碼與解碼

編碼就是把字符轉換為字節,而解碼是把字節重新組合成字符。

如果編碼和解碼過程使用不同的編碼方式那么就出現了亂碼。

  • GBK 編碼中,中文字符占 2 個字節,英文字符占 1 個字節;

  • UTF-8 編碼中,中文字符占 3 個字節,英文字符占 1 個字節;

  • UTF-16be 編碼中,中文字符和英文字符都占 2 個字節。

UTF-16be 中的 be 指的是 Big Endian,也就是大端。相應地也有 UTF-16le,le 指的是 Little Endian,也就是小端。

Java 的內存編碼使用雙字節編碼 UTF-16be,這不是指 Java 只支持這一種編碼方式,而是說 char 這種類型使用 UTF-16be 進行編碼。char 類型占 16 位,也就是兩個字節,Java 使用這種雙字節編碼是為了讓一個中文或者一個英文都能使用一個 char 來存儲。

String 的編碼方式

String 可以看成一個字符序列,可以指定一個編碼方式將它編碼為字節序列,也可以指定一個編碼方式將一個字節序列解碼為 String。

String str1 = "中文";byte[] bytes = str1.getBytes("UTF-8");String str2 = new String(bytes, "UTF-8");System.out.println(str2);

在調用無參數 getBytes() 方法時,默認的編碼方式不是 UTF-16be。雙字節編碼的好處是可以使用一個 char 存儲中文和英文,而將 String 轉為 bytes[] 字節數組就不再需要這個好處,因此也就不再需要雙字節編碼。getBytes() 的默認編碼方式與平臺有關,一般為 UTF-8。

byte[] bytes = str1.getBytes();

Reader 與 Writer

不管是磁盤還是網絡傳輸,最小的存儲單元都是字節,而不是字符。但是在程序中操作的通常是字符形式的數據,因此需要提供對字符進行操作的方法。

  • InputStreamReader 實現從字節流解碼成字符流;

  • OutputStreamWriter 實現字符流編碼成為字節流。

實現逐行輸出文本文件的內容

public static void readFileContent(String filePath) throws IOException {
   
   
   FileReader fileReader = new FileReader(filePath);BufferedReader bufferedReader = new BufferedReader(fileReader);String line;while ((line = bufferedReader.readLine()) != null) {
   
   
   System.out.println(line);}// 裝飾者模式使得 BufferedReader 組合了一個 Reader 對象// 在調用 BufferedReader 的 close() 方法時會去調用 Reader 的 close() 方法// 因此只要一個 close() 調用即可bufferedReader.close();}

五、對象操作

序列化

序列化就是將一個對象轉換成字節序列,方便存儲和傳輸。

  • 序列化:ObjectOutputStream.writeObject()

  • 反序列化:ObjectInputStream.readObject()

不會對靜態變量進行序列化,因為序列化只是保存對象的狀態,靜態變量屬于類的狀態。

Serializable

序列化的類需要實現 Serializable 接口,它只是一個標準,沒有任何方法需要實現,但是如果不去實現它的話而進行序列化,會拋出異常。

public static void main(String[] args) throws IOException, ClassNotFoundException {
   
   
   A a1 = new A(123, "abc");String objectFile = "file/a1";ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile));objectOutputStream.writeObject(a1);objectOutputStream.close();ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(objectFile));A a2 = (A) objectInputStream.readObject();objectInputStream.close();System.out.println(a2);}private static class A implements Serializable {
   
   
   private int x;private String y;A(int x, String y) {
   
   
   this.x = x;this.y = y;}@Overridepublic String toString() {
   
   
   return "x = " + x + "  " + "y = " + y;}}

transient

transient 關鍵字可以使一些屬性不會被序列化。

ArrayList 中存儲數據的數組 elementData 是用 transient 修飾的,因為這個數組是動態擴展的,并不是所有的空間都被使用,因此就不需要所有的內容都被序列化。通過重寫序列化和反序列化方法,使得可以只序列化數組中有內容的那部分數據。

private transient Object[] elementData;

六、網絡操作

Java 中的網絡支持:

  • InetAddress:用于表示網絡上的硬件資源,即 IP 地址;

  • URL:統一資源定位符;

  • Sockets:使用 TCP 協議實現網絡通信;

  • Datagram:使用 UDP 協議實現網絡通信。

InetAddress

沒有公有的構造函數,只能通過靜態方法來創建實例。

InetAddress.getByName(String host);InetAddress.getByAddress(byte[] address);

URL

可以直接從 URL 中讀取字節流數據。

public static void main(String[] args) throws IOException {
   
   
   URL url = new URL("http://www.baidu.com");/* 字節流 */InputStream is = url.openStream();/* 字符流 */InputStreamReader isr = new InputStreamReader(is, "utf-8");/* 提供緩存功能 */BufferedReader br = new BufferedReader(isr);String line;while ((line = br.readLine()) != null) {
   
   
   System.out.println(line);}br.close();}

Sockets

  • ServerSocket:服務器端類

  • Socket:客戶端類

  • 服務器和客戶端通過 InputStream 和 OutputStream 進行輸入輸出。

java IO知識點有哪些


 

Datagram

  • DatagramSocket:通信類

  • DatagramPacket:數據包類

七、NIO

新的輸入/輸出 (NIO) 庫是在 JDK 1.4 中引入的,彌補了原來的 I/O 的不足,提供了高速的、面向塊的 I/O。

流與塊

I/O 與 NIO 最重要的區別是數據打包和傳輸的方式,I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據。

面向流的 I/O 一次處理一個字節數據:一個輸入流產生一個字節數據,一個輸出流消費一個字節數據。為流式數據創建過濾器非常容易,鏈接幾個過濾器,以便每個過濾器只負責復雜處理機制的一部分。不利的一面是,面向流的 I/O 通常相當慢。

面向塊的 I/O 一次處理一個數據塊,按塊處理數據比按流處理數據要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優雅性和簡單性。

I/O 包和 NIO 已經很好地集成了,java.io.* 已經以 NIO 為基礎重新實現了,所以現在它可以利用 NIO 的一些特性。例如,java.io.* 包中的一些類包含以塊的形式讀寫數據的方法,這使得即使在面向流的系統中,處理速度也會更快。

通道與緩沖區

1. 通道

通道 Channel 是對原 I/O 包中的流的模擬,可以通過它讀取和寫入數據。

通道與流的不同之處在于,流只能在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類),而通道是雙向的,可以用于讀、寫或者同時用于讀寫。

通道包括以下類型:

  • FileChannel:從文件中讀寫數據;

  • DatagramChannel:通過 UDP 讀寫網絡中數據;

  • SocketChannel:通過 TCP 讀寫網絡中數據;

  • ServerSocketChannel:可以監聽新進來的 TCP 連接,對每一個新進來的連接都會創建一個 SocketChannel。

2. 緩沖區

發送給一個通道的所有數據都必須首先放到緩沖區中,同樣地,從通道中讀取的任何數據都要先讀到緩沖區中。也就是說,不會直接對通道進行讀寫數據,而是要先經過緩沖區。

緩沖區實質上是一個數組,但它不僅僅是一個數組。緩沖區提供了對數據的結構化訪問,而且還可以跟蹤系統的讀/寫進程。

緩沖區包括以下類型:

  • ByteBuffer

  • CharBuffer

  • ShortBuffer

  • IntBuffer

  • LongBuffer

  • FloatBuffer

  • DoubleBuffer

緩沖區狀態變量

  • capacity:最大容量;

  • position:當前已經讀寫的字節數;

  • limit:還可以讀寫的字節數。

狀態變量的改變過程舉例:

① 新建一個大小為 8 個字節的緩沖區,此時 position 為 0,而 limit = capacity = 8。capacity 變量不會改變,下面的討論會忽略它。

java IO知識點有哪些


② 從輸入通道中讀取 5 個字節數據寫入緩沖區中,此時 position 為 5,limit 保持不變。

java IO知識點有哪些


③ 在將緩沖區的數據寫到輸出通道之前,需要先調用 flip() 方法,這個方法將 limit 設置為當前 position,并將 position 設置為 0。

java IO知識點有哪些


④ 從緩沖區中取 4 個字節到輸出緩沖中,此時 position 設為 4。

java IO知識點有哪些


⑤ 最后需要調用 clear() 方法來清空緩沖區,此時 position 和 limit 都被設置為最初位置。

java IO知識點有哪些


 

文件 NIO 實例

以下展示了使用 NIO 快速復制文件的實例:

public static void fastCopy(String src, String dist) throws IOException {
   
   
   /* 獲得源文件的輸入字節流 */FileInputStream fin = new FileInputStream(src);/* 獲取輸入字節流的文件通道 */FileChannel fcin = fin.getChannel();/* 獲取目標文件的輸出字節流 */FileOutputStream fout = new FileOutputStream(dist);/* 獲取輸出字節流的文件通道 */FileChannel fcout = fout.getChannel();/* 為緩沖區分配 1024 個字節 */ByteBuffer buffer = ByteBuffer.allocateDirect(1024);while (true) {
   
   
   /* 從輸入通道中讀取數據到緩沖區中 */int r = fcin.read(buffer);/* read() 返回 -1 表示 EOF */if (r == -1) {
   
   
   break;}/* 切換讀寫 */buffer.flip();/* 把緩沖區的內容寫入輸出文件中 */fcout.write(buffer);/* 清空緩沖區 */buffer.clear();}}

選擇器

NIO 常常被叫做非阻塞 IO,主要是因為 NIO 在網絡通信中的非阻塞特性被廣泛使用。

NIO 實現了 IO 多路復用中的 Reactor 模型,一個線程 Thread 使用一個選擇器 Selector 通過輪詢的方式去監聽多個通道 Channel 上的事件,從而讓一個線程就可以處理多個事件。

通過配置監聽的通道 Channel 為非阻塞,那么當 Channel 上的 IO 事件還未到達時,就不會進入阻塞狀態一直等待,而是繼續輪詢其它 Channel,找到 IO 事件已經到達的 Channel 執行。

因為創建和切換線程的開銷很大,因此使用一個線程來處理多個事件而不是一個線程處理一個事件,對于 IO 密集型的應用具有很好地性能。

應該注意的是,只有套接字 Channel 才能配置為非阻塞,而 FileChannel 不能,為 FileChannel 配置非阻塞也沒有意義。

java IO知識點有哪些


 

1. 創建選擇器

Selector selector = Selector.open();

2. 將通道注冊到選擇器上

ServerSocketChannel ssChannel = ServerSocketChannel.open();ssChannel.configureBlocking(false);ssChannel.register(selector, SelectionKey.OP_ACCEPT);

通道必須配置為非阻塞模式,否則使用選擇器就沒有任何意義了,因為如果通道在某個事件上被阻塞,那么服務器就不能響應其它事件,必須等待這個事件處理完畢才能去處理其它事件,顯然這和選擇器的作用背道而馳。

在將通道注冊到選擇器上時,還需要指定要注冊的具體事件,主要有以下幾類:

  • SelectionKey.OP_CONNECT

  • SelectionKey.OP_ACCEPT

  • SelectionKey.OP_READ

  • SelectionKey.OP_WRITE

它們在 SelectionKey 的定義如下:

public static final int OP_READ = 1 << 0;public static final int OP_WRITE = 1 << 2;public static final int OP_CONNECT = 1 << 3;public static final int OP_ACCEPT = 1 << 4;

可以看出每個事件可以被當成一個位域,從而組成事件集整數。例如:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

3. 監聽事件

int num = selector.select();

使用 select() 來監聽到達的事件,它會一直阻塞直到有至少一個事件到達。

4. 獲取到達的事件

Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = keys.iterator();while (keyIterator.hasNext()) {
   
   
   SelectionKey key = keyIterator.next();if (key.isAcceptable()) {
   
   
   // ...} else if (key.isReadable()) {
   
   
   // ...}keyIterator.remove();}

5. 事件循環

因為一次 select() 調用不能處理完所有的事件,并且服務器端有可能需要一直監聽事件,因此服務器端處理事件的代碼一般會放在一個死循環內。

while (true) {
   
   
   int num = selector.select();Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = keys.iterator();while (keyIterator.hasNext()) {
   
   
   SelectionKey key = keyIterator.next();if (key.isAcceptable()) {
   
   
   // ...} else if (key.isReadable()) {
   
   
   // ...}keyIterator.remove();}}

套接字 NIO 實例

public class NIOServer {
   
   
   public static void main(String[] args) throws IOException {
   
   
   Selector selector = Selector.open();ServerSocketChannel ssChannel = ServerSocketChannel.open();ssChannel.configureBlocking(false);ssChannel.register(selector, SelectionKey.OP_ACCEPT);ServerSocket serverSocket = ssChannel.socket();InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);serverSocket.bind(address);while (true) {
   
   
   selector.select();Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = keys.iterator();while (keyIterator.hasNext()) {
   
   
   SelectionKey key = keyIterator.next();if (key.isAcceptable()) {
   
   
   ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();// 服務器會為每個新連接創建一個 SocketChannelSocketChannel sChannel = ssChannel1.accept();sChannel.configureBlocking(false);// 這個新連接主要用于從客戶端讀取數據sChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {
   
   
   SocketChannel sChannel = (SocketChannel) key.channel();System.out.println(readDataFromSocketChannel(sChannel));sChannel.close();}keyIterator.remove();}}}private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {
   
   
   ByteBuffer buffer = ByteBuffer.allocate(1024);StringBuilder data = new StringBuilder();while (true) {
   
   
   buffer.clear();int n = sChannel.read(buffer);if (n == -1) {
   
   
   break;}buffer.flip();int limit = buffer.limit();char[] dst = new char[limit];for (int i = 0; i < limit; i++) {
   
   
   dst[i] = (char) buffer.get(i);}data.append(dst);buffer.clear();}return data.toString();}}
public class NIOClient {
   
   
   public static void main(String[] args) throws IOException {
   
   
   Socket socket = new Socket("127.0.0.1", 8888);OutputStream out = socket.getOutputStream();String s = "hello world";out.write(s.getBytes());out.close();}}

內存映射文件

內存映射文件 I/O 是一種讀和寫文件數據的方法,它可以比常規的基于流或者基于通道的 I/O 快得多。

向內存映射文件寫入可能是危險的,只是改變數組的單個元素這樣的簡單操作,就可能會直接修改磁盤上的文件。修改數據與將數據保存到磁盤是沒有分開的。

下面代碼行將文件的前 1024 個字節映射到內存中,map() 方法返回一個 MappedByteBuffer,它是 ByteBuffer 的子類。因此,可以像使用其他任何 ByteBuffer 一樣使用新映射的緩沖區,操作系統會在需要時負責執行映射。

MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

對比

NIO 與普通 I/O 的區別主要有以下兩點:

  • NIO 是非阻塞的;

  • NIO 面向塊,I/O 面向流。

“java IO知識點有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

沁源县| 天峻县| 霍山县| 外汇| 桦甸市| 六盘水市| 古蔺县| 敦化市| 金华市| 玉树县| 连州市| 西乌| 如东县| 大英县| 桃园县| 临武县| 文山县| 玛沁县| 河北省| 台安县| 通化县| 化德县| 西吉县| 河东区| 渭源县| 台江县| 安陆市| 高密市| 崇义县| 建昌县| 达日县| 南江县| 宿松县| 榆林市| 宜阳县| 馆陶县| 德格县| 新乐市| 阳西县| 伊吾县| 额尔古纳市|