1. 緩衝字節流java
緩衝區:緩衝區實質上是一個數組。一般它是一個字節數組,可是也可使用其餘種類的數組。可是一個緩衝區不 僅僅 是一個數組。緩衝區提供了對數據的結構化訪問,並且還能夠跟蹤系統的讀/寫進程。數據庫
緩衝流出現的緣由:使用字節流每次從文件中進行讀寫的時候,都須要和文件進行大量的IO交互,與磁盤交互的效率實際上是比較低的,因此爲了下降與磁盤的交互次數,可使用字節緩衝流。字節緩衝流將數據放到緩存區,而緩衝區是一個內存區域的概念,咱們直接和緩衝區作交互,能夠提高效率。數組
注意:緩存
(1)何時緩衝區的數據寫入硬盤中?網絡
當緩衝區被寫滿時,或是使用flush方法將至寫入硬盤(注意關流後,緩存區的內容會被寫入硬盤,由於關流內部會調用flush方法)ide
(2)byte數組的大小要小於緩存區,緩存區的數據是經過數組間接讀入的性能
1.1 緩衝字節輸出流測試
BufferOutputStream(OutputStream);this
1.1.1 構造方法:編碼
(1)public BufferedOutputStream(OutputStream out)
(2)public BufferedOutputStream(OutputStream out, int size): 此處參數size表示緩衝區的大小,默認是8kb
1.1.2 成員方法:
(1)public void write(int b)
(2)public void write(byte b[])
(3)public void write(byte b[], int off, int len):off表示偏移量,len表示從偏移量位置開始寫入數據的長度
(4)public void flush(): 刷新,將緩存區的內容寫到文件中,通常只有帶緩衝的輸出流纔有這樣的方法
public class BufferOutputStreamDemo { public static void main(String[] args) { try ( BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:/a.txt")); ){ bos.write("媽媽,他們拋棄了我".getBytes());//媽媽,他們拋棄了我 bos.write(97); //a bos.write("媽媽,他們拋棄了我".getBytes(),0,6);//媽媽 bos.flush(); //通常使用輸出流的時候,儘可能把flish寫出來
} catch (Exception e) { e.printStackTrace(); } } }
注意,此處若沒用自動關流,因爲緩存區的內存沒被寫滿,因此內容不會被寫進a.txt,
1.2 緩衝字節輸入流
BufferedInputStream(InputStream)
BufferedInputStream(InputStream)
BufferedInputStream(InputStream,int size) size: 緩衝區大小,默認8k
其讀取數據的方法和FileInputStream是同樣的(見上)
public class BufferedInputStreamDemo { public static void main(String[] args) { try ( BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:/b.txt"));// b.txt中的內容爲:這個世界會好嗎 ){ byte[] bs = new byte[1024]; int len ; while((len = bis.read(bs)) != -1) { //判斷數據讀完的條件 System.out.println(new String(bs,0,len)); } } catch (Exception e) { e.printStackTrace(); } } }
練習:使用BufferedOutputStream/BufferedInputStream拷貝文件,並比較和FileInput的拷貝性能
1 public class CopyFile { 2 public static void fileStream(String srcPath,String destPath) { 3 long start = System.currentTimeMillis(); 4 try( 5 FileInputStream fis = new FileInputStream(srcPath); 6 FileOutputStream fos = new FileOutputStream(destPath); 7 ) { 8 byte[] bs = new byte[1024]; 9 int len; 10 while((len = fis.read(bs)) != -1) { 11 fos.write(bs,0,len); 12 } 13 long end = new Date().getTime(); 14 System.out.println("字節流耗時爲:"+(end-start)+"毫秒"); 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 } 19 public static void bufferedFileStream(String srcPath,String destPath) { 20 long start = System.currentTimeMillis(); 21 try ( 22 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcPath)); 23 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath)); 24 ){ 25 int len; 26 byte[] bs = new byte[1024]; 27 while((len = bis.read(bs)) != -1) { 28 bos.write(bs,0,len); 29 } 30 long end = new Date().getTime(); 31 System.out.println("緩衝字節流耗時爲:"+(end-start)+"毫秒"); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 } 36 }
運行結果是:緩衝字節流與字節流拷貝同一個文件,前者花了95毫秒,後者花了405毫秒,可見緩衝字節流效率很高
2.轉換字節流
2.1 前提
轉換流的本質是一種字符流,爲何叫轉換流,由於其構造方法中有個字節流對象參數,至關於將字節流對象轉爲字符流對象
2.1.1 轉換流出現的緣由及思想
因爲字節流操做中文不是特別的方便,因此,java就提供了轉換流,其本質就是帶了編碼表的字節流,即:字符流=字節流+編碼表
2.1.2 字符串中的編碼問題
編碼:把文字轉爲二進制
解碼:把二進制轉成文件
字符流只能處理純文本文件
2.2 字符輸出流
2.2.1 構造方法
(1)public OutputStreamWriter(OutputStream)
(2)public OutputStreamWriter(OutputStream out, String charsetName):此處的charsetName表示設置編碼的格式,默認是utf-8
2.2.2 成員方法(用法和字節流差很少,只是這裏的參數由byte數組轉換爲char數組,此外還可使用String參數):
(1)public void write(int c)
(2)public void write(char[ ] cbuf)
(3)public void write(char[ ] cbuf, int len)
(4)public void write(String str)
(5)public void write(String str,int off,int len)
public class OutputStreamWriterDemo { public static void main(String[] args) { try ( OutputStreamWriter osw = new OutputStreamWriter((new FileOutputStream("e:/a.txt")),"gbk"); ){ osw.write("突然就流出淚來,突然間想要聽到她的聲音,而我卻一我的越走越遠"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2.3 字符輸入流
2.3.1 構造方法
(1)public InputStreamReader(InputStream in)
(2)public InputStreamReader(InputStream in,String charsetName)
2.3.2 成員方法
(1)public int read()
(2)public int read(char[] cbuf)
public class InputStreamReaderDemo { public static void main(String[] args) { try ( InputStreamReader isr = new InputStreamReader(new FileInputStream("e:/a.txt"),"gbk"); //此處必定要用cbk編碼去讀取數據,由於.txt是用gbk編碼格式寫入的 ){ char[] chs = new char[1024]; int len; while((len = isr.read(chs)) != -1) { System.out.println(new String(chs,0,len)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //運行結果:突然就流出淚來,突然間想要聽到她的聲音,而我卻一我的越走越遠
2.4 字符流拷貝文件
public static void charFileStream(String srcPath,String destPath) { long start = System.currentTimeMillis(); try ( InputStreamReader isr = new InputStreamReader(new FileInputStream(srcPath)); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destPath)); ){ char[] chs = new char[1024]; int len; while((len = isr.read(chs)) != -1) { osw.write(chs,0,len); } long end = new Date().getTime(); System.out.println("字符流耗時爲:"+(end-start)+"毫秒"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
這裏爲了比較字符流拷貝文件與字節流以及緩衝字節流拷貝文件的性能,此處同時進行了這三種拷貝文件方法拷貝同一個文件的耗時比較
測試類
public class CopySpendTime { public static void main(String[] args) { CopyFile.bufferedFileStream("E:\\linked.mkv", "e:/鏈表1.mkv"); CopyFile.fileStream("E:\\linked.mkv", "e:/鏈表2.mkv"); CopyFile.charFileStream("E:\\linked.mkv", "E:\\linked1.mkv"); } }
運行結果:
3 轉換流的簡化寫法,也叫簡化流(字符流),其沒法指定編碼格式
3.1 讀寫
轉換流的名字比較長,而咱們常見的操做都是照本地默認編碼實現的,因此,爲了簡化咱們的書寫,轉換流提供了對應的子類,即FileWriter和FileReader
FileWriter:
其構造方法有不少種,這裏只列出其能夠傳什麼參數的簡單構造方法,至因而否追加或是用什麼編碼方式的構造方法,能夠直接看源碼
(1)public FileWriter(String fileName)
(2)public FileWriter(File file)
public class FileWriterDemo { public static void main(String[] args) { try ( FileWriter fw = new FileWriter("e:/a.txt"); ){ fw.write("咱們生來就是孤獨"); } catch (IOException e) { e.printStackTrace(); } } }
FileReader:
用法相似FileWrite
public class FileReaderDemo { public static void main(String[] args) { try ( FileReader fr = new FileReader("e:/a.txt"); ){ char[] chs = new char[1024]; int len; while((len = fr.read(chs)) != -1) { System.out.println(new String(chs,0,len)); } } catch (IOException e) { e.printStackTrace(); } } }
在一個程序中先寫後讀,要注意:寫完了之後要關流,不然輸出流會繼續佔用文件,致使讀取不回來內容
public class NoticeDemo { public static void main(String[] args) { try ( FileWriter fw = new FileWriter("e:/a.txt"); //只要執行這個語句就會建立一個a.txt文件,若加true就不會覆蓋a.txt原有的內容(如有帶內容的a.txt文件) FileReader fr = new FileReader("e:/a.txt"); ){ fw.write("下起了雨,你覺的冷嗎"); fw.close(); //此處必定要關流,不然輸出流會繼續佔用該文件 char[] chs = new char[1024]; int len; while((len = fr.read(chs)) != -1) { System.out.println(new String(chs,0,len)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3.2 簡化流拷貝文件
//簡化流拷貝文件 public static void SimpleStream(String srcPath, String destPath) { long start = System.currentTimeMillis(); try ( FileWriter fw = new FileWriter(destPath); FileReader fr = new FileReader(srcPath); ){ char[] chs = new char[1024]; int len; while((len = fr.read(chs)) != -1) { fw.write(chs,0,len); } long end = System.currentTimeMillis(); System.out.println("簡化流耗時爲:"+(end-start)+"毫秒"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
注意srcPath和destPath地址別寫反了
4. 緩衝字符流
BufferedReader/bufferedWriter
4.1 緩衝字符輸出流
相似緩衝字節流,其構造方法也要傳相應流的對象,不能想字符流同樣傳字符串
特有方法:
new Line:換行
public class BufferedWriterDemo { public static void main(String[] args) { try ( BufferedWriter bw = new BufferedWriter(new FileWriter("e:/a.txt"));//寫法簡單,但此種獲得緩衝字符流的方法不經常使用 BufferedWriter bw1 = new BufferedWriter (new OutputStreamWriter(new FileOutputStream("e:/b.txt")));//很經常使用,由於咱們獲得的數據通常都爲字節流,因此現將字節流包裝成轉換流,再講轉換流包裝成緩衝字符流 ){ bw.write("這被禁忌的遊戲"); bw.newLine(); //用於換行 bw.write("一如既往的歲月"); bw.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4.2 緩衝字符輸入流
特有的方法:readLine 讀取一行
注意:儘可能不要使用readLine去拷貝文件(有可能會形成空行的丟失)
4.3 緩衝字符流拷貝文件
//緩衝字符流拷貝文件 public static void bufferedCharStream(String srcPath, String destPath) { long start = System.currentTimeMillis(); try ( // BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destPath))); BufferedWriter bw = new BufferedWriter(new FileWriter(destPath)); // BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcPath))); BufferedReader br = new BufferedReader(new FileReader(srcPath)); ){ char[] chs = new char[1024]; int len; while((len = br.read(chs)) != -1) { bw.write(chs,0,len); } long end = System.currentTimeMillis(); System.out.println("緩衝字符流耗時爲:"+(end-start)+"毫秒"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
練習
1. 把ArrayList集合中的字符串數據存儲到文本文件,要求每一個元素佔一行,而後從文本文件中讀取數據(每一行爲一個字符串數據 )到集合中,並遍歷集合
public class Exer1 { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("熱河"); arrayList.add("下雨"); arrayList.add("梵高先生"); arrayList.add("山蔭路上的夏天"); ArrayList<String> array = new ArrayList<>(); try ( BufferedWriter bw = new BufferedWriter(new FileWriter("e:/a.txt")); BufferedReader br = new BufferedReader(new FileReader("e:/a.txt")); ){ //將集合總的內容寫入文件a.txt for (String str : arrayList) { // char[] chs = str.toCharArray(); bw.write(str); bw.newLine(); } bw.close(); // 將文件a.txt中的內容寫入集合array String str = null; while((str = br.readLine()) != null) { array.add(str); } //遍歷集合元素 for (String arr : array) { System.out.println(arr); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2. 複製單級文件夾,複製單級文件夾中指定文件並
(1) 和原來名字保持一致
(2)修改新的名字(使用納秒值命名)
5. 序列化和對象流
5.1 概述
java序列化是指把java對象轉換爲字節序列(二進制)的過程,java反序列化是指把字節序列恢復爲java對象的過程,當兩個java進程進行通訊時,發送方須要把這個java對象轉換爲字節序列,而後在網絡上傳送;另外一方面,接收方須要從字節序列中恢復java的對象
持久化:把內存數據存儲到磁盤上(通常數據庫)
5.2 java序列化API
(1)ObjectOutputStream:表示對象輸出流
writeObject(Object obj)方法能夠對參數指定的obj對象進行序列化,把獲得的字節序列寫到一個目標輸出流中
(2)ObjectInputStream:表示對象輸入流
readObject()方法從輸入流中讀取字節序列,再把它們反序列化成爲一個對象,並將其返回
5.3 java對象序列化要求
(1)假定一個Person類,它的對象須要序列化
只有實現了Serializable或Externalizable接口的類的對象才能序列化,不然拋出異常
(2)構造方法
ObjectOutputStream:
public ObjectOutputStream(OutputStream out)
ObjectInputStream:
public ObjectInputStream(InputStream in)
(3)注意事項
若Person類僅僅實現了Serializable接口,則能夠按照如下方式進行序列化和反序列化
ObjectOutputStream採用默認的序列化方式,對Person對象的非transient的實例變量進行序列化
ObjectInputStream採用默認的反序列化方式,對Person對象的非transient的實例變量進行反序列化
(4)實現序列化步驟
1. 讓類實現Serializable接口
2. 使用ObjectOutputStream寫數據:調用writeObject
3. 使用ObjectInputStream讀數據:調用readObject
若是報錯:NotSerializable,檢查是否實現了Serializable接口
若是報錯:java.io.InvalidClassException: com._51doit.javase.day17.Person; local class incompatible: stream classdesc serialVersionUID = 7515133156099803333, local class serialVersionUID = 4493040116463270318
是由模板(如Person類中重寫了toString方法)改變致使的:
解決方法:
1. 重寫一遍,而後再進行讀操做
2. 生成序列化id(此ID只是改變前的id)
光標放到類名的黃線上,選擇兩個中的任意一種(這個時候再該模板後,進行讀操做就不會報錯)
案例
定義Person類(必定要實現Serializable接口)
public class Person implements Serializable{ String name; int age; char gender; public Person(String name,int age,char gender) { this.name = name; this.age = age; this.gender = gender; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]"; } }
寫
public class ObjectOutputStreamDemo { public static void main(String[] args) { Person p = new Person("老王",38,'男'); try ( ObjectOutputStream oot = new ObjectOutputStream(new FileOutputStream("e:/a.txt")); ){ oot.writeObject(p);
oot.writeInt(100); //此處要注意寫入的順序,這裏是先寫p,再寫的100,讀的時候也要按照這個順序讀 oot.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
將內容讀出來
public class ObjectInputStreamDemo { public static void main(String[] args) { try ( ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e:/a.txt")); ){ Object o = ois.readObject();
ois.readIn(); // 此處若將這一行和上一行換個順序,將會出現java.io.EOFException錯誤,由於寫入文件的內容是先p,再100 Person p = (Person)o; System.out.println(p.name+p.age+p.gender); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果:老王38男
練習:使用對象流寫一個person對象,再寫一個Map<Integer,Person>