常见字节流
導航
- FileInputStream/FileOutputStream
- ByteArrayInputStream/ByteArrayOutputStream
- BufferedInputStream/BufferedOutputStream
- DataInputStream/DataOutputStream
- ObjectInputStream/ObjectOutputStream
- 序列化與反序列化
??以字節為單位獲取數據的流,稱為字節流,它們都繼承于InputStream/OutputStream抽象類。
??常用的字節流有:
- FileInputStream/FileOutputStream,文件字節流(節點流)
- ByteArrayInputStream/ByteArrayOutputStream,字節數組流(節點流)
- BufferedInputStream/BufferedOutputStream,字節緩存流(處理流)
- DataInputStream/DataOutputStream,數據字節流(處理流)
- ObjectInputStream/ObjectOutputStream,對象流(處理流)
??下面分別介紹一下這五組字節流及用法。
FileInputStream/FileOutputStream
??FileInputStream/FileOutputStream是最常用的文件字節流,當數據源為文件對象時,選擇這個數據流進行處理。我們可以通過構造器快速了解它的操作數據類型:
public FileInputStream(File file)public FileOutputStream(File file)??可以看到,這組數據流的操作對象為File類型。
??關于數據流的操作,總是分為四個步驟:
- 1.指定數據源
- 2.選擇合適的數據流
- 3.進行數據操作
- 4.關閉流,釋放資源
??來一個文件的拷貝實例,了解其用法:
public class FileStream {public static void main(String[] args) {//1.指定數據源File in_scr = new File("a.txt"); // 輸入數據為文件File out_scr = new File("a-copy.txt"); // 輸出數據也為文件InputStream is = null;OutputStream os = null;try {//2.選擇合適的數據流is = new FileInputStream(in_scr);os = new FileOutputStream(out_scr);//3.進行數據操作byte[] flush = new byte[1024]; //字節流,數據以字節數組為單位int len = -1;while( (len = is.read(flush)) != -1 ) { // for(int i=0;i<len;i++) { // System.out.println(flush[i]); 可以將輸入流直接打印出來觀察 // }os.write(flush,0,len);}os.flush(); //將緩存內容輸出} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {//4.關閉流,釋放資源//先打開的先關閉try {if(is != null) {is.close();}} catch (IOException e) {e.printStackTrace();}try {if(os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}}??需要注意的點有:
- 1.flush為byte[]數組,與形參不同,數組傳入的是地址,所以在is.read(flush)方法中操作flush,外面的flush也會變換,read方法就是給flush賦值的一個方法,并且有返回值:讀取到的元素個數,未讀到時返回-1。
- 2.輸出流中是有緩存的,就是說當輸出流的數據量到達一定程度,才會輸出。所以在結尾,我們需要手動將未到達緩存量的數據輸出,使用輸出流的flush()方法。
ByteArrayInputStream/ByteArrayOutputStream
??ByteArrayInputStream/ByteArrayOutputStream,字節數組流,也是一個常用的節點流。當數據對象字節數組byte[ ]時,使用這種流進行操作。觀察一下它們的構造器:
public ByteArrayInputStream(byte buf[])public ByteArrayOutputStream()??可以看到,輸入流的操作對象未byte[ ]字節數組;輸出流無參數,并不能直接將流中數據輸出為文件等,但ByteArrayOutputStream有toString()等方法,來方便對輸出流進行操作。
??還是來一個文件的拷貝實例,了解其用法:
public class ByteArrayStream {public static void main(String[] args) {//1.指定數據源byte[] in_scr = new byte[] { -28, -67, -96, -26, -120, -111, -28, -69, -106 };//輸入數據ByteArrayInputStream bais = null;ByteArrayOutputStream baos = null;try {//2.選擇合適的數據流bais = new ByteArrayInputStream(in_scr);baos = new ByteArrayOutputStream();//3.進行數據操作byte[] flush = new byte[1024];int len = -1;System.out.print("輸入流:");while ((len = bais.read(flush)) != -1) {for (int i = 0; i < len; i++) {System.out.print(flush[i]+","); //讀取輸入流}baos.write(flush, 0, len);}System.out.println();baos.flush(); //將緩存內容輸出,到此,輸出流的數據寫入已完成byte[] byteArray = baos.toByteArray(); //將輸入流轉換為字節數組System.out.print("輸出流:");for (byte b : byteArray) {System.out.print(b+",");}System.out.println();System.out.println(baos.size());//輸出流的元素個數System.out.println(baos.toString("UTF-8"));//注意,與byte[]的toString()不同} catch (IOException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源try {if (bais != null) {bais.close();}} catch (IOException e) {e.printStackTrace();}try {if (baos != null) {baos.close();}} catch (IOException e) {e.printStackTrace();}}}} --------------------------------------------------- 輸出結果為: 輸入流:-28,-67,-96,-26,-120,-111,-28,-69,-106, 輸出流:-28,-67,-96,-26,-120,-111,-28,-69,-106, 9 你我他BufferedInputStream/BufferedOutputStream
??BufferedInputStream/BufferedOutputStream,字節緩存流,是一種處理流,使用了裝飾設計模式,通過內部緩存數組來提高操作流的效率。構造器源碼:
public BufferedInputStream(InputStream in)public BufferedOutputStream(OutputStream out)??可以看到BufferedInputStream/BufferedOutputStream只能對流進行操作,而不能直接操作數據,所以我們稱它為處理流。
??當對文件或者其他數據源進行頻繁的讀寫操作時,效率比較低,這時如果使用緩沖流就能夠更高效的讀寫信息。因為緩沖流是先將數據緩存起來,然后當緩存區存滿后或者手動刷新時再一次性的讀取到程序或寫入目的地。
??緩存區的大小默認是8192字節(8K,1024*8),也可以使用其它的構造方法自己指定大小。
??因此,緩沖流還是很重要的,我們在IO操作時都可以加上緩沖流來提升性能。
??來一個文件復制的效率對比實例,了解用法與性能的提升:
public class BufferedStream {public static void main(String[] args) {// 使用緩沖字節流實現復制long time1 = System.currentTimeMillis();bufferedCopy("a.mp4", "a-copy1.mp4");long time2 = System.currentTimeMillis();System.out.println("緩沖字節流花費的時間為:" + (time2 - time1));// 使用普通字節流實現復制long time3 = System.currentTimeMillis();fileCopy("a.mp4", "a-copy2.mp4");long time4 = System.currentTimeMillis();System.out.println("普通字節流花費的時間為:" + (time4 - time3));}public static void bufferedCopy(String in_path, String out_path) {//1.指定數據源File in_scr = new File(in_path);File out_scr = new File(out_path);InputStream is = null;OutputStream os = null;try {//2.選擇合適的數據流is = new BufferedInputStream(new FileInputStream(in_scr));os = new BufferedOutputStream(new FileOutputStream(out_scr));//3.進行數據操作byte[] flush = new byte[1024];int len = -1;while ((len = is.read(flush)) != -1) {os.write(flush, 0, len);}os.flush(); // 將緩存內容輸出} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源// 先打開的先關閉try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();}try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}public static void fileCopy(String in_path, String out_path) {//1.指定數據源File in_scr = new File(in_path);File out_scr = new File(out_path);InputStream is = null;OutputStream os = null;try {//2.選擇合適的數據流is = new FileInputStream(in_scr);os = new FileOutputStream(out_scr);//3.進行數據操作byte[] flush = new byte[1024];int len = -1;while ((len = is.read(flush)) != -1) {os.write(flush, 0, len);}os.flush(); // 將緩存內容輸出} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源// 先打開的先關閉try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();}try {if (os != null) {os.close();}} catch (IOException e) {e.printStackTrace();}}}} ----------------------------------------------- 輸出結果為: 緩沖字節流花費的時間為:224 普通字節流花費的時間為:972DataInputStream/DataOutputStream
??DataInputStream/DataOutputStream,數據字節流(處理流),也是操作流的處理流,構造器如下:
public DataInputStream(InputStream in)public DataOutputStream(OutputStream out)?? 除了對流的包裝處理外,DataInputStream/DataOutputStream還可以實現直接對Java基礎數據類型(int、double、String等)直接進行讀寫操作。
?? 需要注意的是,使用數據流時,讀取的順序一定要與寫入的順序一致,否則不能正確讀取數據。
??先來一個對流的處理實例,還是復制為功能:
public class DataStream1 {public static void main(String[] args) {//1.指定數據源File in_scr = new File("a.txt");File out_scr = new File("a-copy.txt");DataInputStream dis = null;DataOutputStream dos = null;try {//2.選擇合適的數據流dis = new DataInputStream(new FileInputStream(in_scr));dos = new DataOutputStream(new FileOutputStream(out_scr));//3.進行數據操作byte[] flush = new byte[1024];int len = -1;while( (len = dis.read(flush)) != -1 ) { // for(int i=0;i<len;i++) { // System.out.print(flush[i]); // }dos.write(flush, 0, len);}dos.flush();} catch (IOException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源//先打開的先關閉try {if(dis != null) {dis.close();}} catch (IOException e) {e.printStackTrace();}try {if(dos != null) {dos.close();}} catch (IOException e) {e.printStackTrace();}}}}??再來一個直接讀寫基本數據類型的實例:
public class DataStream2 {public static void main(String[] args) {//1.指定數據源File in_scr = new File("a.txt"); File out_scr = new File("a.txt");//輸入數據源與輸出數據源一致,先寫再讀DataInputStream dis = null;DataOutputStream dos = null;try {//2.選擇合適的數據流dis = new DataInputStream(new FileInputStream(in_scr));dos = new DataOutputStream(new FileOutputStream(out_scr));//3.進行數據操作//將如下數據寫入到文件中dos.writeChar('a');dos.writeInt(10);dos.writeDouble(Math.random());dos.writeBoolean(true);dos.writeUTF("你我他");//Stirng類型//手動刷新緩沖區:將流中數據寫入到文件中dos.flush();//直接讀取數據:讀取的順序要與寫入的順序一致,否則不能正確讀取數據。System.out.println("char: " + dis.readChar());System.out.println("int: " + dis.readInt());System.out.println("double: " + dis.readDouble());System.out.println("boolean: " + dis.readBoolean());System.out.println("String: " + dis.readUTF());} catch (IOException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源//先打開的先關閉try {if(dis != null) {dis.close();}} catch (IOException e) {e.printStackTrace();}try {if(dos != null) {dos.close();}} catch (IOException e) {e.printStackTrace();}}}} ----------------------------------------- 輸出結果為: char: a int: 10 double: 0.8627686668141537 boolean: true String: 你我他ObjectInputStream/ObjectOutputStream
??DataInputStream/DataOutputStream可以直接讀寫Java的基本數據類型。但Java中除了基本數據類型,還有引用數據類型,那么我們自己定義類,能不能也以類的方式放入IO流中呢?
??ObjectInputStream/ObjectOutputStream就是Java提供的操作對象的流,也叫對象流(處理流),構造器源碼:
public ObjectInputStream(InputStream in)public ObjectOutputStream(OutputStream out)??也是需要對流進行操作,但使用它后,可以直接讀寫任意的數據類型。
??需要注意的是,使用數據流時,讀取的順序一定要與寫入的順序一致,否則不能正確讀取數據。
序列化與反序列化
??需要注意的是,對象流中傳輸的對象必須支持進行序列化與反序列化操作:
??當兩個進程遠程通信時,彼此可以發送各種類型的數據。 無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。比如,我們可以通過http協議發送字符串信息;我們也可以在網絡上直接發送Java對象。發送方需要把這個Java對象轉換為字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復為Java對象才能正常讀取。
??把Java對象轉換為字節序列的過程稱為對象的序列化。把字節序列恢復為Java對象的過程稱為對象的反序列化。
??對象序列化的作用有如下兩種:
-
1.持久化: 把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中,比如:休眠的實現。以后服務器session管理,hibernate將對象持久化實現。
-
2.網絡通信:在網絡上傳送對象的字節序列。比如:服務器之間的數據通信、對象傳遞。
??只有實現了Serializable接口的類的對象才能被序列化。 Serializable接口是一個空接口,只起到標記作用。
??序列化中需要注意的是:
- 為了防止讀和寫的序列化ID不一致,一般指定一個固定的序列化ID。
- static屬性不參與序列化。
- 對象中的某些屬性如果不想被序列化,使用transient修飾。
??來一個支持序列化類的實例:
//實現Serializable接口 class Student implements Serializable{// 添加序列化ID,它決定著是否能夠成功反序列化!private static final long serialVersionUID = 1L;private transient int num;private String name;private int age;public Student(int num,String name, int age) {super();this.num = num;this.name = name;this.age = age;}@Overridepublic String toString() {return "Student [num=" + num + ", name=" + name + ", age=" + age + "]";}}??有了可以序列化的自定義類后,我們來試試使用ObjectInputStream/ObjectOutputStream流讀寫它:
public class ObjectStream {public static void main(String[] args) {//1.指定數據源File in_scr = new File("a.txt");File out_scr = new File("a.txt"); //輸入數據源與輸出數據源一致,先寫再讀ObjectInputStream ois = null;ObjectOutputStream oos = null;try {//2.選擇合適的數據流ois = new ObjectInputStream(new FileInputStream(in_scr));oos = new ObjectOutputStream(new FileOutputStream(out_scr));//3.進行數據操作//將如下數據寫入到文件中oos.writeChar('a');;oos.writeInt(10);oos.writeDouble(Math.random());oos.writeBoolean(true);oos.writeUTF("你我他");//實例化對象Student student = new Student(1,"小豬",18);oos.writeObject(student);//手動刷新緩沖區:將流中數據寫入到文件中oos.flush();//直接讀取數據:讀取的順序要與寫入的順序一致,否則不能正確讀取數據。System.out.println("char: " + ois.readChar());System.out.println("int: " + ois.readInt());System.out.println("double: " + ois.readDouble());System.out.println("boolean: " + ois.readBoolean());System.out.println("String: " + ois.readUTF());System.out.println("Object: " + ois.readObject());} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {//4.關閉流,釋放資源//先打開的先關閉try {if(oos != null) {oos.close();}} catch (IOException e) {e.printStackTrace();}try {if(ois != null) {ois.close();}} catch (IOException e) {e.printStackTrace();}}}} ---------------------------------------------------- 輸出結果為: char: a int: 10 double: 0.6826249100838067 boolean: true String: 你我他 Object: Student [num=0, name=小豬, age=18]總結
- 上一篇: OTRS 工单系统部署
- 下一篇: Ps和cdr的区别 初学者先学ps还是c