IO流操作
IO流
IO就是輸入輸出,IO裝置在計算機中起著舉足輕重的作用,IO流也就是輸入輸出流,用來互動資料,程式和程式互動,程式也可以和網路等媒介互動。
一、IO流的分類
要分類,肯定得站得不同角度來看這個問題。
- 根據流向劃分,分為輸入流和輸出流
- 根據資料的單位來分劃,分為位元組流和字元流
- 根據功能劃分,分為節點流和包裝流
- 還有很多
二、四大基本流
在java中IO流非常之多,但都繼承於這四大基流,分別為,位元組輸入流
(InputStream),位元組輸出流
(OutputStream),字元輸入流
(Reader)和字元輸出流
(Writer).
它們都是抽象類,不能建立物件,只能建立它們的子類物件,閱讀API發現,它們都有一個共同的方法close方法,用來關閉流,如果不關閉流的話程式會一直引用著檔案。
三、模板操作
- 建立源和目標
- 建立流物件
- 具體的IO操作
- 關閉流
在下面的例子中穿插講述
四、檔案流
檔案流操作是是檔案。分為字元流和位元組流,位元組流操作是所有的二進位制檔案包括視訊啊,圖片啊什麼的。字元流主要解決的還是中文的一些問題,因為中文多個位元組(GBK是2個位元組,UTF-8是3個位元組),所有可能出現讀一半或者寫一半就不讀了或者不寫了,這樣就出現亂碼了。具體用字元流還是位元組流得具體選擇,位元組流是可以操作字元流的,但是反過來就可能出現問題。輸入就是從檔案中讀到程式中來,而輸出就是從程式中輸出到檔案中。在這樣章節記住一句話:讀進來,寫出去 。下面操作一下各個流的常用方法,
-
檔案位元組輸入流(FileInputStream)
int read()從此輸入流中讀取一個數據位元組。 int read(byte[] b) 從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中 int read(byte[] b, int off, int len)從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中
讀取檔案中所有的資料,用到了一個緩衝陣列,也就是每次讀取5個位元組,直到讀完為止有個len返回當前讀了幾個位元組,如果後面沒有位元組了,那麼它就返回-1
//讀取檔案中所有的資料 byte[] buffer = new byte[5];//定義一個緩衝陣列長度為5,每次只能讀取5個位元組 int len = 0;//當前讀取的位元組數,沒有位元組就返回 -1 while((len = in.read(buffer))!= -1 ){ String str = new String(buffer,0,len); System.out.println(str); }
-
檔案位元組輸出流(FileOutputStream)
1.void write(byte[] b) 將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。 2.void write(byte[] b, int off, int len) 將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。 3.void write(int b) 將指定位元組寫入此檔案輸出流。
輸出就是想檔案中寫資料,可以一次寫一個位元組,也可以寫一個數組中的元素,這個比較簡單。可以用一下四個步驟操作一下
public class FileOutputStreamDemo { public static void main(String[] args) throws Exception { //1.建立源 File f = new File("C:/Users/15626/Desktop/test/123.txt"); //2.建立輸出流物件 FileOutputStream out = new FileOutputStream(f); //3.寫操作 out.write(97);//只能一個一個的寫 //將buffer陣列中的資料寫在檔案中 byte[] buffer = "ABCDEF".getBytes(); out.write(buffer); //從索引1開始的3個位元組寫在檔案中 out.write(buffer, 1, 3); //4.關閉資源 out.close(); } }
-
檔案字元輸入流(FileReader)
字元流的方法是從父類中繼承過來的,也和上面位元組流的方法大同小異
int read()讀取單個字元。 int read(char[] cbuf, int offset, int length)將字元讀入陣列中的某一部分。
//檔案字元輸入流 public class FileReaderDemo { public static void main(String[] args) throws IOException { //1.建立源 File src = new File("C:/Users/15626/Desktop/新建資料夾 (2)/test.txt"); //2.建立字元輸入流物件 FileReader in = new FileReader(src); //3.讀操作 char[] buffer = new char[10]; int len = -1; while((len = in.read(buffer)) != -1){ String str = new String(buffer,0,len); System.out.println(str); } //4.關閉流 in.close(); } }
-
檔案字元輸出流(FileWriter)
void write(char[] cbuf, int off, int len) 寫入字元陣列的某一部分。 void write(int c)寫入單個字元。 void write(String str, int off, int len) 寫入字串的某一部分。
檔案的拷貝操作
把指定目錄的.java檔案拷貝到指定的目錄
//拷貝檔案:把指定目錄的.java檔案拷貝到指定的目錄 public class FileCopyDemo2 { public static void main(String []args) throws Exception{ //得到指定目錄的檔案 File srcDir = new File("C:/Users/15626/Desktop/新建資料夾 (2)"); File destDir = new File("C:/Users/15626/Desktop/新建資料夾 (2)/qqq"); //篩選出字尾是.java的檔案儲存在fs這個檔案陣列中 File[] fs = srcDir.listFiles(new FilenameFilter(){ @Override public boolean accept(File dir, String name){ return new File(dir,name).isFile() && name.endsWith(".java"); } }); //遍歷這些檔案 for (File srcFile : fs) { //1.建立目標 File destFile = new File(destDir,srcFile.getName()); //2.建立輸入流和輸出流物件 FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(destFile); //3.輸入輸出操作 byte[] buffer = new byte[10]; int len = -1; while((len = in.read(buffer)) != -1){ out.write(buffer, 0, len); } //4.關閉流 in.close(); out.close(); } } }
資料夾的拷貝
https://www.cnblogs.com/tfper/p/9855228.html
檔案流正確關閉資源
https://www.cnblogs.com/tfper/p/9833722.html
五、緩衝流
緩衝流是一個包裝流,目的起緩衝作用.操作流的時候,習慣定義一個byte/char陣列. int read():每次都從磁碟檔案中讀取一個位元組. 直接操作磁碟檔案效能極低,為了解決這個問題,我們 定義一個數組作為緩衝區. byte[] buffer = new byte[1024]; 該陣列其實就是一個緩衝區. 一次性從磁碟檔案中讀取1024個位元組. 如此以來,操作磁碟檔案的次數少了,效能得以提升,java提供的預設快取區大小是8192(1024*8),我們一般不用修改大小.
緩衝流也有四種,分別是 BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter.操作方法都和檔案流差不多,我們來看一下緩衝流到底快了多少?
/** * @author 15626 *操作字元或者位元組流都應該用緩衝流包裝起來,現在比較一下用緩衝流和不用緩衝流的情況寫,複製一個檔案所花費時間 *長度的比較 */ public class BufferCompare { public static void main(String[] args) throws Exception { File srcFile = new File("C:/Users/15626/Desktop/File/test.pdf"); File destFile = new File("C:/Users/15626/Desktop/File/copy.pdf"); //test1(srcFile,destFile);//不用緩衝流包裝 test2(srcFile,destFile);//用緩衝流包裝 } //使用緩衝流,從記憶體每次讀取1024個位元組:15ms public static void test2(File srcFile, File destFile) throws Exception { long begin = System.currentTimeMillis(); BufferedInputStream in = new BufferedInputStream(new FileInputStream(srcFile)); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destFile)); byte[] buffer = new byte[1024]; int len = -1; while((len = in.read(buffer)) != -1){ out.write(buffer, 0, len); } in.close(); out.close(); System.out.print(System.currentTimeMillis() - begin); } //使用位元組流,從磁碟中每次讀取1024個位元組:38ms public static void test1(File srcFile,File destFile) throws Exception { long begin = System.currentTimeMillis(); FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(destFile); byte[] buffer = new byte[1024]; int len = -1; while((len = in.read(buffer)) != -1){ out.write(buffer, 0, len); } in.close(); out.close(); System.out.print(System.currentTimeMillis() - begin); } }
緩衝流的效率很高,所有以後操作字元位元組流都可以用緩衝流包裝起來。
六、記憶體流
記憶體流就是把程式中的資料暫存在記憶體中,在這裡也就是陣列中,或者把記憶體中的資料輸出到其他媒介,記憶體流也是分為位元組流,字元流還有一個字串流(儲存在字串中)。簡單操作一下,熟悉一個方法
import java.io.*; /** * @author 15626 *位元組記憶體流,或者說是位元組陣列流 *位元組記憶體輸入流 : ByteArrayOutputStream *位元組記憶體輸出流 : ByteArrayInputStream */ public class ByteArrayStreamDemo { public static void main(String[] args) throws Exception { //位元組記憶體輸出流:把程式的內容寫到記憶體中的byte陣列中 ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write("ABSAK".getBytes()); //把程式中的資料存在了緩衝陣列中,toByteArray()方法用來獲取緩衝區的資料 byte[] buffer = out.toByteArray(); //位元組記憶體輸入流: 把記憶體中的資料讀到程式中 ByteArrayInputStream in = new ByteArrayInputStream(buffer); byte[] by = new byte[1024]; int len = -1; while((len = in.read(by)) != -1){ System.out.print(new String(by,0,len)); } //關閉流無效,因為本身就在儲存在記憶體中 in.close(); out.close(); } }
/** * @author 15626 *字元記憶體流或者說是字元陣列流 *字元記憶體輸出流:CharArrayWriter *字元記憶體輸入流:CharArrayReader */ public class CharArrayReaderWriterDemo { public static void main(String[] args) throws Exception { //字元記憶體輸出流 : 從程式把資料儲存到記憶體中的一個緩衝陣列中 CharArrayWriter cWriter = new CharArrayWriter(); cWriter.write("你是天邊最美的雲彩"); //獲取緩衝陣列中的內容,用toCharArray()方法獲取 char[] ch= cWriter.toCharArray(); //字元記憶體輸入流:從記憶體中被資料讀取到程式中來 CharArrayReader sReader = new CharArrayReader(ch); char[] buffer = new char[1024]; int len = -1; while((len = sReader.read(buffer)) != -1){ System.out.println(new String(buffer,0,len)); } //關閉流無效,因為本身就在儲存在記憶體中 sReader.close(); cWriter.close(); } }
/** * @author 15626 *字串的記憶體流,把程式中的資料儲存在一個緩衝字串中 *字串的輸出流 :StringWriter *字串的輸入流:StringReader */ public class StringWriterReaderDemo { public static void main(String[] args) throws Exception { //字串的輸出流 StringWriter out = new StringWriter(); out.write("你真的想成為一隻帥帥氣氣的一隻小人嗎"); //獲取緩衝區的字串 //System.out.print(out.toString()); //字串的輸入流 StringReader in = new StringReader("海納百川,有容乃大"); char[] buffer = new char[1024]; int len = -1; while((len = in.read(buffer)) != -1){ System.out.print(new String(buffer,0,len)); } //關閉流無效,因為本身就在儲存在記憶體中 in.close(); out.close(); } }
要注意的關閉流無效,但是為了追求完美的格式,防止忘記了其他流的關閉,我還還是自娛自樂的關一下吧!
七、物件流
物件流包括ObjectInputStream和ObjectOutputStream,名字起得很有範,肯定是位元組流
一般用物件流進行序列化和反序列化通過writeObject方法做序列化操作的,通過readObject方法做反序列化操作
1.序列化
指把堆記憶體中的Java物件資料,通過某種方式把物件儲存到磁碟檔案中或者傳遞給其他網路的節點(在網路上傳輸). 我們把這個過程稱之為序列化.
需要做序列化的物件的類,必須實現序列化介面: java.io.Serializable介面(標誌介面[沒有抽象方法]). 底層會判斷,如果當前物件是Serializable的例項,才允許做序列化.大多數類都已經實現了這個介面了
2.反序列化
把磁碟檔案中的物件資料或者把網路節點上的物件資料,恢復成Java物件的過程.
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * @author 15626 *物件流的序列化和反序列化操作 *ObjectInputStream 用WriterObjcet 來做序列化 *ObjectOutputStream 用ReaderObject 來做反序列化 *需要做序列化的類必須實現Serializable介面 */ public class ObjectStreamDemo { public static void main(String[] args) throws Exception { File f = new File("file/111.txt"); writeObject(f);//序列化 readObject(f);//反序列化 } public static void readObject(File f) throws Exception{ ObjectInputStream in = new ObjectInputStream(new FileInputStream(f)); Student s = (Student)in.readObject(); System.out.println(s); in.close(); } public static void writeObject(File f) throws Exception { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); out.writeObject(new Student("小夥子",19,"12345")); out.close(); } } class Student implements Serializable { /** * 做反序列化操作必須存在序列化的位元組碼檔案,由於隨著版本升級肯定位元組碼會發生改變,java提供了序列化版本ID, * 可以固定這一份位元組碼檔案,解決了這個問題 */ private static final long serialVersionUID = 1L; private String name; private int age; //密碼一般不會做序列化的,靜態的欄位和瞬態的欄位不能做序列化,所以給密碼加上瞬態transient修飾符 //反序列化的結果就為null transient private String password; @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", password=" + password + "]"; } public Student(String name, int age, String password) { super(); this.name = name; this.age = age; this.password = password; } }
可以看到裡面有兩個重要的問題,一個是反序列化物件需要物件的位元組碼檔案,需要提供序列化ID固定這份位元組碼檔案,還有一個是transient瞬息欄位是不能序列化.
八、列印流
1.位元組列印流
import java.io.File; import java.io.PrintStream; /** * @author 15626 * 位元組列印流 *可以自動重新整理的,不想API說的那樣,必須用換行符或者println才可以重新整理緩衝區 */ public class PrintStreamDemo { public static void main(String[] args) throws Exception { File f = new File("file/123.txt"); PrintStream p = new PrintStream(f); p.print("aa"); p.println(121); p.append("a"); p.close(); //System.out.println();//這行程式碼等價於下面的兩行程式碼 //PrintStream ps = System.out; //ps.println("a"); } }
2.字元列印流
/** * @author 15626 *字元列印流 *當啟用自動重新整理後,即new PrintWriter(new FileOutputStream(f),true);true *呼叫println方法或者printf或者\n後進行重新整理。 * *當不啟動自動重新整理,需要手動重新整理,呼叫flush方法,或者close方法會進行重新整理 */ public class PrintWriterDemo { public static void main(String[]args) throws Exception{ File f = new File("file/123.txt"); PrintWriter p = new PrintWriter(new FileOutputStream(f),true); p.print("wqe"); p.println(); p.close(); } }