以下介紹IO包中擴展功能的流對象:基本都是裝飾設計模式。
Java.io.outputstream.PrintStream:打印流
1:提供了更多的功能,比如打印方法。可以直接打印任意類型的數據。
2:它有一個自動刷新機制,創建該對象,指定參數,對於指定方法可以自動刷新。
3:它使用的本機默認的字符編碼.
4:該流的print方法不拋出IOException。
構造函數。
PrintStream(File file) :創建具有指定文件且不帶自動行刷新的新打印流。
PrintStream(File file, String csn) :創建具有指定文件名稱和字符集且不帶自動行刷新的新打印流。
PrintStream(OutputStream out) :創建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) :創建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, Stringencoding) :創建新的打印流。
PrintStream(String fileName) :創建具有指定文件名稱且不帶自動行刷新的新打印流。
PrintStream(String fileName, String csn)
只有輸出目的爲 流的時候 才能夠設置 是否自動刷新 操作文件 則沒有設置自動刷新需要手動刷新
其println()的有功能是 在字符串後面加上換行符號 (能夠與Buffered的readLine()方法相呼應,TCP傳輸的細節)
PrintStream可以操作目的:1:File對象。2:字符串路徑。3:字節輸出流。
前兩個都JDK1.5版本纔出現。而且在操作文本文件時,可指定字符編碼了。
當目的是一個字節輸出流時,如果使用的println方法,可以在printStream對象上加入一個true參數。這樣對於println方法可以進行自動的刷新,而不是等待緩衝區滿了再刷新。最終print方法都將具體的數據轉成字符串,而且都對IO異常進行了內部處理。
--------------------------------------------------------
PrintWriter:是字符流的子類,可以直接操作字符數據,同時也可以指定具體的編碼。具備了PrintStream的特點同時,還有自身特點:
該對象的目的地有四個:1:File對象。2:字符串路徑。3:字節輸出流。4:字符輸出流。
只有輸出目的爲 流的時候 才能夠設置 是否自動刷新 操作文件 則沒有設置自動刷新需要手動刷新
建議:開發時儘量使用PrintWriter。
構造方法中(目的)直接操作文件的第二參數是編碼表。
(目的)直接操作輸出流的,第二參數是自動刷新。
//讀取鍵盤錄入將數據轉成大寫顯示在控制檯.
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));//源:鍵盤輸入
//目的:把數據寫到文件中,還想自動刷新。
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//設置true後自動刷新
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());//轉大寫輸出
}
//注意:System.in,System.out這兩個標準的輸入輸出流,在jvm啓動時已經存在了。隨時可以使用。當jvm結束了,這兩個流就結束了。但是,當使用了顯示的close方法關閉時,這兩個流在提前結束了。
out.close();
bufr.close();
**********************************************************************************************************************
SequenceInputStream:序列流,作用就是將多個讀取流合併成一個讀取流。實現數據合併。
表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的文件末尾爲止,並 補上文件結束的標緻。
這樣做,可以更方便的操作多個讀取流,其實這個序列流內部會有一個有序的集合容器,用於存儲多個讀取流對象。
構造方法
SequenceInputStream(InputStream s1,InputStream s2)
和並兩個輸入流爲一 最簡單
SequenceInputStream(Enumeration<? extendsInputStream> e)
喲要合併兩個以上流 涉及到集合枚舉
該對象的構造函數參數是枚舉,想要獲取枚舉,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中沒有枚舉,只有自己去創建枚舉對象。
但是方法怎麼實現呢?因爲枚舉操作的是具體集合中的元素,所以無法具體實現,但是枚舉和迭代器是功能一樣的,所以,可以用迭代替代枚舉。
ArrayList<FileInputStream> a = new ArrayList<FileInputStream>();
for (int i = 0; i < parts; i++) {
fis = new FileInputStream(new File(path,(i+1)+".part"));
System.out.println(i);
a.add(fis);
}
final Iterator it = a.iterator();//變量it在局部位置的內部類被訪問 所以it必須爲final
//Enumeration e = Collections.enumeration(a); 測試過不可以呀
Enumeration e = new Enumeration(){
@Override
public boolean hasMoreElements() {
// TODO Auto-generated method stub
return it.hasNext();
}
@Override
public Object nextElement() {
// TODO Auto-generated method stub
return it.next();
}};
SequenceInputStream ss = new SequenceInputStream(e);
FileOutputStream fos = new FileOutputStream(un);
byte[] buf = new byte[1024*1024];
int len = 0;
while ((len=ss.read(buf))!=-1) {
fos.write(buf, 0, len);
fos.flush();
}
合併原理:多個讀取流對應一個輸出流。
切割原理:一個讀取流對應多個輸出流。
切割大文件FileInputStream fis = new FileInputStream("E:/1998.rmvb");
FileOutputStream fos = new FileOutputStream("E:/1.part");
byte[] by = new byte[1024*1024];//創建一個存儲大小爲1M 的字節數組
int len = 0;
int count=1;
int i=0;
while ((len = fis.read(by))!=-1)
{
i++;
if(i==101)
{
count++;
fos = new FileOutputStream("E:/"+count+".part");
i=0;
}
fos.write(by,0,len);//字節數組內有多少 寫多少 字節流不需要刷新即可把數據寫入目標文件
}
fos.close();//養成關流的好習慣
fis.close();
類 ObjectOutputStream 實現了DataInput接口 能夠寫如完整的基本數據
注意被寫入的對象需要實現Serializable
構造方法
ObjectOutputStream(OutputStreamout) 不可直接操作文件 FileInputStream是專門操作文件的字節流
創建寫入指定OutputStream 的 ObjectOutputStream。
常用方法
void write(int val) 寫入一個字節。
void writeInt(int val) 寫入一個 32 位的 int 值。
void writeObject(Object obj) 將指定的對象寫入ObjectOutputStream。
類 ObjectInputStream
構造方法
ObjectInputStream(InputStreamin)
創建從指定InputStream 讀取的 ObjectInputStream。
常用方法
int readInt() 讀取一個 32 位的 int 值。
int read() 讀取數據字節。
Object readObject() 從ObjectInputStream 讀取對象。
ObjectOutputStream存的文件 只能用ObjectInputStream來讀取
對象的序列化:目的:將一個具體的對象進行持久化,寫入到硬盤上。
Serializable:給類定義標記,用於啓動對象的序列化功能,可以強制讓指定類具備序列化功能,該接口中沒有成員,這是一個標記接口。這個標記接口用於給序列化類提供UID。這個uid是依據類中的成員的數字簽名進行運行獲取的,成員變動則UID變化。如果不需要自動獲取一個uid,可以在類中,手動指定一個名稱爲serialVersionUID id號,static final longserialVersionUID = 42L; 依據編譯器的不同,或者對信息的高度敏感性。最好每一個序列化的類都進行手動顯示的UID的指定。
注意:靜態數據不能被序列化,因爲靜態數據不在堆內存中,是存儲在靜態方法區中,無法被序列化存到文件。
如何將非靜態的數據不進行序列化?用transient關鍵字修飾此變量即可。
****************************************************************************************************RandomAccessFile: 直接繼承Object 實現DataInput,DataOutput能夠讀和寫 能操作基本數據類型
特點:
1:該對象即可讀取,又可寫入。
2:該對象中的定義了一個大型的byte數組,通過定義指針來操作這個數組。
3:可以通過該對象的getFilePointer()獲取指針的位置,通過seek()方法設置指針的位置。
4:該對象操作的源和目的必須是文件。
5:其實該對象內部封裝了字節讀取流和字節寫入流。
注意:實現隨機訪問,最好是數據有規律。該類不算是IO體系中的子類,而是直接繼承自Object但是它是IO包中的成員,因爲它具備讀和寫功能,內部封裝了一個數組,而且通過指針對數組的元素進行操作,其實完成讀寫的原理就是內部封裝了字節輸入流和輸出流。
構造方法
RandomAccessFile(File file, String mode)
創建從中讀取和向其中寫入(可選)的隨機訪問文件流,該文件由 File 參數指定。
RandomAccessFile(String name, String mode)
創建從中讀取和向其中寫入(可選)的隨機訪問文件流,該文件具有指定名稱。
通過構造函數可看出 該類只能操作文件, 而且操作文件還有模式:只讀r 讀寫 rw
如果模式爲只讀 r ,不會創建文件。會去讀取一個已存在的文件,如果該文件不存在,則會出現異常
如果模式爲 rw的話,當操作的文件不存在,會自動創建,如果存在不會覆蓋
常用方法
long length() 返回此文件的長度。
int read() 從此文件中讀取一個數據字節。
int read(byte[] b,int off, int len) 將最多 len 個數據字節從此文件讀入 byte 數組。
int read(byte[] b,int off, int len) 將最多 len 個數據字節從此文件讀入 byte 數組。
讀取基本數據類型 readByte() readChar() readFloat() readDouble() readBoolean()
String readLine() 從此文件讀取文本的下一行。
void seek(longpos) 此文件開頭開始測量的指針偏移量,在該位置發生下一個讀取或寫入操作。
int skipBytes(intn) 嘗試跳過輸入的 n 個字節以丟棄跳過的字節。
void write(byte[]b) 將 b.length 個字節從指定 byte 數組寫入到此文件,並從當前文件指針開始。
void write(int b) 向此文件寫入低8位的int。
void writeInt(intv) 按四個字節將 int 寫入該文件,先寫高字節。
void writeLong(longv) 按八個字節將 long 寫入該文件,先寫高字節。
********************************************************************************************
管道流:管道讀取流和管道寫入流可以像管道一樣對接上,管道讀取流就可以讀取管道寫入流寫入的數據。
注意:需要加入多線程技術,因爲單線程,先執行read,會發生死鎖,而read方法是阻塞式的,沒有數據的read方法會讓線程等待。
類 PipedOutputStream
構造方法
PipedOutputStream(PipedInputStreamsnk) 創建連接到指定管道輸入流的管道輸出流。
常用方法
PipedOutputStream(PipedInputStreamsnk) 創建連接到指定管道輸入流的管道輸出流。
voidwrite(int b) 將指定 byte 寫入傳送的輸出流。
類 PipedInputStream
PipedInputStream() 創建尚未連接的 PipedInputStream。
PipedInputStream(PipedOutputStreamsrc) 創建 PipedInputStream,使其連接到管道輸出流 src。
PipedInputStream(PipedOutputStreamsrc, int pipeSize)
創建一個PipedInputStream,使其連接到管道輸出流 src,並對管道緩衝區使用指定的管道大小。
常用方法
voidconnect(PipedOutputStream src) 使此管道輸入流連接到管道輸出流 src。
int read() 讀取此管道輸入流中的下一個數據字節。
int read(byte[] b,int off, int len) 將最多 len 個數據字節從此管道輸入流讀入 byte 數組。
protected void receive(int b) 接收數據字節。
**************************************************************************
ByteArrayInputStream:源:內存 在構造的時候,需要接收數據源,而且數據源是內存中的一個字節數組
ByteArrayOutputStream:目的:內存在構造的時候不要定義數據目的,因爲該對象中已經內部封裝了可變長度的字節數組,這就是數據目的地。
這兩個流對象不涉及底層資源調用,操作的都是內存中數組,並沒有使用系統資源,所以不需要close關閉。
ByteArrayOutputStream 主要方法
voidwriteTo(OutputStream out)
將此 byte 數組輸出流的全部內容寫入到指定的輸出流參數中。
String toString()
使用平臺默認的字符集,通過解碼字節將緩衝區內容轉換爲字符串。
int size() 返回緩衝區的當前大小。
直接操作字節數組就可以了,爲什麼還要把數組封裝到流對象中呢?因爲數組本身沒有方法,只有一個length屬性。爲了便於數組的操作,將數組進行封裝,對外提供方法操作數組中的元素。
對於數組元素操作無非兩種操作:設置(寫)和獲取(讀),而這兩操作正好對應流的讀寫操作。這兩個對象就是使用了流的讀寫思想來操作數組。
//創建源:
ByteArrayInputStreambis = new ByteArrayInputStream("abcdef".getBytes());
//創建目的:
ByteArrayOutputStreambos = new ByteArrayOutputStream();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++編碼表
常見的編碼表
ASCII:美國標準信息交換碼。
用一個字節的7位可以表示。
ISO8859-1:拉丁碼錶。歐洲碼錶
用一個字節的8位表示。
GB2312:中國的中文編碼表6000多字。兩字節表示而且兩字節的高位都是1兼容ASCII
GBK:中國的中文編碼表升級,融合了更多的中文文字符號,一中文兩字節。兩萬多
Unicode:國際標準碼,融合了多種文字。
所有文字都用兩個字節來表示,Java語言使用的就是unicode
UTF-8:最多用三個字節來表示一個字符。
在程序中所有的數據都是以流的方式進行傳輸或保存的,程序需要數據的時候要使用輸入流讀取數據,而當程序需要將一些數據保存起來的時候,就要使用輸出流完成。
程序中的輸入輸出都是以流的形式保存的,流中保存的實際上全都是字節文件。
你好 utf-8 >>> gbk 浣犲ソ
gbk >>> utf-8 ???
可以用來判斷編碼是utf-8還是gbk
String s = "錢學森";
byte[]b = s.getBytes();
for(int i = 0; i < b.length; i++) {
System.out.print(b[i]+",");
輸出:-57,-82,-47,-89,-55,-83,
中文用兩字節表示而且兩字節的高位都是1即都是負數
字符流能不能用於對圖片的操作,是不可以的。
原因 用字符流賦值圖片的話 有可能無法打開 因爲 字符流按默認編碼表讀取數據字符流讀兩字節數據(單字符)就會去查編碼表返回中文字符 如果能查到編碼表
對應的中文字符 那麼讀取的兩字節數據不會變(編碼不變) 如果查不到 那麼就會去未知數據區域查編碼會改變所讀取的編碼 那麼數據就可能會產生變化
字節流 + 字符編碼 = 字符流
字符流處理的單元爲2個字節的Unicode字符,分別操作字符、字符數組或字符串,而字節流處理單元爲1個字節,操作字節和字節數組。所以字符流是由Java虛擬機將字節轉化爲2個字節的Unicode字符爲單位的字符而成的,所以它對多國語言支持性比較好!如果是音頻文件、圖片、歌曲,就用字節流好點,如果是關係到中文(文本)的,用字符流好點
所有文件的儲存是都是字節(byte)的儲存,在磁盤上保留的並不是文件的字符而是先把字符編碼成字節,再儲存這些字節到磁盤。在讀取文件(特別是文本文件)時,也是一個字節一個字節地讀取以形成字節序列
//爲什麼你好 gbk -> 編碼 -> iso8859-1 ->解碼 =亂碼 -> iso8859-1 -> 編碼 -> gbk ->解碼 =你好
// 你好 utf-8 -> 編碼 -> iso8859-1 ->解碼 =亂碼 -> iso8859-1 -> 編碼 -> utf-8 ->解碼 =你好
// 你好 utf-8 -> 編碼 -> gbk ->解碼 =亂碼 -> gbk -> 編碼 -> utf-8 ->解碼 =你好
//不可以?? 你好 gbk -> 編碼 -> utf-8 ->解碼 =亂碼 -> utf-8 -> 編碼 -> gbk ->解碼 = 亂碼
原數據 編碼存入文件(文件中的是編碼後數據) 讀取通過對照同一編碼表 讀取 還原數據
原數據 編碼存入文件(文件中的是編碼後數據) 讀取通過對照另一個編碼表 讀取 當遇到的編碼字節數據 找不到對應的符號 那麼就會 拿着編碼 去 未知字符區查找 字符 得出二進制數據 原數據改變
原二進制數據 通過編碼表轉碼 的到編碼數據 gbk兩字節二進制數據 轉碼爲 兩字節編碼數據 utf-8 三字節原二進制數據 通過utf-8編碼表 轉碼爲三字節編碼數據
在 Java 中,“字符串”與“字節串”之間可以方便地按照指定編碼規則進行轉化:
byte[] b = "中文123".getBytes("GB2312");// 從字符串按照 GB2312 得到字節串
System.out.println(b.length); // 得到長度爲7個 (D6,D0,CE,C4,31,32,33)
Stringstr = newString(b,"GB2312");// 從字節串按照 GB2312 得到字符串
System.out.println("中文123".length()); // 得到長度爲5,因爲是5個字符
在 java.io.* 包裏面有很多類,其中,以“Stream”結尾的類都是用來操作“字節串”的類,以“Reader”,“Writer”結尾的類都是用來操作“字符串”的類。任何一個文件保存到硬盤上的時候,都是以字節爲單位保存的,當要把“字符串”保存到硬盤上的文本文件,必然要選擇一種編碼。
有兩種方法來指定編碼:
Stringstr = "中文123";
// 第一種辦法:先用指定編碼轉化成字節串,然後用 Stream 類寫入
OutputStream os =newFileOutputStream("1.txt");
byte[] b = str.getBytes("utf-8");
os.write(b);
os.close();
// 第二種辦法:構造指定編碼的 Writer 來寫入字符串
Writer ow=newOutputStreamWriter(newFileOutputStream("2.txt"),"utf-8");
ow.write(str);
ow.close();
// 最後得到的 1.txt 和 2.txt 都是 9 個字節。(漢字在 utf-8編碼規則中佔3字節