Java IO 總結(jié)
概述
IO 即輸入輸出輸出系統(tǒng),常見(jiàn)的操作系統(tǒng),需要面對(duì)的介質(zhì)非常之多,常見(jiàn)的IO介質(zhì)有:
- 文件
- 網(wǎng)絡(luò)
- 內(nèi)存緩存
- 標(biāo)準(zhǔn)輸入輸出
- 線程通信
流
JDK設(shè)計(jì)了一系列接口和類(lèi),使面向不同的介質(zhì)的IO操作都可以通過(guò)類(lèi)似的接口來(lái)實(shí)現(xiàn),這類(lèi)接口都源自同一個(gè)抽象概念**流
流的分類(lèi)
按照數(shù)據(jù)的流向可分為:
- 輸入流
- 輸出流
注意:輸入流輸出流是一個(gè)相對(duì)的概念,一般的從程序角度來(lái)定義:如果數(shù)據(jù)從程序流向設(shè)備則是輸出,反之則是輸入。
按照流的處理方式又可分為:
- 字節(jié)流
- 字符流
綜合來(lái)講流,可以按照如下分類(lèi)
| 字節(jié)流 | 字符流 | |
|---|---|---|
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writter |
JDK類(lèi)庫(kù)
JDK對(duì)流的設(shè)計(jì)采用了一種名為裝飾器的設(shè)計(jì)模式。以輸入流為例, 常見(jiàn)的輸出流類(lèi)的繼承結(jié)構(gòu)如下:

