在计算机编程中,IO是一项非常重要的内容。I表示Input,O表示Output,即输入/输出。这里所说的输入/输出都是站在应用程序的立场上讲的。输入和输出都必须有源和目标,对于输入,目标就是程序本身,源可能是文件、网络、内存空间、数据库等等。对于输出,上述情况刚好相反。
一 、流的概念
流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
一个流,必有源和目标,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。
流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能写,而不能读。
实际上,流的源和目标可以简单地看成字节的生产者和消费者,对输入流,可不必关心它的源是什么,只要简单地从流中读取数据即可;对于输出流,也不需要知道它的目标,只是简单地往流中写数据即可。
我们可以将IO流形象的比喻为水流,文件和程序属于水流的两个端点,他们之间连接着一条管道,此时,水流就在他们之间形成了,自然也就出现了方向:可以流进,也可以流出。
二 、Java IO流的分类
如果根据IO的流向分类,当然只有两种了:输入流和输出流。
如果依据操作数据的类型分类,可以分为:字节流和字符流。
按照功能划分的话,可以分为:节点流和包装流。
下面截图展示了Java 的四大基本IO流,并且对应着四个抽象类:
三、Java常用IO流
上面介绍了Java中的四大基本IO流,这四大基流在Java中对应四个抽象类,每个抽象类都定义了属于这种流的规范操作。由于是抽象类,因此我们不能实例化任何对象,要进行IO操作,就必须使用上述四大基流的继承类。根据功能和类型,Java中常用IO流的操作类包含以下几种:
上面列举了Java中各种常用的IO操作类。其中,字节流表示IO操作时以字节为基本单位, 一般用于操作二进制的文件或数据,也可以操作纯文本数据。字符流表示以字符为单位进行IO操作,只能用于纯文本文件或数据的操作。文件流表示IO操作的源或目标是文件。数组/内存流表示IO操作的源或目标是内存中的数组对象。文件流和数组/内存流都属于节点流。包装流表示对节点流的二次封装,用于更为特殊的目的,比如缓冲流用于加速节点流的IO操作。转换流用于将字节流转换为字符流。合并流用于将多个字节流合并为一个连续的字节流。
四、常用IO类的使用
上面讲了这么多概念只为了帮助自己和大家理解、记忆。下面结合一个经典案例,讲讲上述这些IO类的使用方法。
案例:文件拷贝(文本文件、二进制文件、多个文本文件合并)。
1. 使用文件流实现拷贝
a. 字节流实现
public static void fileCopyByByte() {
// 1 输入源和输出目标(一般用于操作二进制文件)
File src = new File("srcFolder/something.avi");
File dest = new File("destFolder/something.avi");
try(// java7新增语法
// 2 流对象
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);
) {
// 3 拷贝操作
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
b. 字符流实现
public static void fileCopyByChar() {
// 1 输入源和输出目标(只能操作文本文件)
File src = new File("srcFolder/something.txt");
File dest = new File("destFolder/something.txt");
try(
// 2 流對象
Reader reader = new FileReader(src);
Writer writer = new FileWriter(dest);
) {
// 3 拷贝操作
char[] buffer = new char[1024];
int len = 0;
while ((len = reader.read(buffer)) > 0) {
writer.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.使用缓冲流加速IO操作
a.字节缓冲流实现
public static void fileCopyByByteBuffer() {
// 1 输入源和输出目标(一般用于操作二进制文件)
File src = new File("srcFolder/something.avi");
File dest = new File("destFolder/something.avi");
try (
// 2 流对象
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(dest));
) {
// 3 拷贝操作
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bin.read(buffer)) > 0) {
bout.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
b. 字符缓冲流实现
public static void fileCopyByCharBuffer() {
// 1 输入源和输出目标(只能操作文本文件)
File src = new File("srcFolder/something.txt");
File dest = new File("destFolder/something.txt");
try (
// 2 流对象
BufferedReader br = new BufferedReader(new FileReader(src));
BufferedWriter bw = new BufferedWriter(new FileWriter(dest));
) {
// 3 拷贝操作
char[] buffer = new char[1024];
int len = 0;
while ((len = br.read(buffer)) > 0) {
bw.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
3. 合并流、转换流、缓冲流综合实例:将多个文本文件拷贝到另一个文件
public static void megreIO() {
// 1 输入源和输出目标
File src1 = new File("srcFolder/a.txt");
File src2 = new File("srcFolder/b.txt");
File dest = new File("destFolder/dest.txt");
// 2 流對象
try (
InputStream sis = new SequenceInputStream(
new FileInputStream(src1), new FileInputStream(src2));
BufferedReader reader = new BufferedReader(new InputStreamReader(sis));
BufferedWriter writer = new BufferedWriter(new FileWriter(dest));
) {
// 3 拷貝操作
String str = null;
while ((str = reader.readLine()) != null) { // 字符缓冲流增加了读取一行的操作方法
writer.write(str);
writer.newLine(); // 写入换行
}
} catch (Exception e) {
e.printStackTrace();
}
}
五、安全关闭
上面介绍的几个实例都使用了Java7新增的语法。在Java7之前我们必须使用下面方法安全的关闭资源(包括socket、文件、数据库等):
public static void fileCopyByByte() {
// 1 源和目标
File src = new File("srcFolder/something.avi");
File dest = new File("destFolder/something.avi");
InputStream in = null;
OutputStream out = null;
try {
// 2 流對象
in = new FileInputStream(src);
out= new FileOutputStream(dest);
// 3 拷贝操作
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(in != null) {
in.close();
}
} catch (IOException e2) {
e2.printStackTrace();
}
try {
if(out != null) {
out.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
由于语法过于繁琐,Java7实现了自动关闭资源的方法,在该方法中我们只需要将资源对象的创建代码放在try catch语句的声明列表中即可。但是,只有实现了java.lang.AutoCloseable接口的资源类才能使用自动关闭,一般的文件、数据库连接等均已实现该接口,该接口的实现方法将调用资源对象的close()方法关闭资源。利用自动关闭,上述代码就变得非常简洁了:
public static void fileCopyByByte() {
// 1 源和目标
File src = new File("srcFolder/something.avi");
File dest = new File("destFolder/something.avi");
try (
// 2 流對象
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);
) {
// 3 拷贝操作
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
浙公网安备 33010602011771号