大家好,我是哪吒。
很多朋友問我,如何才能學好IO流,對各種流的概念,云里霧里的,不求甚解。用到的時候,現百度,功能雖然實現了,但是為什么用這個?不知道。更別說效率問題了~
下次再遇到,再百度,“良性循環”。
今天,我就用一天的時間,整理一下關于Java I/O流的知識點,分享給大家。
每一種IO流,都配有示例代碼,大家可以跟著敲一遍,找找感覺~
本篇文章介紹Java NIO以及其它的各種奇葩流。
Java NIO (New I/O) 是 Java 1.4 引入的,在 Java 7 中又進行了一些增強。NIO 可以提高 I/O 操作的效率,它的核心是通道 (Channel) 和緩沖區 (Buffer)。
Channel 是一種新的 I/O 抽象,它與傳統的 InputStream 和 OutputStream 不同,Channel 可以同時進行讀和寫操作,而且可以對其進行更細粒度的控制。Java NIO 中最基本的 Channel 包括:
使用FileChannel從源文件中讀取內容并將其寫入到目標文件。
import java.io.FileInputStream; // 引入 FileInputStream 類import java.io.FileOutputStream; // 引入 FileOutputStream 類import java.nio.ByteBuffer; // 引入 ByteBuffer 類import java.nio.channels.FileChannel; // 引入 FileChannel 類public class FileChannelExample { public static void main(String[] args) { String sourceFile = "source.txt"; String targetFile = "target.txt"; try { // 使用 FileInputStream 和 FileOutputStream 打開源文件和目標文件 FileInputStream fileInputStream = new FileInputStream(sourceFile); FileOutputStream fileOutputStream = new FileOutputStream(targetFile); // 獲取 FileChannel 對象 FileChannel sourceChannel = fileInputStream.getChannel(); FileChannel targetChannel = fileOutputStream.getChannel(); // 創建 ByteBuffer 對象 ByteBuffer buffer = ByteBuffer.allocate(1024); // 從源文件中讀取內容并將其寫入目標文件 while (sourceChannel.read(buffer) != -1) { buffer.flip(); // 準備寫入(flip buffer) targetChannel.write(buffer); // 向目標文件寫入數據 buffer.clear(); // 緩沖區清空(clear buffer) } // 關閉所有的 FileChannel、FileInputStream 和 FileOutputStream 對象 sourceChannel.close(); targetChannel.close(); fileInputStream.close(); fileOutputStream.close(); // 打印成功信息 System.out.println("文件復制成功!"); } catch (Exception e) { e.printStackTrace(); } }}
用于 UDP 協議的數據讀寫操作。
使用DatagramChannel從一個端口讀取數據并將數據發送到另一個端口。
import java.io.IOException; // 引入 IOException 類import java.InetSocketAddress; // 引入 InetSocketAddress 類import java.nio.ByteBuffer; // 引入 ByteBuffer 類import java.nio.channels.DatagramChannel; // 引入 DatagramChannel 類public class DatagramChannelExample { public static void main(String[] args) { int receivePort = 8888; int sendPort = 9999; try { // 創建 DatagramChannel 對象 DatagramChannel receiveChannel = DatagramChannel.open(); // 綁定接收端口 receiveChannel.socket().bind(new InetSocketAddress(receivePort)); System.out.println("接收端口 " + receivePort + " 正在等待數據..."); // 創建數據緩沖區對象 ByteBuffer receiveBuffer = ByteBuffer.allocate(1024); // 從 receiveChannel 接收數據 receiveChannel.receive(receiveBuffer); // 顯示收到的數據 System.out.println("收到的數據是:" + new String(receiveBuffer.array())); // 關閉 receiveChannel 對象 receiveChannel.close(); // 創建 DatagramChannel 對象 DatagramChannel sendChannel = DatagramChannel.open(); // 創建數據緩沖區對象 ByteBuffer sendBuffer = ByteBuffer.allocate(1024); // 向數據緩沖區寫入數據 sendBuffer.clear(); sendBuffer.put("Hello World".getBytes()); sendBuffer.flip(); // 發送數據到指定端口 sendChannel.send(sendBuffer, new InetSocketAddress("localhost", sendPort)); System.out.println("數據已發送到端口 " + sendPort); // 關閉 sendChannel 對象 sendChannel.close(); } catch (IOException e) { e.printStackTrace(); } }}
用于 TCP 協議的數據讀寫操作。
下面是一個簡單的示例,演示如何使用 SocketChannel 和 ServerSocketChannel 進行基本的 TCP 數據讀寫操作。
import java.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;public class TCPExample { public static void main(String[] args) throws Exception { // 創建 ServerSocketChannel 并綁定端口 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); // 創建一個 ByteBuffer 用于接收數據 ByteBuffer buf = ByteBuffer.allocate(1024); // 等待客戶端連接 while (true) { SocketChannel socketChannel = serverSocketChannel.accept(); if (socketChannel != null) { // 客戶端已連接,從 SocketChannel 中讀取數據 int bytesRead = socketChannel.read(buf); while (bytesRead != -1) { // 處理讀取到的數據 System.out.println(new String(buf.array(), 0, bytesRead)); // 清空 ByteBuffer,進行下一次讀取 buf.clear(); bytesRead = socketChannel.read(buf); } } } }}
示例代碼說明:
需要注意的點:
如果想要向客戶端發送數據,可以使用以下代碼:
// 創建一個 ByteBuffer 用于發送數據ByteBuffer buf = ByteBuffer.wrap("Hello, world!".getBytes());// 向客戶端發送數據socketChannel.write(buf);
Buffer 是一個對象,它包含一些要寫入或要讀出的數據。在 NIO 中,Buffer 可以被看作為一個字節數組,但是它的讀取和寫入操作比直接的字節數組更加高效。NIO 中最常用的 Buffer 類型包括:
字節緩沖區,最常用的緩沖區類型,用于對字節數據的讀寫操作。
import java.nio.ByteBuffer;public class ByteBufferExample { public static void main(String[] args) { // 創建一個新的字節緩沖區,初始容量為10個字節 ByteBuffer buffer = ByteBuffer.allocate(10); // 向緩沖區中寫入4個字節 buffer.put((byte) 1); buffer.put((byte) 2); buffer.put((byte) 3); buffer.put((byte) 4); // 輸出緩沖區中的內容 buffer.flip(); // 將緩沖區切換成讀模式 System.out.println(buffer.get()); // 輸出1 System.out.println(buffer.get()); // 輸出2 System.out.println(buffer.get()); // 輸出3 System.out.println(buffer.get()); // 輸出4 // 將緩沖區清空并重新寫入數據 buffer.clear(); buffer.put((byte) 5); buffer.put((byte) 6); buffer.put((byte) 7); buffer.put((byte) 8); // 輸出緩沖區中的內容,方法同上 buffer.flip(); System.out.println(buffer.get()); // 輸出5 System.out.println(buffer.get()); // 輸出6 System.out.println(buffer.get()); // 輸出7 System.out.println(buffer.get()); // 輸出8 }}
示例代碼說明:
字符緩沖區,用于對字符數據的讀寫操作。
import java.nio.CharBuffer;public class CharBufferExample { public static void main(String[] args) { // 創建一個新的字符緩沖區,初始容量為10個字符 CharBuffer buffer = CharBuffer.allocate(10); // 向緩沖區中寫入4個字符 buffer.put('a'); buffer.put('b'); buffer.put('c'); buffer.put('d'); // 輸出緩沖區中的內容 buffer.flip(); // 將緩沖區切換成讀模式 System.out.println(buffer.get()); // 輸出a System.out.println(buffer.get()); // 輸出b System.out.println(buffer.get()); // 輸出c System.out.println(buffer.get()); // 輸出d // 將緩沖區清空并重新寫入數據 buffer.clear(); buffer.put('e'); buffer.put('f'); buffer.put('g'); buffer.put('h'); // 輸出緩沖區中的內容,方法同上 buffer.flip(); System.out.println(buffer.get()); // 輸出e System.out.println(buffer.get()); // 輸出f System.out.println(buffer.get()); // 輸出g System.out.println(buffer.get()); // 輸出h }}
示例代碼說明:
import java.nio.*;public class BasicBufferExample { public static void main(String[] args) { // 創建各種基本數據類型的緩沖區,初始容量為10 ShortBuffer shortBuf = ShortBuffer.allocate(10); IntBuffer intBuf = IntBuffer.allocate(10); LongBuffer longBuf = LongBuffer.allocate(10); FloatBuffer floatBuf = FloatBuffer.allocate(10); DoubleBuffer doubleBuf = DoubleBuffer.allocate(10); // 向緩沖區中寫入數據 shortBuf.put((short) 1); intBuf.put(2); longBuf.put(3L); floatBuf.put(4.0f); doubleBuf.put(5.0); // 反轉緩沖區,切換到讀模式 shortBuf.flip(); intBuf.flip(); longBuf.flip(); floatBuf.flip(); doubleBuf.flip(); // 讀取緩沖區中的數據 System.out.println(shortBuf.get()); // 輸出1 System.out.println(intBuf.get()); // 輸出2 System.out.println(longBuf.get()); // 輸出3 System.out.println(floatBuf.get()); // 輸出4.0 System.out.println(doubleBuf.get()); // 輸出5.0 }}
示例代碼說明:
Selector 是 Java NIO 類庫中的一個重要組件,它用于監聽多個 Channel 的事件。在一個線程中,通過 Selector 可以監聽多個 Channel 的 IO 事件,并實現了基于事件響應的架構。Selector 可以讓單個線程處理多個 Channel,因此它可以提高多路復用的效率。
import java.io.IOException;import java.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;import java.util.Set;public class SelectorExample { public static void main(String[] args) throws IOException { // 創建一個ServerSocketChannel,監聽本地端口 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8080)); serverSocketChannel.configureBlocking(false); // 創建一個Selector,并將serverSocketChannel注冊到Selector上 Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server started on port 8080"); while (true) { // 如果沒有任何事件發生,則阻塞等待 selector.select(); // 處理事件 Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { // 處理新的連接請求 ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = serverChannel.accept(); clientChannel.configureBlocking(false); System.out.println("Accepted connection from " + clientChannel.getRemoteAddress()); clientChannel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // 處理讀事件 SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = clientChannel.read(buffer); String message = new String(buffer.array(), 0, bytesRead); System.out.println("Received message from " + clientChannel.getRemoteAddress() + ": " + message); // 回寫數據 ByteBuffer outputBuffer = ByteBuffer.wrap(("Echo: " + message).getBytes()); clientChannel.write(outputBuffer); } // 從待處理事件集合中移除當前事件 keyIterator.remove(); } } }}
ZipInputStream 和 ZipOutputStream 可以用于處理 ZIP 文件格式,ZipInputStream 可以從 ZIP 文件中讀取數據,ZipOutputStream 可以向 ZIP 文件中寫入數據。
import java.io.*;import java.util.zip.ZipEntry;import java.util.zip.ZipOutputStream;public class ZipExample { public static void main(String[] args) throws IOException { // 輸入文件路徑和輸出壓縮文件路徑 String inputFile = "/path/to/input/file"; String outputFile = "/path/to/output/file.zip"; // 創建ZipOutputStream,并設置壓縮級別 ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outputFile)); zipOutputStream.setLevel(9); // 讀取需要壓縮的文件到文件輸入流 FileInputStream fileInputStream = new FileInputStream(inputFile); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); // 設置壓縮文件內部的名稱 ZipEntry zipEntry = new ZipEntry(inputFile); zipOutputStream.putNextEntry(zipEntry); // 寫入壓縮文件 byte[] buf = new byte[1024]; int len; while ((len = bufferedInputStream.read(buf)) > 0) { zipOutputStream.write(buf, 0, len); } bufferedInputStream.close(); zipOutputStream.closeEntry(); zipOutputStream.close(); System.out.println("File compressed successfully"); }}
示例代碼說明:
import java.io.*;import java.util.zip.ZipEntry;import java.util.zip.ZipInputStream;public class UnzipExample { public static void main(String[] args) throws IOException { // 輸入壓縮文件路徑和輸出文件路徑 String inputFile = "/path/to/input/file.zip"; String outputFile = "/path/to/output/file"; // 創建ZipInputStream ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inputFile)); // 循環讀取壓縮文件中的條目 ZipEntry zipEntry = zipInputStream.getNextEntry(); while (zipEntry != null) { // 如果是目錄,則創建空目錄 if (zipEntry.isDirectory()) { new File(outputFile + File.separator + zipEntry.getName()).mkdirs(); } else { // 如果是文件,則輸出文件 FileOutputStream fileOutputStream = new FileOutputStream(outputFile + File.separator + zipEntry.getName()); byte[] buf = new byte[1024]; int len; while ((len = zipInputStream.read(buf)) > 0) { fileOutputStream.write(buf, 0, len); } fileOutputStream.close(); } zipInputStream.closeEntry(); zipEntry = zipInputStream.getNextEntry(); } zipInputStream.close(); System.out.println("File uncompressed successfully"); }}
示例代碼說明:
GZIPInputStream 和 GZIPOutputStream 可以用于進行 GZIP 壓縮,GZIPInputStream 可以從壓縮文件中讀取數據,GZIPOutputStream 可以將數據寫入壓縮文件中。
import java.io.*;import java.util.zip.GZIPOutputStream;public class GzipExample { public static void main(String[] args) throws IOException { // 輸入文件路徑和輸出壓縮文件路徑 String inputFile = "/path/to/input/file"; String outputFile = "/path/to/output/file.gz"; // 創建GZIPOutputStream,并設置壓縮級別 GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream(outputFile)); gzipOutputStream.setLevel(9); // 讀取需要壓縮的文件到文件輸入流 FileInputStream fileInputStream = new FileInputStream(inputFile); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); // 寫入壓縮文件 byte[] buf = new byte[1024]; int len; while ((len = bufferedInputStream.read(buf)) > 0) { gzipOutputStream.write(buf, 0, len); } bufferedInputStream.close(); gzipOutputStream.close(); System.out.println("File compressed successfully"); }}
示例代碼說明:
import java.io.*;import java.util.zip.GZIPInputStream;public class GunzipExample { public static void main(String[] args) throws IOException { // 輸入壓縮文件路徑和輸出文件路徑 String inputFile = "/path/to/input/file.gz"; String outputFile = "/path/to/output/file"; // 創建GZIPInputStream GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream(inputFile)); // 輸出文件 FileOutputStream fileOutputStream = new FileOutputStream(outputFile); byte[] buf = new byte[1024]; int len; while ((len = gzipInputStream.read(buf)) > 0) { fileOutputStream.write(buf, 0, len); } gzipInputStream.close(); fileOutputStream.close(); System.out.println("File uncompressed successfully"); }}
示例代碼說明:
ByteArrayInputStream 和 ByteArrayOutputStream 分別是 ByteArrayInputStream 和 ByteArrayOutputStream 類的子類,它們可以用于對字節數組進行讀寫操作。
import java.io.ByteArrayInputStream;import java.io.ByteArrayInputStream;import java.io.IOException;public class ByteArrayInputStreamExample { public static void main(String[] args) throws IOException { // 用字符串初始化一個字節數組,作為輸入數據源 String input = "Hello, world!"; byte[] inputBytes = input.getBytes(); // 創建一個ByteArrayInputStream,使用輸入數據源 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes); // 讀取并輸出輸入流中的數據 byte[] buf = new byte[1024]; int len; while ((len = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, len)); } // 關閉輸入流 inputStream.close(); }}
示例代碼說明:
import java.io.ByteArrayOutputStream;import java.io.IOException;public class ByteArrayOutputStreamExample { public static void main(String[] args) { String input = "Hello World!"; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] output; try { outputStream.write(input.getBytes()); output = outputStream.toByteArray(); System.out.println(new String(output)); } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } finally { try { outputStream.close(); } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } } }}
示例代碼說明:
本文為您講解了 Java I/O、NIO 以及其他一些流的基本概念、用法和區別。Java I/O 和 NIO 可以完成很多復雜的輸入輸出操作,包括文件操作、網絡編程、序列化等。其他流技術可以實現壓縮、讀寫字節數組等功能。在進行開發時,根據具體需求選擇不同的流技術可以提高程序效率和開發效率。
本文轉載自微信公眾號「哪吒編程」,可以通過以下二維碼關注。轉載本文請聯系哪吒編程公眾號。
本文鏈接:http://www.tebozhan.com/showinfo-26-150-0.html一文搞定Java NIO,以及各種奇葩流
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com