Java IO 总结
概述
IO 即输入输出输出系统,常见的操作系统,需要面对的介质非常之多,常见的IO介质有:
- 文件
- 网络
- 内存缓存
- 标准输入输出
- 线程通信
流
JDK设计了一系列接口和类,使面向不同的介质的IO操作都可以通过类似的接口来实现,这类接口都源自同一个抽象概念**流
流的分类
按照数据的流向可分为:
- 输入流
- 输出流
注意:输入流输出流是一个相对的概念,一般的从程序角度来定义:如果数据从程序流向设备则是输出,反之则是输入。
按照流的处理方式又可分为:
- 字节流
- 字符流
综合来讲流,可以按照如下分类
字节流 | 字符流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writter |
JDK类库
JDK对流的设计采用了一种名为装饰器的设计模式。以输入流为例, 常见的输出流类的继承结构如下:
图中 ByteArrayInputStream,StringBufferInputStream,FileInputStream,PipedInputStream,StringBufferInputStream 等类直用于从不同介质读取数据,被称为原始流。而 FilterInputStream 以及他的子类都有一个输入参数为InputStream的构造函数,用于包装原始流以增加跟多的功能,这些类被称为包装类。
对于 OutputStream,Reader,Writer JDK也采用了类似的设计,这里不再赘述.
常用的字符流和字节流,原始流和包装类整理如下:
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 |
经常使用的类
文件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();
}
}
}
输出结果:
readFile count:20 content:Once opon a time ...
为文件读写增加缓存
将上面读写的代码修改为写入10000000次,并一次读取64个字节,观察下写入和读取的时间:
- 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);
输出结果如下:
Write 10000000 strings to a file cost : 131039
Read all content string cost : 49534
对上述代码再做修改,使用 BufferedInputStream 和 BufferedOutputStream 来包装 FileOutputStream 和 FileOutputStream 代码如下:
- Write
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, 1024);
- Read
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis, 1024);
输出结果如下:
Write 10000000 strings to a file cost : 3552
Read all content string cost : 11093
从测试结果看,不论是读写,速度都大大提升。
内存IO
介质为内存的IO常有的流有:StringBufferInputStream,ByteArrayInputStream,PipedInputStream 等,前几种比较简单,本章主要介绍下 PipedOutputStream 和 PipedInputStream。PipedInputStream 和 PipedOutputStream需要成对出现,用于一个线程向另外一个线程写数据,下面是一个例子:
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();
}
}
输出结果为:
ReadCount : 11 content Hello World
可以注意到,PipedOutputStream创建时构造函数传入了一个PipedInputStream,在实际使用的过程中,也可以先创建PipedOutputStream 在将其作为参数用于创建PipedInputStream。
网络IO
网络IO一般是通过 Socket 和 HTTP 相关的API进行,流在这个过程中通常是直接通过相关对象获得,以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服务器,并读取两行数据,写入一行数据,最终输出如下:
reply is +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s])
reply2 is +OK Capability list follows