Java输入/输出流
Java輸入/輸出流
文章目錄
- Java輸入/輸出流
- 一、流的概念
- 二、I/O類體系
- 2.1 字節流
- 2.2 字符流
- 三、文件流
- 3.1 FileInputStream
- 3.2 FileOutputStream
- 3.3 FileReader
- 3.4 FileWriter
- 四、實體流和裝飾流
- 五、緩沖流
- 5.1 緩沖字節流
- 5.2 緩沖字符流
- 六、數據流
- 七、對象流與對象序列化
- 八、標準輸入/輸出
- 九、橋接流
- 十、流的關閉
- 10.1 finally中關閉流
- 10.2 自動關閉流
一、流的概念
輸入流只能讀不能寫,輸出流只能寫不能讀。按照流中數據的處理單位不同,可將流分為字節流和字符流。在字節流中,數據的組織和操作的基本單位是字節;在字符流中,數據的組織和操作的基本單位是字符。綜上,流可分為字節輸入流,字節輸出流,字符輸入流,字符輸出流。
Java是面向對象的語言,使用類來表示各種流
使用流來進行數據輸入的具體步驟為
使用流來進行數據輸出的具體步驟為
二、I/O類體系
為了實現對各種輸入/輸出設備的操作,Java提供了豐富的基于流的I/O類,這些類位于java.io包中。其中有四個抽象類尤為重要,分別是InputStream(字節輸入流)類、OutputStream(字節輸出流)類、Reader(字符輸入流)類和Writer(字符輸出流)類。所有字節流的類都是InputStream類或OutputStream類的子類,所有字符流的類都是Reader類或Writer類的子類。
2.1 字節流
2.2 字符流
三、文件流
File類的構造方法
| public File(File parent, String child) | 根據文件對象和字符串創建一個新的File實例 |
| public File(String pathName) | 根據路徑名字符串創建一個新的File實例 |
| public File(String parent, String child) | 根據兩個字符串創建一個新的File實例 |
| public File(URL uri) | 使用給定的統一資源定位符來創建一個新的File實例 |
File類的常用成員方法
| public boolean exists() | 判斷文件是否存在,存在返回true,否則返回false |
| public boolean isFile() | 判斷是否為文件 |
| public boolean isDirectory() | 判斷是否為目錄 |
| public String getName() | 返回文件或者目錄的名稱 |
| public String getAbsolutePath() | 返回文件的絕對路徑(包含文件名) |
| public long length() | 如果是文件,返回文件的長度(字節數),如果是目錄,返回值不確定 |
| public boolean creatNewFile() throws IOException | 創建新文件,若文件已存在,返回false;該方法只能創建文件,不能創建目錄 |
| public boolean delete() | 刪除當前文件或目錄,如果為目錄,則目錄必須為空時才能刪除 |
| public String[] list() | 返回當前目錄下所有的文件和目錄名稱 |
3.1 FileInputStream
FileInputStream類繼承于InputStream類,是進行文件讀取操作的最基本的類,它的作用是將文件中的數據讀入到內存中。
使用FileInputStream類讀取文件內容的步驟為—>
//(1)創建流,將程序與文件連接起來,具體方法是實例化FileInputStream類的對象 FileInputStream fileInputStream = new FileInputStream("D:\\test\\data.txt"); //或者, File myFile = new File("D:\\test\\data.txt"); FileInputStream fileInputStream = new FileInputStream(myFile); //注意,創建流時,可能拋出異常,必須進行處理--------------------------------//(2)調用read()方法讀取流中的數據,read()方法有如下三種形式,根據需要選用 public int read(); public int read(byte b[]); public int read(byte[] b, int off, int len);//(3)讀取完畢后要關閉流 fileInputStream.close();【示例】文件字節輸入流應用示例,從文本文件中讀取數據并顯示在屏幕上
public class Main {public static void main (String[] args) {try {File file = new File(".//great//data.txt");FileInputStream fileInputStream = new FileInputStream(file);char ch;for(int i = 0; i < file.length(); i++) {ch = (char)(fileInputStream.read());System.out.print(ch);}fileInputStream.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} }字節流中數據操作的單位是字節。由于一個漢字占兩個字節,因此使用字節流讀寫漢字時,可能出現亂碼現象。
從文件中讀取數據時,可以逐個字節讀取,也可以一次讀取多個字節存入字節數組中。以文件的長度來定義字節數組的長度,一次讀取全部數據存入數組,修改后的代碼為
byte[] buf = new byte[(int)(file.length())]; //定義字節數組,以文件的長度確定數組的長度 fileInputStream.read(buf); //一次性讀取文件所有數據存放到字節數組中 String str = new String(buf); //利用字節數組創建字符串 System.out.println(str); //將文件內容以字符串形式輸出當然,這種一次讀取全部數據的方式只適合小文件,當讀取較大文件時,宜采用分塊讀取的方式,即一次讀取固定長度的數據,多次讀取,到達文件尾時結束,讀取數據的代碼修改為
int n = 1024, count = 0; byte[] buf = new byte[n]; while((count = fileInputStream.read(buf)) != -1) { //讀取數據粗如數組//將字節數組轉換為字符串后輸出System.out.print(new String(buf, 0, count)); }3.2 FileOutputStream
創建FileOutputStream類的對象時,如果文件不存在,系統會自動創建該文件,但是當文件路徑中包含不存在的目錄時創建失敗會拋出異常
使用FileOutputStream類將數據輸出到文件的步驟為—>
/*(1)創建流,將程序與文件連接起來,具體方法是實例化FileOutputStream類的對象。將數據寫入文件時,有覆蓋和追加兩種方式。覆蓋是指清除文件的原有數據,寫入新的數據;追加是指保留文件的原有數據,在原有數據的末尾寫入新的數據。默認是覆蓋方式(false) */ FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\data.txt"); //或者 File myFlie = new File("D:\\test\\data.txt"); FileOutputStream fileOutputStream = new FileOutputStream(myFile, true); //注意,創建輸出流時,可能拋出異常,必須進行處理-----------------------------------//(2)調用write()方法將數據輸出到文件,write()方法有以下三種形式 public void write(int b); public void write(byte b[]); public void write(byte b[], int off, int len);//讀取完畢后關閉流 fileOutputStream.close();【示例】從鍵盤輸入一串字符,并將其追加至文件中
public class Main {public static void main (String[] args) {Solution so = new Solution();System.out.println("" );try {FileOutputStream fileOutputStream = new FileOutputStream(".\\great\\data.txt", true);System.out.println("請輸入一行字符:");Scanner sc = new Scanner(System.in);String str = sc.nextLine(); //輸入一行字符byte[] buffer = str.getBytes(); //將字符串轉換為字節數組fileOutputStream.write(buffer); //寫入輸入流fileOutputStream.close(); //關閉輸入流} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} }將逐個字符追加寫入文件
String str = "Hello World!";for(int i = 0; i < str.length(); i++) {fileOutputStream.write(str.charAt(i)); }3.3 FileReader
【示例】文件字符輸入流示例
public class Main {public static void main (String[] args) throws IOException {Solution so = new Solution();System.out.println("" );File file = new File(".//great//data.txt");FileReader fileReader = new FileReader(file);char ch;for(int i = 0; fileReader.ready(); i++) {ch = (char)(fileReader.read());System.out.print(ch);}fileReader.close();} }3.4 FileWriter
不同的操作系統下,回車換行符并不相同,調用System類的getProperty("line.separator")方法能夠獲取當前系統的回車換行符。但是在Windows系統下,回車換行符是"\r\n"。
【示例】文件字符輸出流應用示例。輸入多個字符串,以”#"結束,將這些字符串寫入文件中,要求一個字符串占一行
public class Main {public static void main (String[] args) throws IOException {Solution so = new Solution();System.out.println("" );FileWriter fileWriter = new FileWriter(".//great//data.txt", true);Scanner sc = new Scanner(System.in);String ch = System.getProperty("line.separator");while(!sc.hasNext("#")) {String str = sc.nextLine();fileWriter.write(str);fileWriter.write(ch);}fileWriter.close();System.out.println("已保存至.//great//data.txt");} }四、實體流和裝飾流
按照是否直接連接實際數據源(例如文件)將流劃分為實體流和裝飾流。
實體流能夠直接連接數據源,可單獨使用實現對數據源的讀寫,如文件流。裝飾流不能直接連接數據源,不能單獨使用,必須在其他流(實體流或其他裝飾流)的基礎上使用。常用的有緩沖流、數據流和對象流等。例如訪問文件,既可以只使用文件流來讀寫,也可以在文件流的基礎上配合使用裝飾流。
由于裝飾流是在其他流的基礎上創建的,這種創建流的方式稱作“流的嵌套”。在流的嵌套中,各個流的性質必須相同,也就是流的讀寫單位(字節/字符)、流的方向(輸入/輸出)都要一致。裝飾流不改變實體流中的數據內容,只是對實體流做了一些功能上的增強
五、緩沖流
使用緩沖流,當輸入數據時,數據以塊為單位讀入緩沖區,此后如有讀操作,則直接訪問緩沖區;當輸出數據時,先將數據寫入緩沖區,當緩沖區的數據滿時,才將緩沖區中的數據寫入輸出流中,提高了輸入/輸出的效率。
5.1 緩沖字節流
BufferedInputStream類是InputStream類的子類
列如,在文件流基礎上使用緩沖流進行文件數據的讀取,創建流的代碼為
FileInputStream inOne = new FileInputStream("data.txt"); //創建文件流 BufferedInputStream inTwo = BufferedInputStream(intOne); //以文件流為參數創建緩沖流BufferedOutputStream類是OutputStream類的子類
BufferedOutputStream in = new BufferedOutputStream(new FileOutputStream("data.txt"));5.2 緩沖字符流
BufferedReader類是Reader類的子類,增加了讀取一行字符的方法readLine()
| public BufferedReader(Reader in) | 構造方法,創建具有默認大小緩沖區的緩沖字符輸入流 |
| public BufferedReader(Reader in, int size) | 構造方法,創建具有指定緩沖區大小的緩沖字符輸入流 |
| public String readLine() throws IOException | 從緩沖輸入流中讀取一行字符,以字符串的形式返回(不包括回車符),如果已到達流末尾,則返回null |
BufferedWriter類是Writer類的子類
bw.write("\r\n")與bw.newLine()等價
File file = new File("data.txt"); FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw);六、數據流
Java提供了兩個數據流類,DataInputStream和DataOutputStream,用來輸入/輸出各種類型的數據。使用數據流需要注意以下幾點:
- 數據流屬于裝飾流,不能單獨使用
- 使用DataOutputStream流輸出的數據,必須使用DataInputStream流進行讀取,這兩個類必須配合使用,否則會發生數據錯誤。因為使用DataOutputStream流輸出數據時,除了數據以外,還加上了特定的格式
- 讀取數據時,數據的類型和順序必須與輸出的數據的類型和順序保持一致
下面只介紹數據輸出流DataOutputStream類的方法,數據輸入流相應的方法與之對應
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-d0QXxa4u-1609296044884)(…\JAVA基礎\great\DataOutputStream.png)]
【示例】讀寫學生信息
class StudentInfo {String name;int age;double score;StudentInfo(String name, int age, double score){this.name = name;this.age = age;this.score = score;} }public class Main {public static void main (String[] args) throws IOException {StudentInfo[] stu = new StudentInfo[5];Scanner sc = new Scanner(System.in);//寫入文件FileOutputStream fos = new FileOutputStream(".//great//data.txt");DataOutputStream dos = new DataOutputStream(fos);System.out.println("請輸入學生的姓名、年齡、成績:");for(int i = 0; i < 3; i++) {stu[i] = new StudentInfo(sc.next(), sc.nextInt(), sc.nextDouble());dos.writeUTF(stu[i].name);dos.writeInt(stu[i].age);dos.writeDouble(stu[i].score);}dos.close(); //關閉流//讀出數據DataInputStream dis = new DataInputStream(new FileInputStream(".//great//data.txt"));System.out.println("文件中的內容:");for(int i = 0; i < 3; i++) {System.out.println(dis.readUTF() + " " + dis.readInt() + " " + dis.readDouble());}dis.close();} }七、對象流與對象序列化
對象序列化是指將一個對象的屬性和方法轉化為一種序列化的格式用于存儲和傳輸。在需要的時候,再將對象重構出來,這個過程稱為反序列化。若要將對象能夠進行序列化,其所屬的類必須實現Serializable接口。Serializable接口是一個空接口,不包含任何方法,實現這個接口只是一個標志,表示該類的對象可以進行序列化。Serializable接口在java.io包中
【Serializable】接口
public Myclass implements Serializable {//---- }對象序列化時,不保存對象的transient變量(臨時變量)和static變量(靜態變量)
同數據流一樣,讀取對象時,數據的類型和順序必須與輸出的數據的類型和順序保持一致
【Externalizable】接口
當實現序列化時,如果要自行確定哪些變量要保存,以及具體的順序或者除了成員變量,還需要保存其他的數據,就要通過實現Externalizable接口來進行。
一個類實現Externalizable接口以后,當采用ObjectOutputStream流輸出對象時,會自動調用writeExternal()方法,按照方法規定的邏輯保存對象數據。當采用ObjectInputStream流輸入對象時,會自動調用readExternal()方法
【示例】讀寫用戶信息
class UserInfo implements Externalizable {public String userName;public String userPass;public int userAge;public UserInfo() {}public UserInfo(String username, String userpass, int userage) {this.userName = username;this.userPass = userpass;this.userAge = userage;}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {Date date = (Date)in.readObject();System.out.println(date);this.userName = (String)in.readObject();this.userAge = in.readInt();}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {Date date = new Date();out.writeObject(date);out.writeObject(userName);out.writeInt(userAge);}public String toString() {String str = "用戶名: " + this.userName + ", 密碼: " + this.userPass +", 年齡: " + this.userAge;return str;} }public class Main {public static void main (String[] args) throws IOException, ClassNotFoundException {//寫入文件(序列化)FileOutputStream fout = new FileOutputStream(".//great//data.txt");ObjectOutputStream oout = new ObjectOutputStream(fout);UserInfo user = new UserInfo("飛流", "123456", 18);oout.writeObject(user);oout.close(); //關閉流System.out.println("數據寫入成功!");//讀出數據(反序列化)FileInputStream fin = new FileInputStream(".//great//data.txt");ObjectInputStream oin = new ObjectInputStream(fin);UserInfo user1 = (UserInfo)oin.readObject();oin.close();System.out.println(user1.toString());} }八、標準輸入/輸出
System類功能強大,利用它可以獲得很多Java運行時的系統信息。System類的屬性和方法都是靜態的,System.in和System.out就是System類的兩個靜態屬性,分別對應了系統的標準輸入和標準輸出。
System.in是InputStream類的對象,當程序需要從鍵盤輸入數據時,只需要調用System.in.read()方法即可。不過要注意,System.in是字節對象流,只能輸入字節類型的數據。System.out是PrintStream類的對象,有方法print(),println(),printf()可以輸出各種類型的數據。
System.err是標準錯誤輸出流。它是PrintStream類的對象,有兩個方法print()和println(),調用這兩個方法可以在屏幕上輸出錯誤或者警示信息,如
System.err.println("IOException");九、橋接流
在流的嵌套中,各個流的性質必須相同。為了解決InputStream和Reader兩個體系流的嵌套問題,引入了InputStreamReader和OutputStreamWriter,用于實現字節流向字符流的轉換,在字節流與字符流之間搭建了溝通的橋梁,這兩個類被形象地稱為“橋接流”
相應的構造方法如下
public InputStreamReader(InputStream in); //將字節輸入流轉換為字符輸入流 public OutputStreamWriter(OutputStream out); //將字節輸出流轉換為字符輸出流 //將字節流轉換為字符流 InputStreamReader inputStreamReader = new InputStreamReader(System.in); //上述字符流效率較低,可以使用緩沖流來提高輸入效率 BufferedReader bufferedReader = new BufferedReader(inputStreamReader);【示例】將標準輸入轉換為字符流后再進行數據的輸入
public class Main {public static void main (String[] args) throws IOException {//將字節流轉換為字符流InputStreamReader inputStreamReader = new InputStreamReader(System.in);//上述字符流效率較低,可以使用緩沖流來提高輸入效率BufferedReader bufferedReader = new BufferedReader(inputStreamReader);System.out.println("請輸入學生姓名:");String name = bufferedReader.readLine();System.out.println("請輸入學生成績:");double score = Double.parseDouble(bufferedReader.readLine());bufferedReader.close();System.out.println("姓名: " + name + ", 成績:" + score);} }十、流的關閉
10.1 finally中關閉流
在之前的例子中,流的聲明,創建以及關閉都是在try塊中進行的,如果在執行過程中拋出異常,那么流的關閉語句有可能得不到執行,無法釋放占用的資源。在finally中關閉流,無論是否拋出異常流都會被關閉
InputStreamReader isr = null; try {isr = new InputStreamReader(System.in);int x = (int)isr.read();//······ } catch (IOException e) {e.printStackTrace(); } finally {try {if(isr != null) isr.close();} catch (IOException e) {e.printStackTrace();} }10.2 自動關閉流
使用finally塊程序顯得有些冗長
try-with-resources語句的形式為
try (資源的聲明和創建) {//使用資源 }- 在括號()內創建的資源對象在try塊結束時會自動釋放,不需要再顯式調用close()方法
- 在括號()內創建的資源對象時try塊的局部變量,作用域只限于try塊內部
- 后面可接catch塊和finally塊
- 只能用于實現了java.lang.AutoCloseable接口的那些資源。java.io.Closeable接口繼承了AutoCloseable接口,所有流類都實現了這兩個接口,可以使用try-with-resources語句
try-with-resources語句不僅可以用于流的操作,也可以應用于數據庫編程和網絡編程中所涉及的資源,只要資源實現了AutoCloseable接口即可
參考資料:《Java語言程序設計實用教程》主編 王素琴
總結
以上是生活随笔為你收集整理的Java输入/输出流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习综述(下载PDF版)
- 下一篇: 表哥的Access入门++以Excel视