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