Java輸入流與輸出流超全面講解
一、輸入流與輸出流的定義及作用
定義
輸入流(Input Stream):在 Java 世界里,輸入流是一種機制,它負責(zé)從外部世界(像硬盤文件系統(tǒng)、網(wǎng)絡(luò)連接、內(nèi)存數(shù)組等數(shù)據(jù)源)讀取數(shù)據(jù)到程序內(nèi)部。
輸出流(Output Stream):輸出流則是將程序里的數(shù)據(jù)輸出到外部世界,外部世界可以是文件、網(wǎng)絡(luò)連接等目標(biāo)位置。
作用
輸入流和輸出流是 Java 實現(xiàn)數(shù)據(jù)輸入輸出操作的核心,它們搭建起了程序與外部環(huán)境之間的數(shù)據(jù)交互橋梁,使得程序能夠讀取外部數(shù)據(jù)進行處理,也能將處理結(jié)果輸出到外部。
比如說把控制臺的數(shù)據(jù)讀入到程序當(dāng)中?;蛘哒f把程序當(dāng)中的數(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)。當(dāng)?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ù)組)。當(dāng)程序第一次調(diào)用讀取方法時,它會進行一次系統(tǒng)調(diào)用,從數(shù)據(jù)源(如文件、網(wǎng)絡(luò)等)中讀取一大塊數(shù)據(jù)到緩沖區(qū)中。之后,程序再進行讀取操作時,會先從緩沖區(qū)中獲取數(shù)據(jù),而不是直接進行系統(tǒng)調(diào)用。只有當(dāng)緩沖區(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ù),當(dāng)緩沖區(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)文章
IntelliJ IDEA將導(dǎo)入的項目轉(zhuǎn)成maven項目
這篇文章主要介紹了IntelliJ IDEA將導(dǎo)入的項目轉(zhuǎn)成maven項目,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中JSON字符串進行各種轉(zhuǎn)換的方法小結(jié)
Gson和Hutool的JSONUtil都是常用的用于處理JSON數(shù)據(jù)的工具庫,它們提供了簡單易用的API來進行JSON字符串的解析、轉(zhuǎn)換和操作,下面就跟隨小編一起學(xué)習(xí)一下如果使用他們實現(xiàn)JSON字符串的各種轉(zhuǎn)換吧2024-01-01Spring?Boot?整合?Fisco?Bcos部署、調(diào)用區(qū)塊鏈合約的案例
本篇文章介紹?Spring?Boot?整合?Fisco?Bcos?的相關(guān)技術(shù),最最重要的技術(shù)點,部署、調(diào)用區(qū)塊鏈合約的工程案例,本文通過流程分析給大家介紹的非常詳細,需要的朋友參考下吧2022-01-01Scala實現(xiàn)冒泡排序、歸并排序和快速排序的示例代碼
這篇文章主要介紹了Scala實現(xiàn)冒泡排序、歸并排序和快速排序的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-06-06