Java輸入流與輸出流超全面講解
一、輸入流與輸出流的定義及作用
定義
輸入流(Input Stream):在 Java 世界里,輸入流是一種機制,它負責從外部世界(像硬盤文件系統(tǒng)、網(wǎng)絡(luò)連接、內(nèi)存數(shù)組等數(shù)據(jù)源)讀取數(shù)據(jù)到程序內(nèi)部。
輸出流(Output Stream):輸出流則是將程序里的數(shù)據(jù)輸出到外部世界,外部世界可以是文件、網(wǎng)絡(luò)連接等目標位置。
作用
輸入流和輸出流是 Java 實現(xiàn)數(shù)據(jù)輸入輸出操作的核心,它們搭建起了程序與外部環(huán)境之間的數(shù)據(jù)交互橋梁,使得程序能夠讀取外部數(shù)據(jù)進行處理,也能將處理結(jié)果輸出到外部。
比如說把控制臺的數(shù)據(jù)讀入到程序當中。或者說把程序當中的數(shù)據(jù)輸出到控制臺
import java.io.IOException;
public class ReadByteByByte {
public static void main(String[] args) {
try {
System.out.println("請輸入一些內(nèi)容:");
int input;
while ((input = System.in.read()) != -1) {
System.out.print((char) input);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}二、輸入流和輸出流最基礎(chǔ)的類及方法使用
基礎(chǔ)類
Java 中,InputStream 和 OutputStream 分別是輸入流和輸出流的最基礎(chǔ)抽象類。它們以字節(jié)作為讀取和寫入數(shù)據(jù)的基本單元。
常用方法及使用示例
InputStream 類
int read():該方法從輸入流中讀取一個字節(jié)的數(shù)據(jù),返回值是該字節(jié)對應(yīng)的整數(shù)(范圍為 0 - 255)。當?shù)竭_流的末尾時,返回 -1。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class InputStreamExample {
public static void main(String[] args) {
try (InputStream is = new FileInputStream("input.txt")) {
int byteData;
while ((byteData = is.read()) != -1) {
System.out.print((char) byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}int read(byte[] b):嘗試將最多b.length個字節(jié)的數(shù)據(jù)讀入到字節(jié)數(shù)組b中,返回實際讀取的字節(jié)數(shù)。若到達流末尾,返回 -1。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class InputStreamReadArrayExample {
public static void main(String[] args) {
try (InputStream is = new FileInputStream("input.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
for (int i = 0; i < bytesRead; i++) {
System.out.print((char) buffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}OutputStream 類
void write(int b):將指定的字節(jié)寫入輸出流。這里的參數(shù)b雖然是int類型,但實際上只使用低 8 位。import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class OutputStreamExample { public static void main(String[] args) { try (OutputStream os = new FileOutputStream("output.txt")) { String data = "Hello, World!"; for (int i = 0; i < data.length(); i++) { os.write(data.charAt(i)); } } catch (IOException e) { e.printStackTrace(); } } }void write(byte[] b):將字節(jié)數(shù)組b中的所有字節(jié)寫入輸出流。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamWriteArrayExample {
public static void main(String[] args) {
try (OutputStream os = new FileOutputStream("output.txt")) {
String data = "Hello, Java!";
byte[] buffer = data.getBytes();
os.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}三、能否以其他單位進行讀取
雖然 InputStream 和 OutputStream 以字節(jié)為基礎(chǔ)單位進行讀取和寫入,但 Java 也提供了以其他單位進行操作的方式:
字符
可以使用字符流來以字符為單位進行讀取和寫入。字符流基于字節(jié)流構(gòu)建,會自動處理字符的編碼和解碼。例如 Reader 和 Writer 是字符流的抽象基類,FileReader 和 FileWriter 是具體實現(xiàn)類。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharacterStreamExample {
public static void main(String[] args) {
try (FileReader fr = new FileReader("input.txt");
FileWriter fw = new FileWriter("output.txt")) {
int charData;
while ((charData = fr.read()) != -1) {
fw.write(charData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}數(shù)字和對象
對于數(shù)字和對象,可以使用數(shù)據(jù)流和對象流。
數(shù)據(jù)流:
DataInputStream和DataOutputStream可以方便地讀寫基本數(shù)據(jù)類型(如int、double等)。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataStreamExample {
public static void main(String[] args) {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"));
DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) {
dos.writeInt(123);
int num = dis.readInt();
System.out.println(num);
} catch (IOException e) {
e.printStackTrace();
}
}
}對象流:
ObjectInputStream和ObjectOutputStream用于讀寫 Java 對象。對象需要實現(xiàn)Serializable接口才能進行序列化和反序列化。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class ObjectStreamExample {
public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person person = new Person("John", 30);
oos.writeObject(person);
Person readPerson = (Person) ois.readObject();
System.out.println("Name: " + readPerson.getName() + ", Age: " + readPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}四、字符流、對象流等與字節(jié)流的關(guān)系
字符流、對象流、數(shù)據(jù)流等都與字節(jié)流密切相關(guān),它們是在字節(jié)流的基礎(chǔ)上構(gòu)建起來的。
字符流:在字節(jié)流的基礎(chǔ)上添加了字符編碼協(xié)議。在讀取數(shù)據(jù)時,根據(jù)編碼協(xié)議讀取指定數(shù)量的字節(jié),然后進行解碼得到對應(yīng)的字符;在寫入數(shù)據(jù)時,將字符編碼為字節(jié)后通過字節(jié)流輸出。
對象流:輸入時先獲取對象的序列化結(jié)果(字節(jié)流),然后使用字節(jié)流讀取,再進行反序列化得到對象;輸出時將對象進行序列化得到字節(jié)流,然后使用字節(jié)流將字節(jié)流傳輸出去。
數(shù)據(jù)流:同樣是在字節(jié)流的基礎(chǔ)上,提供了讀寫基本數(shù)據(jù)類型的功能,會將基本數(shù)據(jù)類型轉(zhuǎn)換為字節(jié)進行讀寫。
綜上所述,字節(jié)流是 Java 輸入輸出流體系的基礎(chǔ),其他類型的流都是為了滿足不同的數(shù)據(jù)處理需求而在字節(jié)流基礎(chǔ)上進行的擴展。
五、Buffered 輸入流實現(xiàn)更快讀取效率的原理
我看FIleInputStream每次讀數(shù)據(jù)都要調(diào)用本地方法read0,如果碰到大文件頻繁調(diào)用,可能導(dǎo)致效率會比較低,有什么更好的方法嗎?
在 Java 中,像 BufferedInputStream 和 BufferedReader 這類帶有緩沖區(qū)(Buffer)的輸入流能夠提升讀取效率,其核心原理在于減少系統(tǒng)調(diào)用的次數(shù)。以下是具體的解釋:
系統(tǒng)調(diào)用開銷
系統(tǒng)調(diào)用是指程序向操作系統(tǒng)內(nèi)核請求服務(wù)的過程,例如從磁盤、網(wǎng)絡(luò)或控制臺讀取數(shù)據(jù)。每次進行系統(tǒng)調(diào)用都需要進行上下文切換,也就是從用戶態(tài)切換到內(nèi)核態(tài),這個過程會消耗大量的時間和資源。如果每次只讀取少量的數(shù)據(jù),就會頻繁地進行系統(tǒng)調(diào)用,從而導(dǎo)致效率低下。
緩沖區(qū)的作用
帶有緩沖區(qū)的輸入流在內(nèi)部維護了一個緩沖區(qū)(通常是一個字節(jié)數(shù)組或字符數(shù)組)。當程序第一次調(diào)用讀取方法時,它會進行一次系統(tǒng)調(diào)用,從數(shù)據(jù)源(如文件、網(wǎng)絡(luò)等)中讀取一大塊數(shù)據(jù)到緩沖區(qū)中。之后,程序再進行讀取操作時,會先從緩沖區(qū)中獲取數(shù)據(jù),而不是直接進行系統(tǒng)調(diào)用。只有當緩沖區(qū)中的數(shù)據(jù)被讀完后,才會再次進行系統(tǒng)調(diào)用,讀取新的數(shù)據(jù)到緩沖區(qū)。
例如,BufferedInputStream 會一次性從底層輸入流(如 FileInputStream)中讀取多個字節(jié)到其內(nèi)部的字節(jié)數(shù)組緩沖區(qū),后續(xù)的讀取操作就可以直接從這個緩沖區(qū)獲取數(shù)據(jù),減少了與底層輸入源的交互次數(shù),從而提高了讀取效率。
Buffered 輸入流的使用方法
1. BufferedInputStream 的使用(處理字節(jié)流)
BufferedInputStream 用于處理字節(jié)流,以下是一個簡單的示例,展示如何使用 BufferedInputStream 讀取文件:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class BufferedInputStreamExample {
public static void main(String[] args) {
try (InputStream fis = new FileInputStream("example.txt");
BufferedInputStream bis = new BufferedInputStream(fis)) {
int byteData;
while ((byteData = bis.read()) != -1) {
System.out.print((char) byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}在上述代碼中,首先創(chuàng)建了一個 FileInputStream 用于從文件中讀取字節(jié)數(shù)據(jù),然后將其作為參數(shù)傳遞給 BufferedInputStream 的構(gòu)造函數(shù),創(chuàng)建一個帶有緩沖區(qū)的輸入流。之后,通過 BufferedInputStream 的 read() 方法讀取數(shù)據(jù),它會先從緩沖區(qū)中獲取數(shù)據(jù),當緩沖區(qū)為空時再進行系統(tǒng)調(diào)用讀取新的數(shù)據(jù)。
2. BufferedReader 的使用(處理字符流)
BufferedReader 用于處理字符流,以下是一個使用 BufferedReader 讀取文件的示例:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}在這個示例中,創(chuàng)建了一個 FileReader 用于從文件中讀取字符數(shù)據(jù),然后將其傳遞給 BufferedReader 的構(gòu)造函數(shù)。BufferedReader 會將字符數(shù)據(jù)讀取到其內(nèi)部的緩沖區(qū),通過 readLine() 方法可以方便地按行讀取數(shù)據(jù),提高了讀取文本文件的效率。
通過使用帶有緩沖區(qū)的輸入流,可以顯著減少系統(tǒng)調(diào)用的次數(shù),從而提高數(shù)據(jù)讀取的效率。
總結(jié)
到此這篇關(guān)于Java輸入流與輸出流的文章就介紹到這了,更多相關(guān)Java輸入流與輸出流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot一個接口多個實現(xiàn)類的調(diào)用方式總結(jié)
這篇文章主要介紹了SpringBoot一個接口多個實現(xiàn)類的調(diào)用方式,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01
Java中json格式化BigDecimal保留2位小數(shù)
這篇文章主要給大家介紹了關(guān)于Java中json格式化BigDecimal保留2位小數(shù)的相關(guān)資料,BigDecimal是Java中的一個數(shù)學庫,可以實現(xiàn)高精度計算,文中給出了詳細的代碼實例,需要的朋友可以參考下2023-09-09
maven項目pom.xml中parent標簽的使用小結(jié)
使用maven是為了更好的幫項目管理包依賴,maven的核心就是pom.xml,當我們需要引入一個jar包時,在pom文件中加上就可以從倉庫中依賴到相應(yīng)的jar包,本文就來介紹一下maven項目pom.xml中parent標簽的使用小結(jié),感興趣的可以了解一下2023-12-12
Java?Chassis3應(yīng)用視角的配置管理技術(shù)解密
這篇文章主要為大家介紹了Java?Chassis3應(yīng)用視角的配置管理相關(guān)的機制和背后故事,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01
SpringMVC+ZTree實現(xiàn)樹形菜單權(quán)限配置的方法
本篇文章主要介紹了SpringMVC+ZTree實現(xiàn)樹形菜單權(quán)限配置的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
Spring源碼之事件監(jiān)聽機制詳解(@EventListener實現(xiàn)方式)
這篇文章主要介紹了Spring源碼之事件監(jiān)聽機制(@EventListener實現(xiàn)方式),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08

