Java基础-IO流(2)
(参考博客:https://blog.csdn.net/zhaoyanjun6/article/details/54972773)
一、缓冲流
为何要使用缓冲流?
BufferedInputStream和BufferedOutputStream这两个类分别是FilterInputStream和FilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。
我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader和BufferedWriter两个类。
所以,BufferedInputStream和BufferedOutputStream类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。
总结:
- BufferedInputStream 是缓冲输入流。它继承于FilterInputStream。
- BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持mark()标记和reset()重置方法。
- BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。
(1)字节缓冲流
1. BufferInputStream缓冲输入流
1)构造函数
BufferedInputStream(InputStream in) //使用默认buf大小、底层字节输入流构建bis BufferedInputStream(InputStream in, int size) //使用指定buf大小、底层字节输入流构建bis
2)常用方法
int available(); //返回底层流对应的源中有效可供读取的字节数 void close(); //关闭此流、释放与此流有关的所有资源 boolean markSupport(); //查看此流是否支持mark void mark(int readLimit); //标记当前buf中读取下一个字节的下标 int read(); //读取buf中下一个字节 int read(byte[] b, int off, int len); //读取buf中下一个字节 void reset(); //重置最后一次调用mark标记的buf中的位子 long skip(long n); //跳过n个字节、 不仅仅是buf中的有效字节、也包括in的源中的字节
2. BufferOutputStream缓冲输出流
1)构造函数
BufferedOutputStream(OutputStream out); //使用默认大小、底层字节输出流构造bos。默认缓冲大小是 8192 字节( 8KB ) BufferedOutputStream(OutputStream out, int size); //使用指定大小、底层字节输出流构造bos
2)常用方法
//在这里提一句,`BufferedOutputStream`没有自己的`close`方法,当他调用父类`FilterOutputStrem`的方法关闭时,会间接调用自己实现的`flush`方法将buf中残存的字节flush到out中,再`out.flush()`到目的地中,DataOutputStream也是如此。 void flush(); 将写入bos中的数据flush到out指定的目的地中、注意这里不是flush到out中、因为其内部又调用了out.flush() write(byte b); 将一个字节写入到buf中 write(byte[] b, int off, int len); 将b的一部分写入buf中
3. 实例操作,复制文件,操作:使用缓存流将F盘根目录里面名字为:123.png 图片复制成 abc.png。
public class A3 { public static void main(String[] args) throws IOException { String filePath = "F:/123.png" ; String filePath2 = "F:/abc.png" ; File file = new File( filePath ) ; File file2 = new File( filePath2 ) ; copyFile( file , file2 ); } public static void copyFile( File oldFile , File newFile){ InputStream inputStream = null ; BufferedInputStream bufferedInputStream = null ; OutputStream outputStream = null ; BufferedOutputStream bufferedOutputStream = null ; try { inputStream = new FileInputStream( oldFile ) ; bufferedInputStream = new BufferedInputStream( inputStream ) ; outputStream = new FileOutputStream( newFile ) ; bufferedOutputStream = new BufferedOutputStream( outputStream ) ; byte[] b=new byte[1024]; //代表一次最多读取1KB的内容 int length = 0 ; //代表实际读取的字节数 while( (length = bufferedInputStream.read( b ) )!= -1 ){ //length 代表实际读取的字节数 bufferedOutputStream.write(b, 0, length ); } //缓冲区的内容写入到文件 bufferedOutputStream.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally { if( bufferedOutputStream != null ){ try { bufferedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( bufferedInputStream != null){ try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( inputStream != null ){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if ( outputStream != null ) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
(2)字符缓冲流
1. BufferedReader
1) 构造方法
BufferedReader(Reader in, int sz) //创建一个使用指定大小输入缓冲区的缓冲字符输入流。 BufferedReader(Reader in) //创建一个使用默认大小输入缓冲区的缓冲字符输入流。
2) 常用方法
int read() //读取单个字符。 int read(char[] cbuf, int off, int len) //将字符读入数组的某一部分。 String readLine() //读取一个文本行。 boolean ready() //判断此流是否已准备好被读取。 void reset() //将流重置到最新的标记。 long skip(long n) //跳过字符。 void close() //关闭该流并释放与之关联的所有资源。 void mark(int readAheadLimit) //标记流中的当前位置。 boolean markSupported() //判断此流是否支持 mark() 操作(它一定支持)。
2. BufferedWriter
1) 构造方法
BufferedWriter(Writer out, int sz) //创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 BufferedWriter(Writer out) //建一个使用默认大小输出缓冲区的缓冲字符输出流。
2) 常用方法
void close() // 关闭此流,但要先刷新它。 void flush() //刷新该流的缓冲。 void newLine() //写入一个行分隔符。 void write(char[] cbuf, int off, int len) //写入字符数组的某一部分。 void write(int c) //写入单个字符。 void write(String s, int off, int len) //写入字符串的某一部分。
3. 实例操作
public class A4 { public static void main(String[] args) { String filePath = "F:/123.txt" ; String filePath2 = "F:/abc.txt" ; File file = new File( filePath ) ; File file2 = new File( filePath2 ) ; copyFile( file , file2 ); } private static void copyFile( File oldFile , File newFile ){ Reader reader = null ; BufferedReader bufferedReader = null ; Writer writer = null ; BufferedWriter bufferedWriter = null ; try { reader = new FileReader( oldFile ) ; bufferedReader = new BufferedReader( reader ) ; writer = new FileWriter( newFile ) ; bufferedWriter = new BufferedWriter( writer ) ; String result = null ; //每次读取一行的内容 while ( (result = bufferedReader.readLine() ) != null ){ bufferedWriter.write( result ); //把内容写入文件 bufferedWriter.newLine(); //换行,result 是一行数据,所以没写一行就要换行 } bufferedWriter.flush(); //强制把数组内容写入文件 } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally { try { bufferedWriter.close(); //关闭输出流 } catch (IOException e) { e.printStackTrace(); } try { bufferedReader.close(); //关闭输入流 } catch (IOException e) { e.printStackTrace(); } } } }
二、ByteArrayInputStream、ByteArrayOutPutStream
ByteArrayInputStream
可以将字节数组转化为输入流 。ByteArrayOutputStream
可以捕获内存缓冲区的数据,转换成字节数组。
1. ByteArrayInputStream
构造方法:
public ByteArrayInputStream(byte buf[]) public ByteArrayInputStream(byte buf[], int offset, int length)
常用方法:
void close() // 关闭该流并释放与之关联的所有资源。 String getEncoding() //返回此流使用的字符编码的名称。 int read() //读取单个字符。 int read(char[] cbuf, int offset, int length) //将字符读入数组中的某一部分。 boolean ready() //判断此流是否已经准备好用于读取。
2. ByteArrayOutputStream
构造方法:
public ByteArrayOutputStream() public ByteArrayOutputStream(int size)
常用方法:
void write(int b) void write(byte b[], int off, int len) void writeTo(OutputStream out) byte toByteArray()[] void close()
3. 实例测试
//字节流ByteArrayInputStream的读写过程测试
package com.app; import java.io.ByteArrayInputStream; import java.io.IOException; public class A7 { public static void main(String[] args) { String mes = "hello,world" ; byte[] b = mes.getBytes() ; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( b ) ; int result = -1 ; while( ( result = byteArrayInputStream.read() ) != -1{ System.out.println( (char) result ); } try { byteArrayInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
//将ByteArrayOutputStream
读出的字节流用FileOutputStream
写入文件
package com.app; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class A6 { public static void main(String[] args) { String mes = "你好,world" ; byte[] b = mes.getBytes() ; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream() ; try { byteArrayOutputStream.write( b ); FileOutputStream fileOutputStream = new FileOutputStream( new File( "F:/123.txt" ) ) ; byteArrayOutputStream.writeTo( fileOutputStream ) ; fileOutputStream.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally{ try { byteArrayOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
三、转换流
1. InputStreamReader
InputStreamReader 是字符流Reader的子类,是字节流通向字符流的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。一次只读一个字符。
构造函数:
InputStreamReader(Inputstream in) //创建一个使用默认字符集的 InputStreamReader。 InputStreamReader(Inputstream in,Charset cs) //创建使用给定字符集的 InputStreamReader。 InputStreamReader(InputStream in, CharsetDecoder dec) //创建使用给定字符集解码器的 InputStreamReader。 InputStreamReader(InputStream in, String charsetName) //创建使用指定字符集的 InputStreamReader。
一般方法:
void close() // 关闭该流并释放与之关联的所有资源。 String getEncoding() //返回此流使用的字符编码的名称。 int read() //读取单个字符。 int read(char[] cbuf, int offset, int length) //将字符读入数组中的某一部分。 boolean ready() //判断此流是否已经准备好用于读取。
2. OutPutStreamWriter
OutputStreamWriter
是字符流Writer
的子类,是字符流通向字节流的桥梁。每次调用 write()
方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。一次只写一个字符。
构造函数:
OutputStreamWriter(OutputStream out) //创建使用默认字符编码的 OutputStreamWriter OutputStreamWriter(OutputStream out, String charsetName) //创建使用指定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, Charset cs) //创建使用给定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, CharsetEncoder enc) //创建使用给定字符集编码器的 OutputStreamWriter。
一般方法:
void write(int c) //写入的字符长度 void write(char cbuf[]) //写入的字符数组 void write(String str) //写入的字符串 void write(String str, int off, int len) //应该写入的字符串,开始写入的索引位置,写入的长度 void close() //关闭该流并释放与之关联的所有资源。
注意:InputStreamReader
、OutputStreamWriter
实现从字节流到字符流之间的转换,使得流的处理效率得到提升,但是如果我们想要达到最大的效率,我们应该考虑使用缓冲字符流包装转换流的思路来解决问题。
3. 实例测试
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; public class A5 { public static void main(String[] args) { String filePath = "F:/123.txt" ; String filePath2 = "F:/abc.txt" ; File file = new File( filePath ) ; File file2 = new File( filePath2 ) ; copyFile( file , file2 ); } private static void copyFile( File oldFile , File newFile ){ InputStream inputStream = null ; InputStreamReader inputStreamReader = null ; OutputStream outputStream = null ; OutputStreamWriter outputStreamWriter = null ; try { inputStream = new FileInputStream( oldFile ) ; //创建输入流 inputStreamReader = new InputStreamReader( inputStream ) ; //创建转换输入流 outputStream = new FileOutputStream( newFile ) ; //创建输出流 outputStreamWriter = new OutputStreamWriter( outputStream ) ; //创建转换输出流 int result = 0 ; while( (result = inputStreamReader.read()) != -1){ //一次只读一个字符 outputStreamWriter.write( result ); //一次只写一个字符 } outputStreamWriter.flush(); //强制把缓冲写入文件 } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally{ if ( outputStreamWriter != null) { try { outputStreamWriter.close(); } catch (IOException e) { e.printStackTrace(); } } if ( inputStreamReader != null ) { try { inputStreamReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
流的类型多而复杂,死记硬背是不可取的,用的时候请查阅:http://www.51gjie.com/java/1164.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步