08 - JavaSE之IO流
IO流
JAVA流式輸入輸出原理:可以想象成一根管道懟到文件上,另一端是我們程序,然後流的輸入輸出都是按照程序本身作為第一人稱說明的。比如 input,對於我們程序來說就是有數據輸入我們程序,output就是我們程序輸出數據到文件等。對象不能搞錯了,否則就南轅北轍了。
- 通過不同的角度對流的輸入輸出功能進行分類:
- 按數據流的方向分為:輸入流和輸出流
- 按處理數據單位不同分為:字節流和字符流(2個字節)
- 按功能不同分為:節點流和處理流
輸入流和輸出流
JAVA JDK 所提供的所有流類型位於包 java.io 內部,分別繼承自下面四個抽象流類型:
字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer
節點流和處理流
- 節點流為可以從一個特定的數據源(節點)讀寫數據(如:文件,內存)。
節點流可以簡單的理解為:一根管道直接懟到文件上,進行數據的讀寫。
- 處理流是連接在已存在的節點流或處理流上的,通過讀數據進行處理(過濾等)為程序提供更加強大的讀寫功能。
處理流可以簡單的理解為:套在節點流管道的管子,可以對流過節點流的數據進行處理,過濾等操作。
InputStream 抽象類
- 繼承自 InputStream的流都是用於向程序中輸入數據,且數據的單位為字節。
- 繼承自 InputStream的類有如下等:(加粗為節點流,未加粗為處理流)
FileInputStream
PipedInputStream
FilterInputStream
ByteArrayInputStream
SequenceInputStream
StringBufferInputStream
ObjectInputStream
InputStream的基本方法
int read() throws IOException
int read(byte[] buffer) throws IOException
int read(byte[] buffer, int offset, int length) throws IOException
void close() throws IOException
OutputStream 抽象類
- 繼承自 OutputStream的流都是用於從程序中輸出數據,且數據的單位為字節。
- 繼承自 OutputStream的類有如下等:(加粗為節點流,未加粗為處理流)
FileOutputStream
PipedOutputStream
FilterOutputStream
ByteArrayOutputStream
ObjectOutputStream
OutputStream 基本方法
void write(int b) throws IOException
void write(byte[] b) throws IOException
void write(byte[] b, int off, int len) throws IOException
void close() throws IOException
void flush() throws IOException // 在關閉輸出流之前使用,將輸出緩沖區的數據寫到目的地
Reader
- 繼承自 Reader 的流都是用於程序從外部讀入數據,且數據的單位是字符(2個字節)。
- 如下為繼承自Reader的流。(加粗為節點流,未加粗為處理流)
BufferedReader
CharArrayReader
InputStreamReader
FilterReader
PipedReader
StringReader
- Reader的基本方法 - 略
Writer
- 繼承自 Writer的流都是用於程序向外部寫入數據,且數據的單位是字符(2個字節)。
- 如下為繼承自Writer的流。(加粗為節點流,未加粗為處理流)
BufferedWriter
CharArrayWriter
OutputStreamWriter
FilterWriter
PipedWriter
StringWriter
- Writer的基本方法 - 略
節點流類型
類型 字節流 字符流
File(文件) FileInputStream / FileOutputStream FileReader / FileWriter
Memory Array ByteArrayInputStream / ByteArrayOutputStream CharArrayReader / CharArrayWriter
Memory String -- StringReader / StringWriter
Pipe(管道) PipedInputStream / PipedOutputStream PipedReader / PipedWriter
舉例1:FileInputStream
import java.io.*;
public class Test {
public static void main(String[] args) {
int b = 0;
long num = 0;
FileInputStream ln = null;
try {
ln = new FileInputStream("I:/Java/Demo/test.txt");
} catch (FileNotFoundException e) {
System.out.println("文件不存在!");
System.exit(-1);
}
try {
while ((b=ln.read()) != -1) {
System.out.print((char)b);
num++;
}
ln.close();
System.out.println();
System.out.println("共讀出:" + num + "個字節。");
} catch (IOException e) {
System.out.println("文件讀取失敗!");
System.exit(-1);
}
}
}
舉例2:FileOutputStream
import java.io.*;
public class Test {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("I:/Java/Demo/test.txt");
out = new FileOutputStream("I:/Java/Demo/newtest.txt");
while((b=in.read()) != -1) {
out.write(b);
}
} catch (FileNotFoundException e) {
System.out.println("文件不存在!");
System.exit(-1);
} catch (IOException e) {
System.out.println("文件復制失敗!");
System.exit(-2);
}
System.out.println("文件復制成功!");
}
}
處理流類型
處理類型 字節流 字符流
Buffering BufferedInputStream/BufferedOutputStream BufferedReader/BufferedWriter
Filtering FilterInputStream/FilterOutputStream FilterReader/FilterWriter
Converting between bytes and character -- InputStreamReader/OutputStreamWriter
Object Serialization ObjectInputStream/ObjectOutputStream --
Data conversion DataInputStream/DataOutputStream --
Counting LineNumberInputStream LineNumberReader
Peeking ahead PushbackInputStream PushbackReader
Printing PrintStream PrintWriter
- 緩沖流(以Buffered開頭)
緩沖流套接在相應的節點流上,對讀寫的數據提供了緩沖的功能,提高了讀寫的效率,同時增加了一些新的方法。
BufferedReader / BufferedWriter / BufferedInputStream / BufferedOutputStream
- 緩沖流支持其父類的 mark 和 reset 方法。
- BufferedReader 提供了 readLine 方法用於讀取一行字符串(以\r或者\n分割)
- BufferedWriter 提供了 newLine 用於寫入一個行分隔符。
對於輸出的緩沖流,寫出的數據會先在內存中緩存,使用 flush 方法將會使內存中的數據立刻寫出。
舉例1:
import java.io.*;
public class Test {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
BufferedInputStream bin = null;
try {
in = new FileInputStream("I:/Java/Demo/test.txt");
bin = new BufferedInputStream(in);
bin.mark(100);
for(int i=0; (i<=10)&&(b=bin.read()) != -1; i++) {
System.out.print((char)b);
}
bin.reset();
System.out.println();
for(int i=0; (i<=10)&&(b=bin.read()) != -1; i++) {
System.out.print((char)b);
}
} catch (FileNotFoundException e) {
System.out.println("文件不存在!");
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
舉例2:
import java.io.*;
public class Test {
public static void main(String[] args) {
String s = null;
try {
BufferedWriter bw = new BufferedWriter(new FileWriter("I:/Java/Demo/test.txt"));
BufferedReader br = new BufferedReader(new FileReader("I:/Java/Demo/test.txt"));
for(int i=0; i<100; i++) {
s = Double.toString(Math.random());
bw.write(s);
bw.newLine();
}
bw.flush();
while((s=br.readLine()) != null) {
System.out.println(s);
}
bw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//0.7365309652822098
//0.577186775602007
//0.11736466966949166
//0.2998096440959087
//0.23859539950503672
//...
- 轉換流
- InputStreamReader 和 OutputStreamWriter 用於字節數據到字符數據之間的轉換。
- InputStreamReader 需要和 InputStream 套接。
- OutputStreamWriter 需要和 OutputStream 套接。
- 轉換流在構造時可以指定其編碼結合,例如: InputStream is = new InputStreamReader(System.in, "ISO8859_1")
舉例1:
import java.io.*;
public class Test {
public static void main(String[] args) {
try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("I:/Java/Demo/test.txt"));
osw.write("1234567890");
System.out.println(osw.getEncoding());
osw.flush();
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("I:/Java/Demo/test.txt", true), "ISO8859_1");
osw.write("abcdefghijklmn");
System.out.println(osw.getEncoding());
osw.flush();
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
舉例2:
import java.io.*;
public class Test {
public static void main(String[] args) {
try {
String s = null;
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
while((s = br.readLine()) != null) {
if(s.equalsIgnoreCase("exit")) {
break;
}
System.out.println(s);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 數據流
- DataInputStream 和 DataOutputStream 分別繼承自 InputStream 和 OutputStream,它屬於處理流,需要分別套接在 InputStream 和 OutputStream 類型的節點流上。
- DataInputStream 和 DataOutputStream 提供了可以存取與機器無關的 Java 原始類型數據(如:int,double 等)的方法。
- DataInputStream 和 DataOutputStream 的構造方法為:
DataInputStream (InputStream in)
DataOutputStream (OutputStream out)
數據流的作用?
比如,我們如何存儲一個很大的數到文件?我們之前的方法是將其轉換成字符串存儲,可不可以直接存儲呢?數據流可以。它提供了很多方法用於存儲基礎數據類型的數據,參看 API 文檔。
舉例1:
import java.io.*;
public class Test {
public static void main(String[] args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 在內存自動生成一個 Byte[] 數組
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeDouble(Math.random());
dos.writeBoolean(true);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
dos.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
問題:我們寫一個 double,寫一個boolean寫到哪裏了呢?
其實,在我們 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 的時候,就在內存中開辟了一塊空間,用來將程序要寫入的數據寫到這片內存中。
還有,在讀數據的時候,由於這片內存的分配是按照隊列的方式分配的,所以先寫進去的數據要先讀出來,如果如上面程序那樣,先讀boolean的話,就會讀取到double 8個字節的第一個字節上,切記!
- Print 流(打印流)
- PrintWriter 和 PrintStream 都屬於輸出流,分別針對與字符和字節。
- PrintWriter 和 PrintStream 提供了重載的 print println 方法用於多種數據類型的輸出。
- PrintWriter 和 PrintStream 的輸出操作不會拋出異常,用戶通過檢測錯誤狀態獲取錯誤信息。
PrintWriter 和 PrintStream 有自動 flush 功能。
PrintWriter(PrintWriter out)
PrintWriter(Writer out, boolean autoFlush)
PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean autoFlush)
PrintStream(OutputStream out)
PrintStream(OutputStream out, boolean autoFlush)
舉例1:
import java.io.*;
public class Test {
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream("I:\\Java\\Demo\\test.txt");
ps = new PrintStream(fos);
if(null != ps) {
System.setOut(ps);
}
for(int i=0; i<60000; i++) {
System.out.print((char)i + " ");
if(i%50 == 0) {
System.out.println();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
將輸出打印語句重定向到 test.txt 文件中。
舉例2:
import java.io.*;
public class Test {
public static void main(String[] args) {
String filename = args[0];
if (filename != null) {
list(filename, System.out);
}
}
public static void list(String f, PrintStream fs) {
try {
String s = null;
BufferedReader br = new BufferedReader(new FileReader(f));
while ((s = br.readLine()) != null) {
fs.println(s);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
將 args[0] 所寫的文件名對應的文件打印到終端。
舉例3:
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
String s = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
FileWriter fw = new FileWriter("I:/Java/Demo/log.txt", true);
PrintWriter log = new PrintWriter(fw);
while((s=br.readLine()) != null) {
if(s.equalsIgnoreCase("exit")) {
break;
}
System.out.println(s);
log.println("------");
log.println(s);
log.flush(); // 可以省略
}
log.println("--- " + new Date() + " ---");
log.flush(); // 可以省略
log.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
將從終端輸入的內容作為 log 寫到 log.txt 文件裏面。
- Object 流
Object 流的作用:當我們存儲一個元素的時候,(比如在畫圖軟件上畫了一個圓)我們的圓有很多屬性,原點位置,半徑大小,顏色,粗細等,既然每一個屬性都需要存儲,而且這些屬性都在一個 Object 對象裏面,那麽我們為什麽不直接寫整個 Object呢?(這叫序列化。)
註意:當你 new 一個 Object 的時候,不單單只有你的屬性,還有一些比如 Object的版本號,this,super 等。
舉例:
import java.io.*;
public class Test {
public static void main(String[] args) {
TmpClass tm = new TmpClass();
tm.a =10;
tm.d = false;
try {
FileOutputStream fos = new FileOutputStream("I:/Java/Demo/text.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(tm);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("I:/Java/Demo/text.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
TmpClass t = (TmpClass) ois.readObject();
System.out.println(t.a + " " + t.b + " " + t.c + " " + t.d);
} catch (ClassNotFoundException c) {
System.out.println("讀取文件失敗");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class TmpClass implements Serializable {
int a = 1;
int b = 2;
double c = 3.0;
boolean d = true;
}
- Serializable 接口
- 可以被序列化的,如果想把一個類的對象,寫到硬盤或者網絡,想把這個對象序列化成一個字節流的話,必須要實現這個接口。
- 這個接口沒有任何方法,是一個空接口,是一個標記性接口,它只是給編譯器看的,編譯器看到後就知道這個類的對象時可以被序列化的,是可以整個寫入文件的。
- tansient關鍵字
使用方法,修飾成員變量。
class TmpClass implements Serializable {
int a = 1;
int b = 2;
tansient double c = 3.0;
boolean d = true;
}transient 表示透明的意思,就是說在一個類的對象序列化的時候不予考慮,就是將這個類的對象寫入文件的時候,不寫這個被transient 修飾的成員變量,那麽我們再讀出來的時候就是默認值(如上,c 讀出來的時候是 0.0)
- externalizable 接口
- 可以理解為個性化的 Serializable 接口,externalizable 接口是繼承 Serializable 接口得到的,相當於自己控制序列化的過程。(了解就好)
總結
- 本章復習方法:把所有的名字,關鍵字寫到一張紙上,每天看看想想自己,給朕看半個時辰。—— 康熙
08 - JavaSE之IO流