第8次文章:其他流
- 2019 年 10 月 8 日
- 筆記
各位小夥伴兒,大家周末愉快哈
1、位元組數組 位元組 節點流
在兩台電腦上進行數據的相互的傳輸
輸入流 :ByteArrayInputStream
使用方法:read(byte[] b,int off,int len) +close()
輸出流 :ByteArrayOutputStream
使用方法:write(byte[] b,int off,int len) +toByteArray() + close() 不要使用多態
例如:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class ByteArrayDemo01 { public static void main(String[] args) throws IOException { read(write()); } /** * 寫出操作 * 位元組輸出流與文件輸出有些不同,因為位元組輸出流有新增方法,所以不可以使用多態 * @throws IOException */ public static byte[] write() throws IOException { //目標位置 byte[] dest ; //選擇流 這裡有不同點:沒有將目標位置和輸出流進行關聯 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //操作 寫出 String msg = "位元組輸入流與文件輸入流一致"; byte[] info = msg.getBytes(); bos.write(info, 0, info.length); //獲取數據 dest = bos.toByteArray(); //釋放資源 bos.close(); return dest; } /** * 位元組輸入流(ByteArrayInputStream)與文件輸入流一致(FileInputStream) * @throws IOException */ public static void read(byte[] src) throws IOException { //數據源 使用外部傳入的方式來獲取 //選擇流。通過流,打開另一端電腦的數據,所以不能在數據源的地方進行列印輸出 InputStream is = new BufferedInputStream(//緩衝流 new ByteArrayInputStream(src)//節點流 ); //操作 byte[] info =new byte[1024]; int len = 0; while(-1 != (len=is.read(info))) { System.out.println(new String(info,0,len)); } //釋放資源 is.close();//此處的close是一個空命令,兩台電腦之間不需要釋放資源 } }
在寫出操作的時候,與文件輸出流不同的是:位元組輸出流沒有和寫出的目標位置相關聯,而是直接將內容寫進位元組數組輸出流中,然後使用位元組數組輸出流將內容轉化為位元組數組,並返回給目標位置。
二、處理流
1、基本類型 + String 保留數據+類型
輸入流:DateInputStream readXxx
輸出流:DateOutputStream writeXxx
在之前的IO流中,我們僅僅是將字元相關的資訊保存在了相應的文件中,卻沒有注意各個保存的內容參數的類型,比如int,double,float,long,String等等,那麼這樣的保存,在我們後期的調用相關數據時,難以快速確定各個參數的類型,所以會降低效率。現在使用DateInputStream和DateOutputStream時,就可以有效的避免這樣的問題。保存數據的時候,同時保存數據相對應的類型。
例如下面的實例:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; /** * 數據類型(基本類型+String)處理流 * 在保存時,同時保存數據以及其對應的數據類型 */ public class DataDemo2 { public static void main(String[] args) throws IOException { byte[] data = write(); read(data); System.out.println(data.length); } /** * 數據+類型 從位元組數組中讀取 * @throws IOException */ public static void read(byte[] src) throws IOException { //選擇流 //選擇位元組輸入流 InputStream is = new ByteArrayInputStream(src); DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) ); //操作 讀取的順序必須與寫出的順序一致 必須存在才能讀取 double point = dis.readDouble(); long num = dis.readLong(); String str = dis.readUTF(); System.out.println(point+"--->"+num+"--->"+str); //釋放資源 dis.close(); } /** * 數據+類型 輸出到位元組數組 * @param destPath * @throws IOException */ public static byte[] write() throws IOException { double point = 2.5; long num = 100L; String str = "數據類型"; //創建目的地 byte[] dest = null; //選擇流 //選擇位元組數組輸出流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(//數據類型處理流 new BufferedOutputStream(//緩衝處理流 bos//建立聯繫 ) ); //寫出的順序很重要,因為後續在告訴用戶的過程中,需要告訴用戶第幾個參數是什麼含義 dos.writeDouble(point); dos.writeLong(num); dos.writeUTF(str); dos.flush(); //獲取數據 dest = bos.toByteArray(); //釋放資源 dos.close(); return dest; } }
在讀取最後的寫出結果時,可以直接識別出寫出的結果是什麼類型的,這樣就可以大大的提高運行效率。
2、引用類型(對象) 保留數據+類型
反序列化:輸入流:ObjectInputStream readObject()
序列化:輸出流:ObjectOutputStream writeObject()
注意:
1)先序列化後反序列化;但序列化的順序必須和序列化的順序相同
2)不是所有的對象都可以序列化,需要對象實現介面java.io.Serializable
不是對象的所有屬性都需要序列化,可以在不需要序列化的屬性前面加上修飾符transient
例如:
1)創建Employee類,實現介面java.io.Serializable,同時利用transient修飾屬性name,使得name不進行序列化。
package com.peng.IO.others; /** * 空介面只是一個標識 * */ public class Employee implements java.io.Serializable{//此處是一個空介面 //transient表示該屬性不需要序列化 private transient String name; private double salary; public Employee(String name, double salary) { super(); this.name = name; this.salary = salary; } public Employee() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
2)對Employee對象進行序列化寫出以及反序列化讀取
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; /** * 數據類型(不僅限於基本數據類型,可以為引用數據類型,對象)處理流 * 1、序列化------>寫出 ObjectOutputStream * 2、反序列化----->讀取 ObjectInputStream * * 不是所有的對象都可以序列化 java.io.NotSerializableException * 不是所有的屬性都需要序列化 transient * */ public class ObjectDemo01 { public static void main(String[] args) throws IOException, ClassNotFoundException { seri("E:\java學習\test\seri.txt"); read("E:\java學習\test\seri.txt"); } //反序列化 讀取 public static void read(String srcPath) throws FileNotFoundException, IOException, ClassNotFoundException { //創建源 File src = new File(srcPath); //選擇流 ObjectInputStream ObjectInputStream ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream(src) ) ); //操作 Object obj = ois.readObject(); if (obj instanceof Employee) { Employee temp = (Employee) obj; System.out.println(temp.getName()); System.out.println(temp.getSalary()); } Object obj1 = ois.readObject(); int[] arr = (int[]) obj1; System.out.println(Arrays.toString(arr)); //釋放資源 ois.close(); } //序列化 寫出 public static void seri(String destPath) throws FileNotFoundException, IOException { Employee temp = new Employee("鵬程萬里",1000); int[] arr = {1,2,3,4,5}; //創建源 File dest = new File(destPath); //選擇流ObjectOutputStream ObjectOutputStream dos = new ObjectOutputStream( new BufferedOutputStream(//緩衝流 new FileOutputStream(dest)//節點流 ) ); dos.writeObject(temp); dos.writeObject(arr); dos.flush(); dos.close(); } }
3)查看結果
null 1000.0 [1, 2, 3, 4, 5]
由於我們使用了transient對Employee對象的name屬性進行了修飾,所以在反序列化(讀取)的時候,並沒有輸出Employee對象的name屬性。
注意:
1)數組也屬於一個對象,所以在序列化的時候,可以將數組也作為一個對象進行序列化和反序列化。
2)在反序列化(讀取)時,讀取的順序必須與序列化(寫出)的順序相同,所以在寫出的時候,需要注意寫出的對象的順序,避免後期讀取的時候報錯。
3)寫出的文件內容是給電腦查看的內容,保留有各個屬性的類型,所以我們查看路徑「E:java學習testseri.txt」的內容時,所看到的是一堆亂碼。如下圖所示:

