IO流
I是指Input(输入),O是指Output(输出)。
在Java中,文件的输入和输出是通过流(Stream)来实现的,流的概念源于Unix中管道(pipe)的概念。在Unix系统中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。对于流而言,我们不用关心数据是如何传输的,只需要向源端输入数据,向目的端获取数据即可。
流按照处理数据的单位,可以分为字节流和字符流;按照流向分为输入流和输出流
(注意:输入流和输出流都是站在程序的角度参照的)
字节流
字节流用于处理以字节为单位的二进制文件(如音乐、图片等)。
输入字节流
InputStream抽象类是所有输入字节流类的直接或间接父类,FileInputStream是其重要子类。
FileInputStream常用构造方法:
- FileInputStream(File file) :通过File对象创建FileInputStream对象。
- FileInputStream(String name) :通过文件(非“目录”)路径创建FileInputStream对象。
FileInputStream常用方法:
- int read():从输入流中读取单个字节的数据;如果已到达文件末尾,则返回 -1。
- int read(byte[] b):从输入流中将最多b.length个字节的数据读入一个byte数组中,以整数形式返回存入数组中的实际字节个数;如果已到达文件末尾,则返回 -1。
- void close():关闭此文件输入流并释放与此流有关的所有系统资源。
输出字节流
OutputStream抽象类是所有输出字节流类的直接或间接父类,FileOutputStream是其重要子类。
FileOutputStream常用构造方法:
- FileOutputStream(File file) :通过File对象创建FileInputStream对象。
- FileOutputStream(String name) :通过文件(非“目录”)路径创建FileOutputStream对象。
- FileOutputStream(File file, boolean append):通过File对象创建FileOutputStream对象;第二个参数如果为true ,则字节将被写入文件的末尾而不是开头。
FileOutputStream常用方法:
- void write(int b):将指定的单个字节数据写入此文件输出流。
- void write(byte[] b, int off, int len):将byte数组中从off开始的len个字节写入此文件输出流。
- void flush():刷新字节输出流并强制写出缓冲内所有字节数据。
- void close():关闭此文件输出流并释放与此流有关的所有系统资源。
我们使用输入字节流和输出字节流完成一次文件复制操作:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
public class Test {
public static void main(String[] args) {
FileInputStream input = null;
FileOutputStream output = null;
try {
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
input = new FileInputStream("E:\\西海情歌.mp3");
output = new FileOutputStream("E:\\1.mp3");
int data=0;
while((data=input.read())!=-1) {
output.write(data);
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(input!=null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
根据文件创建时间以及文件大小看出文件复制成功,但是此种方法时效太慢,仅仅复制一个大小为4M左右的文件就操作了20s,所以我们有下面方法:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
public class Test {
public static void main(String[] args) {
FileInputStream input = null;
FileOutputStream output = null;
try {
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
input = new FileInputStream("E:\\西海情歌.mp3");
output = new FileOutputStream("E:\\1.mp3");
int vaildLength=0;//记录一次传输的字节数
byte [] car = new byte[1024];//设置一次传输的最大字节数
while((vaildLength=input.read(car))!=-1) {//input.read(car)装车
output.write(car, 0, vaildLength);//一次传输,卸车
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
} catch (Exception e) {
e.printStackTrace();
}finally {//释放资源
try {
if(input!=null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果:
文件复制瞬间完成,操作效率大大提高。
注意: FileOutputStream(File file) 、 FileOutputStream(String name) 或FileOutputStream(File file, false)创建FileOutputStream对象时会创建一个新的空文件;如果使用FileOutputStream(File file, true)创建FileOutputStream对象,则只在第一次执行时创建一个新的空文件。
输入字符流
Java虚拟机将字节转化为2个字节的Unicode字符就形成了字符流,字符流用于处理以Unicode字符为单位的文本文件(如记事本文件等)。Reader抽象类是所有输入字符流类的直接或间接父类, FileReader是其重要子类。
FileReader常用构造方法:
- FileReader(File file) :通过File对象创建FileReader对象。
- FileReader(String fileName) :通过文件(非“目录”)路径创建FileReader对象。
FileReader常用方法:
- int read():从输入流中读取单个字符的数据,如果已到达流的末尾,则返回 -1 。
- int read(char[] cbuf):从输入流中将最多cbuf.length个字符的数据读入一个char数组中,以整数形式返回存入数组中的实际字节个数,如果已到达流的末尾,则返回 -1 。
- void close():关闭此文件输入流并释放与此流有关的所有系统资源。
输出字符流
Writer抽象类是所有输出字符流类的直接或间接父类,FileWriter是其重要子类:
FileWriter常用构造方法:
- FileWriter(File file) :通过File对象创建FileWriter对象。
- FileWriter(String fileName) :通过文件(非“目录”)路径创建FileWriter对象。
- FileWriter(File file, boolean append):通过File对象创建FileWriter对象;第二个参数如果为true ,则字节将被写入文件的末尾而不是开头。
FileWriter常用方法:
- void write(int c):将指定的单个字符数据写入此文件输出流
- void write(char[] cbuf, int off, int len):将char数组中从off开始的len个字符写入此文件输出流。
- void flush():刷新字符输出流并强制写出缓冲内所有字符数据。
- void close():关闭此文件输出流并释放与此流有关的所有系统资源。
我们在此使用输入字符流和输出字符流中常用构造方法以及常用方法来实现一次字符文件复制。
public class Test {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
try {
reader = new FileReader("E:\\table.html");
writer = new FileWriter("E:\\2.txt");
int data = 0;
while((data=reader.read())!=-1) {
writer.write(data);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
}
}
执行结果:
本次测试使用的是大小为1M的文件,结果文件复制操作进行了4s,耗时过长,因为在一次输入输出中只传输了一个字符,所以我们想到一次传输多个字符可以加快传输效率。
public class Test {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
try {
reader = new FileReader("E:\\table.html");
writer = new FileWriter("E:\\2.txt");
int vaildLength = 0;
char [] car = new char[1024];
while((vaildLength=reader.read(car))!=-1) {
writer.write(car, 0, vaildLength);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
}
}
执行结果:
耗时3s,(⊙﹏⊙)好像没太大提升。
转换流
在介绍转换流之前我们先观察一个现象:
public class Test {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
try {
reader = new FileReader("E:\\java.txt");
writer = new FileWriter("E:\\1.txt");
int data = 0;
while((data=reader.read())!=-1) {
writer.write(data);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
}
}
上面是使用字符流实现简单的文件复制,java.txt内容为java代码,但是其中有中文注释,操作完成后我们点开复制文件:
发现中文乱码这是因为文件自身编码方式和程序运行时使用的默认编码方式不一致,致使程序读取或输出字符文件时可能会出现乱码,这时可以使用字节流操作文件,然后再将字节流转换成字符流,这一转换过程可以借助转换流实现。
public class Test {
public static void main(String[] args) {
InputStreamReader reader = null;
OutputStreamWriter writer = null;
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
try {
reader = new InputStreamReader(new FileInputStream("E:\\java.txt"), "gbk");
writer = new OutputStreamWriter(new FileOutputStream("E:\\1.txt"),"gbk");
int data = 0;
while((data=reader.read())!=-1) {
writer.write(data);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(writer!=null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
}
}
再次执行,查看复制文件:
中文成功显示。
缓冲流
缓冲流是一种装饰器类,目的是让原字节流、字符流新增缓冲的功能以提高读取或写入。
缓冲字节输入流:
BufferedInputStream(InputStream in):
缓冲字节输出流:
BufferedOutputStream(OutputStream out):
public class Test {
public static void main(String[] args) {
BufferedInputStream input = null;
BufferedOutputStream output = null;
Date date = new Date();
System.out.println("文件开始传输时间:"+date);
try {
input = new BufferedInputStream(new FileInputStream("E:\\MY-1.231.1.exe"));//缓冲字节输入流
output = new BufferedOutputStream(new FileOutputStream("E:\\1.exe"));//缓冲字节输出流
int vaildLength = 0;
byte [] car = new byte[1024];
while((vaildLength=input.read(car))!=-1) {
output.write(car, 0, vaildLength);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(input!=null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(output!=null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
date = new Date();
System.out.println("文件传输完成时间:"+date);
}
}
此次测试使用的文件大小为1G,执行结果:
速度非常快。
最后字符缓冲输入流和字符缓冲输出流与字节相似在此不再赘述。