一文搞定Java I/O流,看看這些你就知道了!
大家好,我是哪吒。
很多朋友問我,如何才能學(xué)好IO流,對各種流的概念,云里霧里的,不求甚解。用到的時候,現(xiàn)百度,功能雖然實(shí)現(xiàn)了,但是為什么用這個?不知道。更別說效率問題了~
下次再遇到,再百度,“良性循環(huán)”。
今天,我就用一天的時間,整理一下關(guān)于Java I/O流的知識點(diǎn),分享給大家。
每一種IO流,都配有示例代碼,大家可以跟著敲一遍,找找感覺~
一、InputStream
InputStream 代表一個輸入流,它是一個抽象類,不能被實(shí)例化。InputStream 定義了一些通用方法,如 read() 和 skip() 等,用于從輸入流中讀取數(shù)據(jù)。常用的 InputStream 實(shí)現(xiàn)類包括:
1、FileInputStream的代碼示例
下面是使用 FileInputStream 讀取文件內(nèi)容的示例代碼:
import java.io.*;
public class FileInputStreamExample {
public static void main(String[] args) {
// 要讀取的文件路徑和名稱
String filePath = "C:/example/file.txt";
// 創(chuàng)建輸入流對象
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
byte[] buffer = new byte[1024];
int len;
// 使用 while 循環(huán)讀取文件,每次最多讀取 1024 個字節(jié)
while ((len = fis.read(buffer)) != -1) {
// 將讀取的字節(jié)轉(zhuǎn)換為字符串,并輸出到控制臺
String content = new String(buffer, 0, len, "UTF-8");
System.out.println(content);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filePath);
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
} finally {
// 關(guān)閉輸入流
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在示例中,我們首先指定要讀取的文件路徑和名稱。在實(shí)際使用中,您應(yīng)該將其替換為實(shí)際的文件路徑和名稱。
- 然后,我們使用文件路徑創(chuàng)建一個FileInputStream對象。注意,在創(chuàng)建FileInputStream對象時,我們必須提供要讀取的文件的路徑和名稱。
- 接著,我們使用while循環(huán)從FileInputStream對象中讀取數(shù)據(jù)。每次循環(huán)中,我們使用read()方法讀取最多1024字節(jié),并將讀取的字節(jié)存儲在一個緩沖區(qū)中。然后,我們將讀取的字節(jié)轉(zhuǎn)換為字符串,并在控制臺上輸出。在while循環(huán)結(jié)束后,我們關(guān)閉FileInputStream對象。
- 注意:在使用FileInputStream類時,我們需要確保文件存在,并且我們有讀取文件的權(quán)限。此外,在實(shí)際應(yīng)用中,可能需要根據(jù)需要使用更高效的方法讀取大型文件,以避免IO開銷的問題。
2、ByteArrayInputStream的代碼示例
下面是使用 ByteArrayInputStream 讀取字節(jié)數(shù)組內(nèi)容的示例代碼:
import java.io.*;
public class ByteArrayInputStreamExample {
public static void main(String[] args) {
byte[] bytes = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 };
// 創(chuàng)建字節(jié)輸入流對象
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try {
byte[] buffer = new byte[1024];
int len;
// 使用 while 循環(huán)讀取字節(jié)數(shù)組中的內(nèi)容,每次最多讀取 1024 個字節(jié)
while ((len = bais.read(buffer)) != -1) {
// 將讀取的字節(jié)轉(zhuǎn)換為字符串,并輸出到控制臺
String content = new String(buffer, 0, len, "UTF-8");
System.out.println(content);
}
} catch (IOException e) {
System.out.println("Error reading byte array: " + e.getMessage());
} finally {
// 關(guān)閉輸入流
try {
if (bais != null) {
bais.close();
}
} catch (IOException e) {
System.out.println("Error closing byte array input stream: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在示例中,我們首先創(chuàng)建了一個字節(jié)數(shù)組,其中包含ASCII碼為72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33的字符序列"Hello World!"。
- 然后,我們使用字節(jié)數(shù)組創(chuàng)建了一個ByteArrayInputStream對象。注意,在創(chuàng)建ByteArrayInputStream對象時,我們必須提供要從中讀取的字節(jié)數(shù)組。
- 接著,我們使用while循環(huán)從ByteArrayInputStream對象中讀取數(shù)據(jù)。每次循環(huán)中,我們使用read()方法讀取最多1024字節(jié),并將讀取的字節(jié)存儲在一個緩沖區(qū)中。然后,我們將讀取的字節(jié)轉(zhuǎn)換為字符串,并在控制臺上輸出。在while循環(huán)結(jié)束后,我們關(guān)閉ByteArrayInputStream對象。
- 注意:在使用ByteArrayInputStream類時,字節(jié)數(shù)組是一次性全部加載到內(nèi)存中的,如果字節(jié)數(shù)組較大,可能會導(dǎo)致內(nèi)存不足的問題。此外,在實(shí)際應(yīng)用中,可能需要使用更高效的數(shù)據(jù)源(如文件或網(wǎng)絡(luò)流)來存儲數(shù)據(jù),以避免內(nèi)存限制的問題。
3、PipedInputStream的代碼示例
PipedInputStream:管道輸入流,用于線程之間的通信。
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedInputStreamExample {
public static void main(String[] args) throws Exception {
// 創(chuàng)建一對PipedInputStream和PipedOutputStream
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream(input);
// 創(chuàng)建一個寫線程
Thread writerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 寫入一些數(shù)據(jù)到PipedOutputStream
output.write("Hello, World!".getBytes());
output.close(); // 關(guān)閉PipedOutputStream
} catch (IOException e) {
e.printStackTrace();
}
}
});
// 創(chuàng)建一個讀線程
Thread readerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 讀取PipedInputStream中的數(shù)據(jù)
int data;
while ((data = input.read()) != -1) {
System.out.print((char) data); // 將數(shù)據(jù)打印到控制臺
}
input.close(); // 關(guān)閉PipedInputStream
} catch (IOException e) {
e.printStackTrace();
}
}
});
// 啟動寫線程和讀線程
writerThread.start();
readerThread.start();
// 等待寫線程和讀線程完成
writerThread.join();
readerThread.join();
}
}
代碼說明
PipedInputStream和PipedOutputStream是Java IO庫提供的一對管道流,可以用于數(shù)據(jù)的發(fā)送和接收。
- 在示例中,我們首先創(chuàng)建了一個PipedInputStream和一個PipedOutputStream。它們被連接在一起,使得我們可以像使用一個普通的輸入流- 和輸出流一樣進(jìn)行讀寫操作。
- 接著,我們創(chuàng)建一個寫線程來向管道中寫入數(shù)據(jù)。在這個例子中,我們寫入了字符串"Hello, World!"。
- 然后,我們創(chuàng)建一個讀線程來從管道中讀取數(shù)據(jù)。我們使用了一個while循環(huán)來讀取數(shù)據(jù),直到遇到了流的末尾。
- 最后,我們啟動寫線程和讀線程,等待它們完成,然后關(guān)閉流。
- 注意:如果讀線程在數(shù)據(jù)被寫入管道之前就開始讀取流,它將會阻塞(即等待數(shù)據(jù)被寫入)。
- 另外,還要注意線程之間的同步問題。在這個例子中,我們使用了一個簡單的join()方法來等待寫線程和讀線程完成。在實(shí)際使用中,您可能需要使用更高級的同步機(jī)制來確保線程之間的正確協(xié)作。
二、 OutputStream
OutputStream 代表一個輸出流,它也是一個抽象類,不能被實(shí)例化。OutputStream 定義了一些通用方法,如 write() 和 flush() 等,用于向輸出流中寫入數(shù)據(jù)。常用的 OutputStream 實(shí)現(xiàn)類包括:
1、FileOutputStream代碼示例
文件輸出流,用于向文件中寫入數(shù)據(jù)。
import java.io.*;
public class FileOutputStreamExample {
public static void main(String[] args) {
// 要寫入的文件路徑和名稱
String filePath = "C:/example/output.txt";
// 要寫入文件的內(nèi)容
String content = "Hello, World!";
// 創(chuàng)建輸出流對象
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath);
// 將字符串轉(zhuǎn)換為字節(jié)數(shù)組,并將其寫入文件
fos.write(content.getBytes("UTF-8"));
// 刷新輸出流
fos.flush();
// 輸出提示信息
System.out.println("Content has been written to " + filePath);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filePath);
} catch (IOException e) {
System.out.println("Error writing file: " + e.getMessage());
} finally {
// 關(guān)閉輸出流
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在示例中,我們首先指定要寫入的文件路徑和名稱。在實(shí)際使用中,您應(yīng)該將其替換為實(shí)際的文件路徑和名稱。
- 然后,我們創(chuàng)建一個用于寫入文件的輸出流對象。在創(chuàng)建FileOutputStream對象時,如果文件不存在,Java將會自動創(chuàng)建它。
- 接著,我們將要寫入的內(nèi)容轉(zhuǎn)換為字節(jié)數(shù)組,并使用write()方法將其寫入文件。
- 然后,我們使用flush()方法刷新輸出流。在文件操作完成后,我們應(yīng)該始終調(diào)用flush()方法以確保所有數(shù)據(jù)都被寫入到磁盤上的文件中。
- 最后,我們關(guān)閉FileOutputStream對象,即使在發(fā)生異常時也應(yīng)該關(guān)閉。
- 注意:在使用FileOutputStream類時,我們需要確保文件存在,并且我們有寫入文件的權(quán)限。此外,在實(shí)際應(yīng)用中,可能需要使用更高效的方法來寫入大型文件,以避免IO開銷的問題。
2、ByteArrayOutputStream代碼示例:
字節(jié)數(shù)組輸出流,用于將數(shù)據(jù)寫入內(nèi)存中的字節(jié)數(shù)組中。
import java.io.*;
public class ByteArrayOutputStreamExample {
public static void main(String[] args) {
// 創(chuàng)建字節(jié)數(shù)組輸出流對象
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// 將字符串轉(zhuǎn)換為字節(jié)數(shù)組,并寫入到字節(jié)數(shù)組輸出流中
baos.write("Hello, World!".getBytes("UTF-8"));
// 將字節(jié)數(shù)組輸出流中的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組
byte[] bytes = baos.toByteArray();
// 將字節(jié)數(shù)組轉(zhuǎn)換為字符串,并輸出到控制臺
String content = new String(bytes, "UTF-8");
System.out.println(content);
} catch (IOException e) {
System.out.println("Error writing to byte array: " + e.getMessage());
} finally {
// 關(guān)閉字節(jié)數(shù)組輸出流
try {
if (baos != null) {
baos.close();
}
} catch (IOException e) {
System.out.println("Error closing byte array output stream: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在示例中,我們首先創(chuàng)建了一個ByteArrayOutputStream對象,用于向內(nèi)存中的字節(jié)數(shù)組中寫入數(shù)據(jù)。
- 然后,我們將要寫入的內(nèi)容轉(zhuǎn)換為字節(jié)數(shù)組,并使用write()方法將其寫入ByteArrayOutputStream對象。
- 接著,我們調(diào)用toByteArray()方法將ByteArrayOutputStream對象中的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組。需要注意的是,要在調(diào)用toByteArray()方法之前先關(guān)閉ByteArrayOutputStream。
- 最后,我們將字節(jié)數(shù)組轉(zhuǎn)換為字符串,并將其輸出到控制臺。
- 注意:在使用ByteArrayOutputStream類時,需要注意內(nèi)存占用問題。BytesArrayOutputStream類主要用于在內(nèi)存中臨時存儲數(shù)據(jù)。對于大數(shù)據(jù),可能需要使用其他方式存儲。
3、PipedOutputStream代碼示例:
管道輸出流,用于線程之間的通信。
import java.io.*;
public class PipedOutputStreamExample {
public static void main(String[] args) {
// 創(chuàng)建一對PipedInputStream和PipedOutputStream
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
try {
// 將輸入流和輸出流連接起來
input.connect(output);
// 創(chuàng)建一個寫線程
Thread writerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 寫入一些數(shù)據(jù)到PipedOutputStream
output.write("Hello, World!".getBytes("UTF-8"));
// 刷新PipedOutputStream
output.flush();
// 關(guān)閉PipedOutputStream
output.close();
} catch (IOException e) {
System.out.println("Error writing to pipe: " + e.getMessage());
}
}
});
// 創(chuàng)建一個讀線程
Thread readerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 讀取PipedInputStream中的數(shù)據(jù)
byte[] buffer = new byte[1024];
int len = input.read(buffer);
// 將讀取的字節(jié)轉(zhuǎn)換為字符串,并輸出到控制臺
String content = new String(buffer, 0, len, "UTF-8");
System.out.println(content);
// 關(guān)閉PipedInputStream
input.close();
} catch (IOException e) {
System.out.println("Error reading from pipe: " + e.getMessage());
}
}
});
// 啟動寫線程和讀線程
writerThread.start();
readerThread.start();
// 等待寫線程和讀線程完成
writerThread.join();
readerThread.join();
} catch (IOException | InterruptedException e) {
System.out.println("Error communicating between threads: " + e.getMessage());
}
}
}
示例代碼說明:
- 在示例中,我們首先創(chuàng)建了一對PipedInputStream和PipedOutputStream,用于在線程之間進(jìn)行通信。
- 接著,我們使用connect()方法將PipedInputStream和PipedOutputStream連接起來。
- 然后,我們創(chuàng)建一個寫線程和一個讀線程。在寫線程中,我們向PipedOutputStream寫入數(shù)據(jù),并使用flush()和close()方法刷新和關(guān)閉輸出流。在讀線程中,我們從PipedInputStream讀取數(shù)據(jù),并將其轉(zhuǎn)換為字符串并打印到控制臺。在讀操作完成后,我們關(guān)閉輸入流
- 最后,我們啟動寫線程和讀線程,并等待它們完成。
- 注意:在使用PipedInputStream和PipedOutputStream類時,需要考慮線程同步問題,以確保在線程之間正確地交換數(shù)據(jù)。在實(shí)際應(yīng)用中,您可能需要使用更高級的同步機(jī)制來確保線程之間的協(xié)作。
三、字符輸入流Reader
除了字節(jié)流,Java 還提供字符流,字符流類似于字節(jié)流,不同之處在于字符流是按字符讀寫數(shù)據(jù),而不是按字節(jié)。Java 中最基本的字符流是 Reader 和 Writer,它們是基于 InputStream 和 OutputStream 的轉(zhuǎn)換類,用于完成字節(jié)流與字符流之間的轉(zhuǎn)換。
常用的實(shí)現(xiàn)類包括 FileReader 和 InputStreamReader等
1、FileReader 代碼示例
import java.io.FileReader; // 引入 FileReader 類
import java.io.IOException; // 引入 IOException 類
public class FileReaderExample {
public static void main(String[] args) {
// 定義文件路徑
String filePath = "example.txt";
try {
// 創(chuàng)建 FileReader 對象
FileReader fileReader = new FileReader(filePath);
// 讀取字符
int character;
while ((character = fileReader.read()) != -1) {
// 打印字符
System.out.print((char) character);
}
// 關(guān)閉 FileReader 對象
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、InputStreamReader 代碼示例
import java.io.BufferedReader; // 引入 BufferedReader 類
import java.io.IOException; // 引入 IOException 類
import java.io.InputStreamReader; // 引入 InputStreamReader 類
public class InputStreamReaderExample {
public static void main(String[] args) {
try {
// 創(chuàng)建 InputStreamReader 對象
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
// 創(chuàng)建 BufferedReader 對象
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// 獲取用戶輸入
System.out.println("請輸入字符串:");
String inputString = bufferedReader.readLine();
// 打印用戶輸入
System.out.println("您輸入的字符串是:" + inputString);
// 關(guān)閉 BufferedReader 對象
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、字符輸出流Writer
1、FileWriter代碼示例
import java.io.*;
public class FileWriterExample {
public static void main(String[] args) {
FileWriter writer = null;
try {
writer = new FileWriter("example.txt");
writer.write("Hello World!");
writer.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在這個例子中,創(chuàng)建了一個FileWriter對象 writer,并將字符串"Hello World!"寫入文件 "example.txt"。然后,我們使用close()方法關(guān)閉寫入器以確保所有的數(shù)據(jù)都被刷新到磁盤。
- 注意:在使用FileWriter時,要確保在不再需要它時關(guān)閉它以確保所有的字符都被刷新到文件中。如果您使用Java7或更高版本,可以考慮使用try-with-resources語句,這樣您就不需要顯式地關(guān)閉寫入器。
2、OutputStreamWriter代碼示例
import java.io.*;
public class OutputStreamWriterExample {
public static void main(String[] args) {
FileOutputStream outputStream = null;
OutputStreamWriter writer = null;
try {
outputStream = new FileOutputStream("example.txt");
writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write("Hello World!");
writer.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
} finally {
try {
if (writer != null)
writer.close();
if (outputStream != null)
outputStream.close();
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
}
示例代碼說明:
- 在這個例子中,創(chuàng)建了一個FileOutputStream對象 outputStream 用于寫入文件 "example.txt"。接著,創(chuàng)建了一個OutputStreamWriter對象 writer,它被用于將字符串"Hello World!"寫入到文件中。指定了"UTF-8"作為字符編碼。
- 最后,使用close()方法關(guān)閉寫入器和輸出流以確保所有的數(shù)據(jù)都被刷新到磁盤上。
- 注意:在使用OutputStreamWriter時,要確保在不再需要它時關(guān)閉它以確保所有的字符都被刷新到文件中。如果您使用Java 7或更高版本,可以考慮使用try-with-resources語句,這樣您就不需要顯式地關(guān)閉寫入器和輸出流。
五、緩沖流
BufferedInputStream 和 BufferedOutputStream 是 I/O 包中提供的緩沖輸入輸出流。它們可以提高 I/O 操作的效率,具有較好的緩存機(jī)制,能夠減少磁盤操作,縮短文件傳輸時間。使用 BufferedInputStream 和 BufferedOutputStream 進(jìn)行讀取和寫入時,Java 會自動調(diào)整緩沖區(qū)的大小,使其能夠適應(yīng)不同的數(shù)據(jù)傳輸速度。
緩沖流代碼示例
import java.io.BufferedInputStream; // 引入 BufferedInputStream 類
import java.io.BufferedOutputStream; // 引入 BufferedOutputStream 類
import java.io.FileInputStream; // 引入 FileInputStream 類
import java.io.FileOutputStream; // 引入 FileOutputStream 類
import java.io.IOException; // 引入 IOException 類
public class BufferedStreamsExample {
public static void main(String[] args) {
String sourceFile = "source.txt";
String targetFile = "target.txt";
try {
// 創(chuàng)建 BufferedInputStream 和 BufferedOutputStream 對象
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(targetFile));
// 讀取數(shù)據(jù),直到讀取的內(nèi)容為-1
int data;
while ((data = bufferedInputStream.read()) != -1) {
bufferedOutputStream.write(data);
}
// 關(guān)閉 BufferedInputStream 和 BufferedOutputStream 對象
bufferedInputStream.close();
bufferedOutputStream.close();
// 打印成功信息
System.out.println("復(fù)制文件成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
六、對象流
可以讀取或?qū)懭?Java 對象的流,比較典型的對象流包括ObjectInputStream 和 ObjectOutputStream。
對象流需要將對象序列化和反序列化為字節(jié)序列,使用 ObjectInputStream 和 ObjectOutputStream 可以將 Java 對象轉(zhuǎn)換為字節(jié)流進(jìn)行傳輸或存儲。
在網(wǎng)絡(luò)傳輸和文件存儲中,ObjectInputStream 和 ObjectOutputStream 通常會被使用到。
對象流代碼示例
import java.io.FileInputStream; // 引入 FileInputStream 類
import java.io.FileOutputStream; // 引入 FileOutputStream 類
import java.io.ObjectInputStream; // 引入 ObjectInputStream 類
import java.io.ObjectOutputStream; // 引入 ObjectOutputStream 類
import java.io.Serializable; // 引入 Serializable 接口
class Person implements Serializable {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + name + "\n年齡:" + age;
}
}
public class ObjectStreamsExample {
public static void main(String[] args) {
String filePath = "person.dat";
// 創(chuàng)建 Person 對象
Person person = new Person("Alice", 20);
try {
// 創(chuàng)建 ObjectOutputStream 對象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
// 將 Person 對象寫入文件
objectOutputStream.writeObject(person);
// 關(guān)閉 ObjectOutputStream 對象
objectOutputStream.close();
// 創(chuàng)建 ObjectInputStream 對象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
// 從文件中讀取 Person 對象
Person personFromFile = (Person) objectInputStream.readObject();
// 關(guān)閉 ObjectInputStream 對象
objectInputStream.close();
// 打印讀取的對象
System.out.println(personFromFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
本文轉(zhuǎn)載自微信公眾號「哪吒編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系哪吒編程公眾號。