三、列印流
1、在java編程中,我們經常使用System.out.println()命令,將我們想要看到的變數列印在控制台上,便於我們看到變數的具體內容。而System.out.println()命令也屬於IO流中的一種,也屬於處理流中的一種。列印流的使用方法,也和其他的處理流的使用方法相同:
package com.peng.IO.others; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; /** * PrintStream 列印流---->處理流 */ public class PrintStreamDemo01 { public static void main(String[] args) throws FileNotFoundException { System.out.println("test"); PrintStream ps = System.out; ps.println(false); //輸出到文件 File src = new File("E:\java學習\test\print.txt"); ps = new PrintStream(//處理流 new BufferedOutputStream(//緩衝流 new FileOutputStream(src)//節點流--- 位元組流 ) ); ps.println("peng is very good"); ps.flush(); ps.close(); } }
通過上面的程式碼可以看出來,列印流(PrintStream)使用的是其自己的方法println方法,將字元串寫出到目標文件中。
2、在列印流中,有幾個基本的知識點這裡提一下:
1)三個常量:
- System.in 輸入流—–鍵盤輸入
- System.out 輸出流—–控制台輸出
- System.err 輸出流—–控制台輸出 其中,System.out和System.err的功能完全相同,都是將相應的提示資訊輸出到控制台上,唯一的差別就是在控制台上輸出的資訊的字體顏色不同,System.out輸出到控制台上的字體顏色默認為黑色,而System.err輸出到控制台上的字體顏色默認為紅色。
2)重定向
由於在上述的三個常量,默認的輸入與輸出都是在控制台上進行,假如用戶需要將資訊輸出到相應的文本文件中,而不是顯示在控制台上,那麼就需要使用重定向,將其輸入與輸出的位置進行重新關聯。
例如:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.util.Scanner; public class SystemDemo01 { public static void main(String[] args) throws FileNotFoundException { test(); } public static void test() throws FileNotFoundException { //重定向 System.setOut(new PrintStream(//本質上還是一個輸出流 ---- 處理流 new BufferedOutputStream( new FileOutputStream( new File("E:/java學習/test/print.txt") ) ),true ) ); //此時的輸出已經不在控制台中了,將所需要輸出的字元輸出到指定的文件中 System.out.println("test");//控制台----->文件 System.out.println("one produce two, two produce three,three produce all things."); //重新回到控制台 System.setOut(new PrintStream( new BufferedOutputStream( new FileOutputStream( FileDescriptor.out ) ),true ) ); System.out.println("back........."); } }
重定向主要使用三條命令進行實現:SetIn();SetOut();SetErr(),三條命令分別設定輸入,輸出,以及輸出錯誤資訊的位置。
控制台的目標位置同樣使用三條命令進行實現:FileDescriptor.in;FileDescriptor.out;FileDescriptor.err,三條命令分別代表輸入,輸出和錯誤資訊在返回到控制台上的位置。
四、裝飾設計模式
1、類與類之間的關係
1)依賴:形參||局部變數
2)關聯:屬性
聚合:屬性 整體與部分 不一致的生命周期 人與手
組合:屬性 整體與部分 一致的生命周期 人與大腦
3)繼承:父子類關係
4)實現:介面與實現類的關係
2、IO流中使用的屬於裝飾設計模式
也就是利用不同的處理流,對節點流進行包裝處理。比如在老師講課的時候,使用擴音器,就屬於將老師的講課聲音進行了擴大,對原聲進行了裝飾。
例如:
1)我們先建立一個聲音類
package com.peng.IO.pattern; public class Voice { private int voice = 10; public Voice() { } public Voice(int voice) { super(); this.voice = voice; } public int getVoice() { return voice; } public void setVoice(int voice) { this.voice = voice; } public void say() { System.out.println(voice); } }
2)然後建立一個擴音器
package com.peng.IO.pattern; public class Amplifier { private Voice voice; public Amplifier() { } public Amplifier(Voice voice) { super(); this.voice = voice; } public Voice getVoice() { return voice; } public void setVoice(Voice voice) { this.voice = voice; } public void say() { System.out.println(voice.getVoice()*1000); } }
3)最後應用一下
package com.peng.IO.pattern; public class App { public static void main(String[] args) { Voice v =new Voice(20); v.say(); Amplifier am = new Amplifier(v); am.say(); } }
最終的結果當然就是我們將聲音的數值擴大了1000倍,這就是簡單的使用了裝飾模式。類比在IO流的使用上,我們依舊是首先利用處理流,使用處理流對緩衝流進行包裝,然後再利用緩衝流對節點流或者字元流進行包裝,最後實現我們需要的功能。