Java IO<3>處理流:緩衝流 資料流 轉換流 物件流
Java io 處理流
節點流和處理流概述
Java流可以分節點流和處理流兩類。
節點流是面向各種物理節點的流,比如面向讀寫檔案的FileInputStream和FileOutputStream;面向物件的ObjectInputStream和ObjectOutputStream等等。
處理流則需要依附於節點流,用來對節點流的功能進行拓展和加強。比如BufferedInputStream,用它來包裝FileInputStream(或者其他的節點輸入流也一樣)以後 ,直接呼叫BufferedInputStream的read方法,這個read方法的效果和FileInputStream的read方法的效果相比,就多出來一個快取的功能。
java緩衝流本身不具IO功能,只是在別的流上加上緩衝提高效率,像是為別的流裝上一種包裝。當對檔案或其他目標頻繁讀寫或操作效率低,效能差。這時使用緩衝流能夠更高效的讀寫資訊。因為緩衝流先將資料快取起來,然後一起寫入或讀取出來。所以說,緩衝流還是很重要的,在IO操作時記得加上緩衝流提升效能。
<1>處理流之緩衝流
緩衝流分為位元組和字元緩衝流
位元組緩衝流為:
BufferedInputStream—位元組輸入緩衝流
BufferedOutputStream—位元組輸出緩衝流
字元緩衝流為:
BufferedReader—字元輸入緩衝流
BufferedWriter—字元輸出緩衝流
BufferedWriter
public static void main(String[] args) throws IOException { String filePath = "d:" + File.separator + "test.txt"; File file = new File(filePath); FileWriter writer = new FileWriter(file); BufferedWriter bufferedWriter = new BufferedWriter(writer); bufferedWriter.write("c++"); bufferedWriter.newLine(); bufferedWriter.write("java"); bufferedWriter.newLine(); bufferedWriter.write(98); bufferedWriter.flush(); bufferedWriter.newLine(); char[] chars = "測試程式碼".toCharArray(); bufferedWriter.write(chars, 1, chars.length - 1); bufferedWriter.close(); writer.close(); /* c++ java b 試程式碼*/ }
BufferedReader
public static void main(String[] args) throws IOException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
FileReader reader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(reader);
int len;
char[] chars = new char[1024];
String res = "";
while ((len = bufferedReader.read(chars)) != -1) {
res += new String(chars, 0, len);
}
System.out.println(res);
bufferedReader.close();
reader.close();
}
FileInputStream
public static void main(String[] args) throws IOException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int len;
byte[] bytes = new byte[2];
String res = "";
while ((len = bufferedInputStream.read(bytes)) != -1) {
res += new String(bytes, 0, len);
}
System.out.println(res);
bufferedInputStream.close();
fileInputStream.close();
}
FileOutputStream
public static void main(String[] args) throws IOException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write(97);
//換行
bufferedOutputStream.write(10);
byte[] bytes = "hello world".getBytes();
bufferedOutputStream.write(bytes, 0, bytes.length - 1);
bufferedOutputStream.close();
fileOutputStream.close();
//輸出
//a
//hello worl
}
<2>處理流之轉換流
轉換流提供了在位元組流和字元流之間的轉換
Java API提供了兩個轉換流:
- InputStreamReader :將InputStream 轉換為Reader
- OutputStreamWriter :將Writer 轉換為OutputStream
位元組流中的資料都是字元時,轉成字元流操作更高效。很多時候我們使用轉換流來處理檔案亂碼問題。實現編碼和解碼的功能。
InputStreamReader
實現將位元組的輸入流按指定字符集轉換為字元的輸入流。
構造器
- public InputStreamReader(InputStream in)
- public InputSreamReader(InputStream in,String charsetName)
如: Reader isr = new InputStreamReader(System.in,”gbk”);
OutputStreamWriter
實現將字元的輸出流按指定字符集轉換為位元組的輸出流。
構造器
- public OutputStreamWriter(OutputStream out)
- public OutputSreamWriter(OutputStream out,String charsetName)
public void testMyInput() throws Exception {
FileInputStream fis = new FileInputStream("dbcp.txt");
FileOutputStream fos = new FileOutputStream("dbcp5.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedReader br = new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(osw);
String str = null;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
常見的編碼表
- ASCII:美國標準資訊交換碼。用一個位元組的7位可以表示。
- ISO8859-1:拉丁碼錶。歐洲碼錶,用一個位元組的8位表示。
- GB2312:中國的中文編碼表。最多兩個位元組編碼所有字元。
- GBK:中國的中文編碼表升級,融合了更多的中文文字元號。最多兩個位元組編碼。
- Unicode:國際標準碼,融合了目前人類使用的所有字元。為每個字元分配唯一的字元碼。所有的文字都用兩個位元組來表示。
- UTF-8:變長的編碼方式,可用1-4個位元組來表示一個字元。
<3>處理流之資料流
DataOutputStream資料輸出流允許應用程式將基本Java資料型別寫到基礎輸出流中,而DataInputStream資料輸入流允許應用程式以機器無關的方式從底層輸入流中讀取基本的Java型別.
DataInputStream
DataInputStream中的方法
public final int read(byte b[]) throws IOException {}
public final int read(byte b[], int off, int len) throws IOException {}
public final void readFully(byte b[]) throws IOException {}
public final void readFully(byte b[], int off, int len) throws IOException {}
public final int skipBytes(int n) throws IOException {}
public final boolean readBoolean() throws IOException {}
public final byte readByte() throws IOException {}
public final int readUnsignedByte() throws IOException {}
public final short readShort() throws IOException {}
public final int readUnsignedShort() throws IOException {}
public final char readChar() throws IOException {}
public final int readInt() throws IOException {}
public final long readLong() throws IOException {}
public final float readFloat() throws IOException {}
public final double readDouble() throws IOException {}
public final String readUTF() throws IOException {}
使用示例:
public static void main(String[] args) throws IOException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(inputStream);
char c = dataInputStream.readChar();
String s = dataInputStream.readUTF();
float v = dataInputStream.readFloat();
System.out.println(c);
System.out.println(s);
System.out.println(v);
}
DataOutputStream
DataOutputStream中的方法:
private void incCount(int value) {}
public synchronized void write(int b) throws IOException {}
public synchronized void write(byte b[], int off, int len) throws IOException {}
public void flush() throws IOException {}
public final void writeBoolean(boolean v) throws IOException {}
public final void writeByte(int v) throws IOException {}
public final void writeShort(int v) throws IOException {}
public final void writeChar(int v) throws IOException {}
public final void writeInt(int v) throws IOException {}
public final void writeLong(long v) throws IOException {}
public final void writeFloat(float v) throws IOException {}
public final void writeDouble(double v) throws IOException {}
public final void writeBytes(String s) throws IOException {}
public final void writeChars(String s) throws IOException {}
public final void writeUTF(String str) throws IOException {}
public final int size() {}
使用示例:
public static void main(String[] args) throws IOException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
FileOutputStream outputStream = new FileOutputStream(file);
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF("hello");
dataOutputStream.write(97);
dataOutputStream.writeChars("\n");
dataOutputStream.writeBoolean(true);
dataOutputStream.writeChar(10);
dataOutputStream.writeDouble(7.62);
dataOutputStream.flush();
dataOutputStream.close();
outputStream.close();
}
<4>處理流之物件流
ObjectInputStream 和OjbectOutputSteam用於儲存和讀取基本資料型別資料或 物件的處理流。它的強大之處就是可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。
- 序列化:用ObjectOutputStream類 儲存基本型別資料或物件的機制
- 反序列化:用ObjectInputStream類 讀取基本型別資料或物件的機制
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變數
序列化
物件序列化機制允許把記憶體中的Java物件轉換成平臺無關的二進位制流,從
而允許把這種二進位制流持久地儲存在磁碟上,或通過網路將這種二進位制流傳
輸到另一個網路節點。當其它程式獲取了這種二進位制流,就可以恢復成原
來的Java物件。
序列化的好處在於可將任何實現了Serializable介面的物件轉化為 位元組資料,
使其在儲存和傳輸時可被還原。序列化是 RMI(Remote Method Invoke – 遠端方法呼叫)過程的引數和返回值都必須實現的機制,而 RMI 是 JavaEE 的基礎。因此序列化機制是JavaEE 平臺的基礎。如果需要讓某個物件支援序列化機制,則必須讓物件所屬的類及其屬性是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一。否則,會丟擲NotSerializableException異常。
- Serializable
- Externalizable
實現了Serializable 介面的物件,可將它們轉換成一系列位元組,並可在以後
完全恢復回原來的樣子。 這一過程亦可通過網路進行。這意味著序列化機
制能自動補償作業系統間的差異。在 換句話說,可以先在Windows 機器上創
臺 建一個物件,對其序列化,然後通過網路發給一臺Unix 機器,然後在那裡
準確無誤地重新“裝配”。不必關心資料在不同機器上如何表示,也不必
關心位元組的順序或者其他任何細節。
凡是實現Serializable介面的類都有一個表示序列化版本識別符號的靜態變數:
private static final long serialVersionUID;
serialVersionUID用來表明類的不同版本間的相容性。 簡言之,其目的是以序列化物件進行版本控制,有關各版本反序列化時是否相容。
如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自動生成的。若類的例項變數做了修改,serialVersionUID 可能發生變化。故建議,顯式宣告。
簡單來說,Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗
證版本一致性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同
就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異
常。(InvalidCastException)
使用物件流序列化物件
若某個類實現了 Serializable 介面,該類的物件就是可序列化的:
建立一個 ObjectOutputStream, 呼叫 ObjectOutputStream 物件的 writeObject( 物件) 方法輸出可 序列化物件,注意寫出一次,操作flush() 一次。
反序列化,建立一個 ObjectInputStream, 呼叫 readObject() 方法讀取流中的物件。
如果某個類的屬性不是基本資料型別或 String 型別,而是另一個
引用型別,那麼這個引用型別必須是可序列化的,否則擁有該型別的
Field 的類也不能序列化。
示例:
public static void main(String[] args) throws IOException, ClassNotFoundException {
String filePath = "d:" + File.separator + "test.txt";
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
Person lilei = new Person("李雷", 25, 1.78, true, 58);
objectOutputStream.writeObject(lilei);
objectOutputStream.flush();
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Person p = (Person) objectInputStream.readObject();
System.out.println(p);
objectInputStream.close();
//Person{name='李雷', age=25, height=1.78, isMale=true, weight=58.0}
}
Person類:
import java.io.Serializable;
public class Person implements Serializable {
public String name;
public int age;
public double height;
public boolean isMale;
public double weight;
public Person(String name, int age, double height, boolean isMale, double weight) {
this.name = name;
this.age = age;
this.height = height;
this.isMale = isMale;
this.weight = weight;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", isMale=" + isMale +
", weight=" + weight +
'}';
}
}
<5>處理流之標準輸入輸出流
System.in和System.out分別代表了系統標準的輸入和輸出裝置
預設輸入裝置是:鍵盤,輸出裝置是:顯示器
System.in的型別是InputStream
System.out的型別是PrintStream,其是OutputStream的子類
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("請輸入資訊(退出輸入e或exit):");
// 把"標準"輸入流(鍵盤輸入)這個位元組流包裝成字元流,再包裝成緩衝流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 讀取使用者輸入的一行資料 --> 阻塞程式
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
System.out.println("-->:" + s.toUpperCase());
System.out.println("繼續輸入資訊");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 關閉過濾流時,會自動關閉它包裝的底層節點流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}