圖中 ByteArrayInputStream,StringBufferInputStream,FileInputStream,PipedInputStream,StringBufferInputStream 等類(lèi)直用于從不同介質(zhì)讀取數(shù)據(jù),被稱(chēng)為原始流。而 FilterInputStream 以及他的子類(lèi)都有一個(gè)輸入?yún)?shù)為InputStream的構(gòu)造函數(shù),用于包裝原始流以增加跟多的功能,這些類(lèi)被稱(chēng)為包裝類(lèi)。
對(duì)于 OutputStream,Reader,Writer JDK也采用了類(lèi)似的設(shè)計(jì),這里不再贅述.
常用的字符流和字節(jié)流,原始流和包裝類(lèi)整理如下:
| Stream | Original Stream | Wrapper |
|---|---|---|
| InputStream | ByteArrayInputStream StringBufferInputStream FileInputStream PipedInputStream |
FilterInputStream PushbackInputStream LineNumberInputStream BufferedInputStream DataInputStream |
| OutputStream | FileOutputStream PipedOutputStream ByteArrayOutputStream |
FileOutputStream BufferedOutputStream DataOutputStream |
| Reader | InputStreamReader StringReader CharArrayReader PipedReader |
FilterReader PushbackReader BufferedReader LineNumberReader |
| Writter | InputStreamWritter StringWritter CharArrayWritter PipedWritter |
FilterWritter PushbackWritter BufferedWritter LineNumberWritter |
經(jīng)常使用的類(lèi)
文件IO
private static final String FILE_NAME = "D:\\test.txt";
private static final String NONSENS_CONTENT = "Once opon a time ...";
private static void writeFile() {
File file = new File(FILE_NAME);
FileOutputStream fos = null;
try {
if (!file.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(file);
fos.write(NONSENS_CONTENT.getBytes());
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
}
}
private static void readFile() {
File file = new File(FILE_NAME);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
int readCount = fis.read(bytes);
String content = new String(bytes);
System.out.print("readFile count:" + readCount + " content:" + content);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
輸出結(jié)果:
readFile count:20 content:Once opon a time ...
為文件讀寫(xiě)增加緩存
將上面讀寫(xiě)的代碼修改為寫(xiě)入10000000次,并一次讀取64個(gè)字節(jié),觀察下寫(xiě)入和讀取的時(shí)間:
- Write
long start = System.currentTimeMillis();
byte bytes[] = NONSENS_CONTENT.getBytes();
fos = new FileOutputStream(file);
for (int i = 0; i < 10000000; i++) {
fos.write(bytes);
}
fos.flush();
long duration = System.currentTimeMillis() - start;
System.out.println("Write 10000000 strings to a file cost : " + duration);
- Read
fis = new FileInputStream(file);
byte[] bytes = new byte[50];
long start = System.currentTimeMillis();
while (fis.available() > 0) {
fis.read(bytes);
}
long duration = System.currentTimeMillis() - start;
System.out.println("Read all content string cost : " + duration);
輸出結(jié)果如下:
Write 10000000 strings to a file cost : 131039
Read all content string cost : 49534
對(duì)上述代碼再做修改,使用 BufferedInputStream 和 BufferedOutputStream 來(lái)包裝 FileOutputStream 和 FileOutputStream 代碼如下:
- Write
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, 1024);
- Read
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis, 1024);
輸出結(jié)果如下:
Write 10000000 strings to a file cost : 3552
Read all content string cost : 11093
從測(cè)試結(jié)果看,不論是讀寫(xiě),速度都大大提升。
內(nèi)存IO
介質(zhì)為內(nèi)存的IO常有的流有:StringBufferInputStream,ByteArrayInputStream,PipedInputStream 等,前幾種比較簡(jiǎn)單,本章主要介紹下 PipedOutputStream 和 PipedInputStream。PipedInputStream 和 PipedOutputStream需要成對(duì)出現(xiàn),用于一個(gè)線程向另外一個(gè)線程寫(xiě)數(shù)據(jù),下面是一個(gè)例子:
private static class WriteThread extends Thread {
PipedOutputStream mOut;
public WriteThread(PipedOutputStream pos) {
super();
mOut = pos;
}
public void run() {
try {
mOut.write("Hello World".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class ReadThread extends Thread {
PipedInputStream mIn;
ReadThread(PipedInputStream pis) {
super();
mIn = pis;
}
public void run() {
try {
byte[] bytes = new byte[1024];
int readCount = mIn.read(bytes);
System.out.println("ReadCount : " + readCount + " content "
+ new String(bytes, 0, readCount));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
WriteThread writeThread = new WriteThread(pos);
ReadThread readThread = new ReadThread(pis);
writeThread.start();
readThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
輸出結(jié)果為:
ReadCount : 11 content Hello World
可以注意到,PipedOutputStream創(chuàng)建時(shí)構(gòu)造函數(shù)傳入了一個(gè)PipedInputStream,在實(shí)際使用的過(guò)程中,也可以先創(chuàng)建PipedOutputStream 在將其作為參數(shù)用于創(chuàng)建PipedInputStream。
網(wǎng)絡(luò)IO
網(wǎng)絡(luò)IO一般是通過(guò) Socket 和 HTTP 相關(guān)的API進(jìn)行,流在這個(gè)過(guò)程中通常是直接通過(guò)相關(guān)對(duì)象獲得,以Socket為例:
public static void main(String[] args) {
Socket socket = new Socket();
SocketAddress addr = new InetSocketAddress("pop3.163.com", 110);
OutputStream out = null;
InputStream in = null;
try {
socket.connect(addr);
in = socket.getInputStream();
String reply = readLine(in);
System.out.println("reply is " + reply);
out = socket.getOutputStream();
writeLine(out, "CAPA");
reply = readLine(in);
System.out.println("reply2 is " + reply);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String readLine(InputStream in) throws IOException {
int d;
StringBuilder sb = new StringBuilder();
while ((d = in.read()) != -1) {
if (d == '\r') {
continue;
} else if (d == '\n') {
break;
} else {
sb.append((char) d);
}
}
return sb.toString();
}
private static void writeLine(OutputStream out, String content) throws IOException {
if (content != null && content.length() > 0) {
out.write(content.getBytes());
out.write('\r');
out.write('\n');
}
}
上面的代碼鏈接了163的POP3服務(wù)器,并讀取兩行數(shù)據(jù),寫(xiě)入一行數(shù)據(jù),最終輸出如下:
reply is +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s])
reply2 is +OK Capability list follows