IO的简介
IO
IO簡介
In/Out: 相對于程序而言的輸入(讀取)和輸出(寫出)的過程。
即: 通過java程序到磁盤讀取數據的過程, 我們稱為In的過程, 也就是讀取(輸入)
將java程序中的數據寫入磁盤的過程, 我們稱為Out的過程, 也就是寫出(輸出)
在Java中,根據處理的數據單位不同,分為字節流和字符流。
字節流: 一個字節(byte)一個字節的去讀取, 或者寫出
字符流: 一個字符一個字符的去讀取, 或者寫出
JDK核心類庫中提供了IO流相關的類, 這些類都在<java.io>包下
流的概念
程序中數據的讀取和寫入, 可以想象成水流在管道中流動。
– 流只能單方向流動
– 輸入流用來讀取in
– 輸出流用來寫出Out
– 數據只能從頭到尾順序的讀取一次或寫出一次
節點流和處理流
按照流是否直接與特定的地方(如磁盤,內存,設備等)相連,分為節點流和處理流兩類
節點流
可以從或向一個特定的地方(節點)讀寫數據
處理流
是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫
處理流特點
處理流的構造方法總是要帶一個其他的流對象做參數,一個流對象經過其他流的多次包裝,成為流的鏈接.
通常節點流也被稱之為低級流.處理流也被稱之為高級流或者過濾流
節點流
OutputStream抽象類
此抽象類是表示輸出字節流的所有類的超類。輸出流接受輸出字節并將這些字節發送到某個接收器。
FileOutputStream字節輸出流
直接插在文件上,直接寫出文件數據
創建對象:
FileOutputStream(String name) ? 創建一個向具有指定名稱的文件中寫入數據的輸出文件流。 FileOutputStream(File file) ? 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。 FileOutputStream(File file, boolean append) –追加 ? 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。注意: 以上構造方法中, 如果參數指向的文件以及父目錄都不存在, 將會拋出FileNotFoundException異常!
如果參數指向的文件不存在, 但文件的所在目錄存在, 將不會拋異常, 程序會自動創建該文件!
FileOutputStream代碼案例
package io;import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream;/*** FOS FileOutputStream 文件輸出流*/ public class FOSDemo {public static void main(String[] args) throws Exception {//1.創建一個FileOutputStream流,將數據輸出到./test.dat文件中//File file = new File("./test.dat");//如果沒有該文件,會自動創建文件//true 表示開啟流的追加模式,就不會覆蓋原有的內容了//默認是false,是覆蓋模式OutputStream out =new FileOutputStream("./test.dat",true);//2.開始向文件寫入數據//write寫出一個字節//此處寫入的是一個int,int占4字節// x x x x//1個字節等于8個二進制位//0 1 10 11 100//00000000 00000000 00000000 00000001//00000000 00000000 00000001 00000001out.write(1);//00000001out.write(97);//aout.write(98);//bout.write(99);//cout.write(100);//dout.write(255);out.write(257);//1//write寫一個byte數組//getBytes將字符串轉成字節數組out.write("ABCDE".getBytes());//輸出byte數組的一部分 表示從下標1處(G)開始,輸出后面的3個字節(包括G,GHI)//out.write("FGHIJ".getBytes(),1,3);//3.關閉流(釋放資源)System.out.println("成功的將數據寫入到了test.dat中");out.close();} }InputStream抽象類
此抽象類是表示字節輸入流的所有類的超類/抽象類。
FileInputStream子類
直接插在文件上,直接讀取文件數據。
創建對象
FileInputStream(File file)
? 通過打開一個到實際文件的連接來創建一個 FileInputStream,該文件通過文件系統中的 File 對象 file 指定。
FileInputStream(String pathname)
? 通過打開一個到實際文件的連接來創建一個 FileInputStream,該文件通過文件系統中的路徑名 name 指定。
FileInputStream代碼案例
package io;import java.io.FileInputStream; import java.io.InputStream;/*** FIS FileInputStream 文件輸入流*/ public class FISDemo {public static void main(String[] args) throws Exception {//File file = new File("./test.dat");//FileInputStream in = new FileInputStream(file);InputStream in =new FileInputStream("./test.dat");/*int read() 讀取文件中的1個字節,返回該字節的整數形式是將讀取到的字節的2進制保存在返回的int值的最后8位上對于我們來說test.dat文件的二進制形式如下01100001 01100010 01100011^^^^^^^^0110000100000000 00000000 00000000 01100001轉成int值 97如果返回的整數是-1,表示讀到了流的末尾-1的2進制:11111111 11111111 11111111 11111111寫入-1,只取最后8位 11111111 255讀取的時候,1111111100000000 00000000 00000000 11111111*/ // System.out.println(in.read());//a // System.out.println(in.read());//b // System.out.println(in.read());//c // System.out.println(in.read());//d // System.out.println(in.read());//e // System.out.println(in.read());//f // System.out.println(in.read());//g // System.out.println(in.read());//h // System.out.println(in.read());//-1 表示沒有內容啦//重復的讀取可以利用循環來完成/*for循環一般用于控制次數的循環while循環一般用于不知道次數的循環*///定義變量,用于存儲每次從文件中讀取的數據(1個字節)int data;while ((data = in.read())!=-1){System.out.println(data);}//關閉流in.close();} }復制文件
復制文件代碼案例
package io;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream;/*** 復制文件*/ public class CopyDemo {public static void main(String[] args) throws Exception {//1.創建讀取要復制的文件的流FileInputStream fis =new FileInputStream("./DAY02/demo.jpg");//2.創建輸入要創建的目標文件FileOutputStream fos =new FileOutputStream("./DAY02/demo_cp.jpg");long start = System.currentTimeMillis();//3.利用輸入流,將demo.jpg的字節讀取到內存中int data;while ((data=fis.read())!=-1){//4.每讀取一個字節,要將字節寫入到目標文件中fos.write(data);}long end = System.currentTimeMillis();System.out.println("復制完畢,共花費了:"+(end-start)+"ms");//16882ms//5.關閉流fis.close();fos.close();} }快讀寫優化代碼案例
package io;import java.io.FileInputStream; import java.io.FileOutputStream;/*** 復制文件* 提高每次讀寫的數據量,減少實際讀寫的次數,可以提高讀寫的效率* 一組字節一組字節的讀寫:塊讀寫形式*/ public class CopyDemo2 {public static void main(String[] args) throws Exception {//1.創建讀取要復制的文件的流FileInputStream fis =new FileInputStream("./DAY02/demo.jpg");//2.創建輸入要創建的目標文件FileOutputStream fos =new FileOutputStream("./DAY02/demo_cp.jpg");/*1.簡單介紹實現思路超類:java.io.inputStram中定義了塊讀取字節的方法int read(byte[] data) 一次性讀取給定的字節數組總長度的字節量,并存入到該數組中返回值為實際的字節量,如果返回的是-1,表示流讀到了末尾1)假定文件只有7個字節,然后四個字節一讀取demo.jpg文件數據10101001 10101000 10101010 01010100 10101010 10101011 10101010byte[] data = new byte[4]; 定義4個字節數組int len; 返回讀取的字節數量2)第一次調用方法讀取4個字節len = fis.read(data);demo.jpg文件數據10101001 10101000 10101010 01010100 10101010 10101011 10101010^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^data:[10101001 10101000 10101010 01010100]len: 4 表示此次讀到了4個字節3)第二次調用方法讀取剩余的字節,但是此時只剩三個demo.jpg文件數據10101001 10101000 10101010 01010100 10101010 10101011 10101010^^^^^^^^ ^^^^^^^^ ^^^^^^^^data:[10101010 10101011 10101010 01010100]|----本次讀取到的數據--------||-舊數據-|len:34)第三次調用方法,讀取字節demo.jpg文件數據10101001 10101000 10101010 01010100 10101010 10101011 10101010 文件末尾^^^^^^^data:[10101010 10101011 10101010 01010100]|--------------舊數據----------------|len:-1 表示文件末尾了*///3.利用輸入流,將demo.jpg的字節讀取到內存中/*0 1 1位2進制 1bit0000000 8位2進制稱為1byte(1字節)1024byte=1kb1024kb=1mb1024mb=1gb1024gb=1t*/byte[] data = new byte[1024*10];//10kbint len;long start = System.currentTimeMillis();while ((len = fis.read(data))!=-1){//fos.write(data);fos.write(data,0,len);}long end = System.currentTimeMillis();System.out.println("復制完畢,共花費了:"+(end-start)+"ms");//3ms//5.關閉流fis.close();fos.close();} }寫入字符串
package io;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.nio.charset.StandardCharsets;/*** 向文件寫出字符串*/ public class WriterStringDemo {public static void main(String[] args) throws Exception {//向demo.txt文件寫入字符串FileOutputStream fos =new FileOutputStream("./DAY02/demo.txt");String line = "bi,bi,im' sheep~bi,bi,im' sheep~";/*String提供了將字符串轉換為字節數組的方法,通常使用UTF-8字符集getBytes()方法添加如下的參數java.nio.charset.StandardCharsets指定編碼位UTF_8*/fos.write(line.getBytes(StandardCharsets.UTF_8));fos.write("比,比,安慕希~比,比,安慕希~".getBytes(StandardCharsets.UTF_8));fos.close();} }簡易筆記本
package io;import javax.sound.sampled.Line; import javax.swing.text.html.StyleSheet; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.nio.charset.StandardCharsets; import java.util.Scanner;/*** @author少珩* @data 2022/3/7 20:11* 實現簡易的記事本工具* 程序啟動后,要求將控制臺輸入的每一行的字符串都寫入到文件note.txt中,* 當在控制臺輸入exit時,程序退出*/ public class TestNotes {public static void main(String[] args) throws Exception {System.out.println("請開始輸入內容,單獨輸入exit退出");FileOutputStream fos = new FileOutputStream("./DAY02/note.txt");Scanner scanner = new Scanner(System.in);//構建一個死循環//for(;;) for循環的死循環while (true) {//每次循環都接受在控制臺輸入的一句字符串String line = scanner.nextLine();//先判斷line是否是exit,如果是,停止錄入//String類中已經幫我們重寫了equals方法/*不建議比較字符串相等時這樣寫 對象.equals(字符串)line.equals("exit")line都是用戶傳遞過來的,我們不能控制用戶輸入的是否為空*///if("exit".equals(line)){//equalsIgnoreCase 忽略大小寫if ("exit".equalsIgnoreCase(line)) {System.out.println("錄入已停止");break;}fos.write(line.getBytes(StandardCharsets.UTF_8));}fos.close();} }文件追加模式案例
package io;import java.io.FileOutputStream; import java.nio.charset.StandardCharsets; import java.util.Scanner;/*** @author少珩* @data 2022/3/7 20:11* 實現簡易的記事本工具* 程序啟動后,要求將控制臺輸入的每一行的字符串都寫入到文件note.txt中,* 當在控制臺輸入exit時,程序退出*/ public class TestNotes2 {public static void main(String[] args) throws Exception {System.out.println("請開始輸入內容,單獨輸入exit退出");/*文件流有兩種創建模式,覆蓋模式和追加模式構造器:FileOutputStream(String path)FileOutputStream(File file)以上兩種的創建方式默認都是覆蓋模式,每次執行都會先清除原有文件的內容FileOutputStream(String path,boolean append)FileOutputStream(File file,boolean append)如果第二個參數傳入true,則文件流為追加模式,此時連接的文件如果有內容,則會保留原有的內容*/FileOutputStream fos =new FileOutputStream("./DAY02/note.txt",true);Scanner scanner = new Scanner(System.in);while (true) {String line = scanner.nextLine();if ("exit".equalsIgnoreCase(line)) {System.out.println("錄入已停止");break;}fos.write(line.getBytes(StandardCharsets.UTF_8));}fos.close();} }處理流
緩沖流
- BufferedOutputStream緩沖輸出流
- BufferedInputStream 緩沖輸入流
復制文件代碼案例
package io;import java.io.*;/*** @author少珩* @data 2022/3/7 21:09*/ public class CopyDemo3 {public static void main(String[] args) throws Exception {//給源文件插上低級流,文件輸入字節流FileInputStream fis = new FileInputStream("./DAY02/demo.jpg");//為fis插上高級流,緩沖字節輸入流BufferedInputStream bis = new BufferedInputStream(fis);//給目標文件插上低級流,文件輸出字節流FileOutputStream fos = new FileOutputStream("./DAY02/demo_cpp.jpg");//為fos插上高級流,緩沖字節輸出流BufferedOutputStream bos = new BufferedOutputStream(fos);//測試單字節的讀寫int d;long start = System.currentTimeMillis();//由于此處讀的字節是經過高級流處理過的,所以使用高級流讀while((d=bis.read())!=-1){bos.write(d);}long end = System.currentTimeMillis();System.out.println("耗時:"+(end-start)+"毫秒");//69ms//關閉流,只需要關閉高級流即可會自動關閉和他相連的低級流bis.close();bos.close();} }flush代碼案例
package io;import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.nio.charset.StandardCharsets;/*** @author少珩* @data 2022/3/7 21:30* 緩沖流寫出數據的緩沖區問題*/ public class BOSFlushDemo {public static void main(String[] args) throws Exception {FileOutputStream fos = new FileOutputStream("./DAY02/bos.txt");//緩沖流內部默認是一個8K的字節數組,寫出的數據會先存入到數組中,知道數組裝滿了才會寫出BufferedOutputStream bos = new BufferedOutputStream(fos);bos.write("我們的祖國是花園".getBytes(StandardCharsets.UTF_8));System.out.println("寫出完畢");/*此處能寫出的原因不是因為close,主要原因是因為flush方法作用:讓緩存輸出流將其緩沖區的已經緩存的數據立即寫出*/bos.close();} }對象流
Person代碼
package io;import java.io.Serializable; import java.util.Arrays;/*** @author少珩* @data 2022/3/7 22:06* 使用當前類測試對象流的序列化與反序列化操作* 序列化:將內存中的對象按照其結構轉換為一組字節,存儲到文件中的過程* 反序列化:將文件中的內容讀取到內存中,轉換為對象的過程* 如果一個類需要被序列化,這個類必須要實現一個Serializable接口*/ public class Person implements Serializable {/*當一個類實現了可序列化接口后,最好顯示的定義下面的屬性:serialVersionUID既:序列化版本號當對象輸出流在進行序列化時,會查看是否有顯示的序列化版本號,如果沒有,就自動根據當前類的結構動態生成一個唯一在的版本號,只要結構不發生變化,版本號就不會發生變化重點:當使用對象輸入流反序列化時,對象輸入流會檢查要反序列化的對象與其對應的類(比如我們反序列化一個之前的person對象)的版本號是否一致,如果不一致,則會拋出異常:java.io.InvalidClassException,比如OOSDemo序列化一個Person對象,并寫入文件后,我們在Person上添加一個新屬性salary,此處Person類,發生了變化,那么再使用OISDemo反序列化文件,就會發生異常,就是因為版本號不一樣了如果后期修改了類結構,還不希望不能反序列化原來的對象,則需要顯示的定義出來序列化版本號,這樣一來,當一個對象序列化后,當前類結構即使發生了變化,只要版本號不變,依舊是可以反序列化的*/static final long serialVersionUID = 42L;private String name;//姓名private Integer age;//年齡private String gender;//性別/*當一個屬性被transient關鍵字修飾了,那么當這個對象被序列化時,這個屬性的值就會被忽略,提高程序的響應速度,當然,反序列化時,這個屬性的值是默認值*/private transient String[] otherInfo;//其他信息private int salary;//薪資//生成全參構造,set,get,toString//alt+insert@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", otherInfo=" + Arrays.toString(otherInfo) +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String[] getOtherInfo() {return otherInfo;}public void setOtherInfo(String[] otherInfo) {this.otherInfo = otherInfo;}public Person(String name, Integer age, String gender, String[] otherInfo) {this.name = name;this.age = age;this.gender = gender;this.otherInfo = otherInfo;} }OOSDemo案例
package io;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.ObjectOutputStream;/*** @author少珩* @data 2022/3/7 22:14* OOS ObjectOutputStream* OIS ObjectInputStream* 對象流是一對高級流,在流連接中完成對象與字節的轉換,即序列化與反序列化*/ public class OOSDemo {public static void main(String[] args) throws Exception {String name="馮翔";int age = 24;String gender = "男";String[] otherInfo = {"是一個男的","來自北京","愛好女","是個帥氣的海王"};Person p = new Person(name, age, gender, otherInfo);//Person中重寫了toString方法System.out.println(p);//將Person對象寫入到person.obj文件中FileOutputStream fos = new FileOutputStream("./DAY02/person.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);/*writeObject(p); 對象輸出流中提供獨特的方法該方法可以將對象序列化,并將序列化后的字節通過其連接的流寫出*/oos.writeObject(p);System.out.println("寫出完畢");oos.close();} }OISDemo案例
package io;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.ObjectInputStream;/*** @author少珩* @data 2022/3/9 19:56* 使用對象輸入流進行對象的反序列化*/ public class OISDemo {public static void main(String[] args) throws Exception {FileInputStream fis = new FileInputStream("./DAY02/person.obj");ObjectInputStream ois = new ObjectInputStream(fis);//readObject 讀取文件中的對象,返回的是Object類型Person p = (Person) ois.readObject();p.setAge(18);System.out.println(p);ois.close();} }字節流和字符流
在Java中,根據處理的數據單位不同,分為字節流和字符流。
字節流: 一個字節(byte)一個字節的去讀取, 或者寫出
字符流: 一個字符一個字符的去讀取, 或者寫出
字節流
字節流(stream):針對二進制文件(文本,圖片,音頻,視頻…等)
InputStream(包含input都是輸入流)
– FileInputStream
– BufferedInputStream
– ObjectInputStream
OutputStream(包含output都是輸出流)
– FileOutputStream
– BufferedOutputStream
– ObjectOutputStream
字符流
字符流(Reader,Writer):針對文本文件,讀寫容易發生亂碼現象,在讀寫時最好指定編碼集為utf-8
Reader(Reader結尾的都是字符輸入流)
– FileReader
– BufferedReader
– InputStreamReader
Writer(Writer結尾的都是字符輸出流)
– FileWriter
– BufferedWriter
– OutputStreamWriter
– PrintWriter/PrintStream
轉換字符流
OutputStreamWriter
代碼案例
package io;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets;/*** @author少珩* @data 2022/3/9 20:56* OSW OutputStreamWriter* java.io將流按照讀寫單位劃分為字節流和字符流* InputStream和OutputStream是所有字節輸入與輸出流的超類,讀寫的最小單位是字節* Reader和Writer是所有字符輸入和輸出流的超類,讀寫的最小單位是字符* 字符流只適合讀寫文本,像圖片,音頻等字節文件不適合* 字符流的底層本質還是字節流,只是字符與字節的轉換由字符流自行完成* InputStreamReader和OutputStreamWriter是一對高級流*/ public class OSWDemo {public static void main(String[] args) throws Exception {//向osw.txt中寫入字符串FileOutputStream fos = new FileOutputStream("./DAY02/osw.txt");//在創建轉換流時,通常需要指定第二個參數,明確使用的編碼OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);osw.write("我可以接收你的所有的小脾氣");osw.write("我可以帶你吃很多的好東西");System.out.println("寫出完畢");osw.close();} }InputStreamReader
代碼案例
package io;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets;/*** @author少珩* @data 2022/3/9 21:07* ISR InputStreamReader*/ public class ISRDemo {public static void main(String[] args) throws Exception {//將osw.txt的內容讀取出來FileInputStream fis = new FileInputStream("./DAY02/osw.txt");InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);int d;while((d=isr.read())!=-1){System.out.print((char)d);}isr.close();} }緩沖字符流
PrintWriter
代碼案例
package io;import java.io.FileNotFoundException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets;/*** @author少珩* @data 2022/3/9 21:19* PW PrintWriter* 緩沖字符流* BufferWriter和BufferReader** PrintWriter 具有自動行刷新功能的緩沖字符輸出流,內部會自動連接BufferWriter* 特點:* 按行讀寫字符串* 自動行刷新* 可以提高寫出字符串的效率*/ public class PwDemo {public static void main(String[] args) throws Exception {/*構造方法,還有第二個參數允許傳入一個字符串指定編碼需要注意,此處不能傳遞StandardCharsets.UTF_8,當時還沒有StandardCharsets類呢所以此處傳參時,由于是字符串,所以容易寫錯,大小寫均可*/PrintWriter pw = new PrintWriter("./DAY02/pw.txt","UTF-8");pw.println("大河向東流");pw.println("天上的星星參北斗啊~~~");System.out.println("寫出完畢");pw.close();} }代碼案例2
package io;import java.io.*; import java.util.Scanner;/*** @author少珩* @data 2022/3/9 21:55* 在流連接中使用PW*/ public class PWDemo2 {public static void main(String[] args) throws Exception {//向pw2.txt文件寫入內容//1.創建文件字節輸出流,是一個低級流//加上true可以將當前的流改為追加模式FileOutputStream fos = new FileOutputStream("./DAY02/pw2.txt",true);//2.創建一個轉換字符輸出流,是一個高級流,//銜接字符與字節的流,將寫出的字符串轉換為字節OutputStreamWriter oos = new OutputStreamWriter(fos);//3.緩沖輸出流,是一個高級流,塊寫入到文件中BufferedWriter bw = new BufferedWriter(oos);//4.緩沖字符輸出流/*PrintWriter 構造方法有第二個參數,是布爾值當該值是true時,會自動打開行刷新功能,這意味著我們每次調用println方法后會自動調用flush*/PrintWriter pw = new PrintWriter(bw,true);Scanner scanner = new Scanner(System.in);System.out.println("請輸入想要錄入到文檔中的內容");//接收控制臺輸入字符串//完成簡易的記事本,控制臺輸入每行字符串,按行寫入文件,單獨輸入exit,退出while(true){//crtl+shift+↑↓ 可以上下移動當前行代碼String s = scanner.nextLine();//equalsIgnoreCase 忽略大小寫比較if("exit".equalsIgnoreCase(s)){break;}pw.println(s);}pw.close();} }總結
- 上一篇: 【整理向】老板让我用SPSS做A/Bte
- 下一篇: 软件安全与脆弱性分析-对于freenot