一、什么是流
流通常可以理解外一串连续不断的数据的集合,就像你家的水龙头,水的一端是水泵在一点一点的供水,而在另外一端看到的是一股连续不断的水流。数据的写入可以是一段一段向管道中写入数据,这些数据段,会先后排成一个长长的数据流。对于数据读取程序来说,看不到流写入时的情况,每次读取只能先读取前面先到达的数据,再读取后面的数据。就好像水龙头,你只有放掉了水管前面的水,才能收到水管后面的水。
二、IO流的分类
(1)按照读取类型可以分为:
- 字节流
- 字符流
(2)按照读取方向可以分为:
- 输入流
- 输出流
三、IO继承关系图(部分)
四、字符流
(1)FileReader
FileReader
用于读取字符文件。
public class ReadFile {
public static void main(String[] args) throws Exception {
/**
* int read(char cbuf[])方法 将读取的字符数 存储在cbuff的char数组中
* 返回值:位读取字符的长度
* 如果读到最后了 返回-1
*/
int len = 0;
char [] temp = new char[1024];
Reader reader = new FileReader(new File("D:\\testfile\\a.txt"));
while ((len=reader.read(temp))>0){
System.out.println(new String(temp,0,temp.length));
}
//关流
reader.close();
}
}
(2)FileWriter
FileWriter
用于输出字符流,将字符流写入文件
// 将字符串写出到a.txt文件
public class WriterFile {
public static void main(String[] args) throws Exception {
FileWriter fileWriter = new FileWriter(new File("D:\\testfile\\a.txt"));
fileWriter.write("我来写出字符串");
//刷新 写入
fileWriter.flush();
//关流
fileWriter.close();
}
}
复制文本
将一个文本内容 复制到另外一个文件中
/**
* @author hao
* @create 2019-07-27 ${TIM}
* 复制字符流
* 将a.txt的文本内容复制到b.txt中
*/
public class CopyFile {
public static void main(String[] args) throws Exception{
int len = 0;
char [] temp = new char[1024];
//声明字符读取流b
Reader reader = new FileReader(new File("D:\\testfile\\a.txt"));
//字符输出流
FileWriter fileWriter = new FileWriter(new File("D:\\testfile\\b.txt"));
while ((len=reader.read(temp))>0){
fileWriter.write(temp,0,len);
//每次都要刷新写入
fileWriter.flush();
}
//关闭资源
fileWriter.close();
reader.close();
}
}
(3)字符缓冲流
1、BufferedWriter
BufferedWriter
,使用缓冲区功能写出字符。他的构造函数BufferedWriter(Writer out) 传入一个字符输入流,其实这是属于一种装饰者模式,对传入的Writer
进行加强了。
通过BufferedWriter源码发现
1、当使用write(char cbuf[], int off, int len)会自动调用刷新flushBuffer()
2、BufferedWriter有一个独有的方法 newLine() 产生一个新行
public class BufferedWriterFile {
public static void main(String[] args) throws Exception{
//构建字符缓冲区输出流
//他的构造函数BufferedWriter(Writer out) 传入一个字符输入流
//BufferedWriter特点:
//1、当使用write(char cbuf[], int off, int len)会自动调用刷新
//2、 close()方法的时候 会执行刷新动作
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("D:\\testfile\\a.txt")));
writer.write("哈哈哈哈或");
writer.write("嘿嘿嘿嘿");
writer.write("喵喵喵喵");
//关闭资源(内部进行了刷新缓冲区)
writer.close();
}
}
当我们以上面的方法,写出时,我们的内容都会是一行的 ,如下:
哈哈哈哈或嘿嘿嘿嘿喵喵喵喵
可以是用两种方式使得换行,1.在结尾加上换行字符\n
2.使用newLine()
产生新的一行再写出
writer.write("哈哈哈哈或");
writer.newLine(); //产生新的一行
writer.write("嘿嘿嘿嘿");
writer.newLine();
writer.write("嘿嘿嘿嘿");
writer.newLine();
writer.write("喵喵喵喵");
其实内部也是写入一个\n
只不过这种方法更加优雅。
2、BufferedReader
BufferedReader
字符缓冲读取流:从一个字符输入流中读取文本。缓冲字符,以便提供字符、数组和行的有效读取。
public class BufferedReaderFile {
public static void main(String[] args) throws Exception {
BufferedReader readerFile = new BufferedReader(new FileReader(new File("D:\\testfile\\a.txt")));
/**
* 使用readLine()每次读取一行 当有数据时候 返回读取到行内容 当没有数据的时候 返回null
*/String len = null;
while ((len=readerFile.readLine())!=null){
System.out.println(len);
}
readerFile.close();
}
}
五、字节流
字节流,以字节为单位,读取文件,输出文件
(1)InputStream:字节输入流。
/**
* @author hao
* @create 2019-07-27 ${TIM}
* 以字节流的形式读取
* 读取c.txt中的数据
* c.txt中的内容 只有一个字符 A
*/
public class InputStreamFile {
public static void main(String[] args) throws IOException {
//获得流
InputStream inputStream = new FileInputStream(new File("D:\\testfile\\c.txt"));
//将获得流的byte 存储在int中
int read = inputStream.read();
System.out.println(read);//输出结果65
}
}
输出结果:65
说明,是以字节为单位读取的文件。一个字母A
,占一个字节。每次读取一个字节
(2)OutputStream 字节输出流
public class OutputStreamFile {
public static void main(String[] args) throws Exception{
//获得流
OutputStream outputStream = new FileOutputStream(new File("D:\\testfile\\c.txt"));
//字节数组输出
outputStream.write("hello".getBytes());
//关闭流
outputStream.close();
}
}
·(3)复制操作
概述:将一个文件以字节流的形式读入,再以流的形式写出,从而达到复制的目的。因为是字节流,理论上可以复制所有类型的文件。测试复制一个音频文件。
public class CopyFile {
public static void main(String[] args) {
FileInputStream in = null;
FileOutputStream out = null;
try {
long start = System.currentTimeMillis();
in = new FileInputStream("D:\\testfile\\a\\海阔天空.mp3");
out = new FileOutputStream("D:\\testfile\\b\\海阔天空.mp3");
//临时缓存字节
byte [] temp = new byte[1024];
int len = 0;
while ((len =in.read(temp))!=-1){
//进行写出
out.write(temp,0,len);
}
long end = System.currentTimeMillis();
System.out.println("复制花费时间: "+(end-start));
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(null!=in)in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null!=out)out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出:复制花费时间: 170
(4)缓冲流
设置缓冲区,可以加快读流和写流的速度
BufferedInputStream
,BufferedOutputStream
,分别是输入缓冲流和输出缓冲流。通如下过构造方法,获得对象。
使用读取和写出的方法相同。其实就是加强版的InputStream
和OutputStream
public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)
同样的复制操作使用,缓冲流
public class CopyFile {
public static void main(String[] args) {
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
long start = System.currentTimeMillis();
in = new BufferedInputStream(new FileInputStream("D:\\testfile\\a\\海阔天空.mp3"));
out = new BufferedOutputStream(new FileOutputStream("D:\\testfile\\b\\海阔天空.mp3"));
//临时缓存字节
byte [] temp = new byte[1024];
int len = 0;
while ((len =in.read(temp))!=-1){
//进行写出
out.write(temp,0,len);
}
long end = System.currentTimeMillis();
System.out.println("复制花费时间: "+(end-start));
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(null!=in)in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null!=out)out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出:复制花费时间: 120
通过对比发现使用缓冲流的复制时间小于未使用的。说明可以提高效率。
六、转换流
(1)InputStreamReader 是字节转字符的输入流。他是字节流通向字符流的桥梁:它使用chartset读取字节,并将其解码。(如果未设置,将使用默认的编码格式)
//构造方法
//传入一个 字节流 并制定编码格式
public InputStreamReader(InputStream in, Charset cs)
InputStreamReader代码:
public class InputStreamReaderFile {
public static void main(String[] args) throws Exception {
//使用GBK方式 读取字节流 并将其解码
InputStreamReader reader = new InputStreamReader(new FileInputStream("D:\\testfile\\b1.txt"),"GBK");
int len = 0;
char [] temp = new char[1024]; while ((len=reader.read(temp))>0){
System.out.println(new String(temp,0,temp.length));
}
reader.close();
}
}
(2)OutputStreamWriter:是字节转字符流的桥梁:他使用制定的charst将字节转换为字符并且写入。
public class OutputStreamWriterFile {
public static void main(String[] args ) throws Exception {
FileOutputStream fos=new FileOutputStream("d:\\testfile\\a.txt");
//以gbk编码格式写出 这就要求写入的文件必须是以gbk编码的
OutputStreamWriter opsw=new OutputStreamWriter(fos,"GBK");
opsw.write("我好你好大家好");
opsw.close();
}
}
(3)练习情景
将一个以gbk编码的a.txt内容 复制到 以utf-8编码的b.txt 这个时候就需要用到转换流啦
@Test
public void test() throws Exception{
InputStreamReader reader = new InputStreamReader(new FileInputStream("D:\\testfile\\a.txt"),"GBK");
OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream("d:\\testfile\\b.txt"),"UTF-8");
int len = 0;
char [] temp = new char[1024];
while ((len=reader.read(temp))>0){
writer.write(temp,0,len);
}
//关流
reader.close();
writer.close();
}
其实我们看继承树就可以发现,FileWriter
是OutputStreamWriter的子类,FileReader
是OutputStreamWriter
子类。而且他们底层也是调用他们父类方法实现的。
当我们调用FileWriter
的构造方法的时候,调用父类的构造
public FileWriter(File file) throws IOException {
//它是调用他的父类,其实也就是OutputStreamWriter
super(new FileOutputStream(file));
}
父类的构造
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}