Java IO流总结归纳,输入输出流
字符流&字节流 区别
-
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
-
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
-
字节流:一次读入或读出是8位二进制。使用字节流时,即使流没有关闭,最终也可以输出到文件;
-
字符流:一次读入或读出是16位二进制。使用字符流时,所有的内容保存在缓冲区,流关闭时 会强制性的将缓冲区内容写到文件中,如果没有关闭流,文件中就不会有内容输出。
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
InputStream
常用方法 | 描述 |
---|---|
public abstract int read( ) | 读取一个byte的数据,返回值是高位补0的int类型值。 若返回值=-1说明没有读取到任何字节读取工作结束。 |
public int read(byte b[ ]) | 读取数据放到b数组中。返回值是读取的字节数。 |
public int read(byte b[ ], int off, int len) | 从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中 |
public int available( ) | 返回输入流中可以读取的字节数。 |
public int close( ) | 使用完后,必须对我们打开的流进行关闭 |
主要子类
-
FileInputStream:把一个文件作为InputStream,实现对文件的读取操作
-
ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用
-
StringBufferInputStream:把一个String对象作为InputStream
-
PipedInputStream:实现了pipe的概念,主要在线程中使用
-
SequenceInputStream:把多个InputStream合并为一个InputStream
OutputStream
常用方法 | 描述 |
---|---|
public void write(byte b[ ]) | 将参数b中的字节写到输出流 |
public void write(byte b[ ], int off, int len) | 将参数b的从偏移量off开始的len个字节写到输出流 |
public abstract void write(int b) | 先将int转换为byte类型,把低字节写入到输出流中 |
public void flush( ) | 将数据缓冲区中数据全部输出,并清空缓冲区 |
public void close( ) | 关闭输出流并释放与流相关的系统资源 |
主要子类
-
ByteArrayOutputStream:把信息存入内存中的一个缓冲区中
-
FileOutputStream:把信息存入文件中
-
PipedOutputStream:实现了pipe的概念,主要在线程中使用
-
SequenceOutputStream:把多个OutStream合并为一个OutStream
注:流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。
文件输入流 FileInputStream
import java.io.FileInputStream;
import java.io.IOException;
public class A1 {
public static void main(String[] args) throws IOException {
String filePath = "a.txt";
FileInputStream fis = new FileInputStream(filePath);
int a;
String result = "";
// 流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。
while ((a = fis.read()) != -1) {
result += a;
}
System.out.println(result);
/*返回值是 int 类型的,会将取出来的一个字节高位会补 0 成一个 int 型。所以输出来的应该这个数据的 ASCII 码。*/
}
}
结果:
分析:
1.我们看到输入哈,输出?,但这并不是我们想要的。为什么会这样呢?我们来看一下 ? 的 ASCII 码,刚好是 229,这我们就知道它只取了第一字节ASCII码对应的字符。
2.会输出一些整数即 ASCII 码,因为读的时候是一个字节一个字节的读,而中文又不止一个字节,到底要几个字节来表示,这要取决于编码集(我这里的是拆成了三个字节,两个字节是用来标记一行的)。反正会拆成几个字节来读。
文件输出流 FileoutStream
用法 | 描述 |
---|---|
FileOutputStream(File file/ String filepath) | 创建一个向指定 File或String 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(File file, boolean append) | append表示内容是否追加 |
注:
(1)文件中写数据时,若文件已经存在,则覆盖存在的文件;
(2)读/写操作结束时,应调用close方法关闭流。
import java.io.FileOutputStream;
import java.io.IOException;
public class A1 {
public static void main(String[] args) throws IOException {
String filePath = "a.txt";
FileOutputStream fos = new FileOutputStream(filePath);
int a;
while ((a = System.in.read()) != -1) {
fos.write(a);
}
}
}
缓冲流 BufferedInputStream/ BufferedOutputStream(也称包装流)
首先抛出一个问题,有了InputStream为什么还要有BufferedInputStream?
BufferedInputStream
和BufferedOutputStream
这两个类分别是FilterInputStream
和FilterOutputStream
的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。
我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream
写完数据后,要调用flush()
方法或close()
方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader
和BufferedWriter
两个类。
现在就可以回答在本文的开头提出的问题:
BufferedInputStream
和BufferedOutputStream
类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。
总结:
BufferedInputStream
是缓冲输入流。它继承于FilterInputStream
。BufferedInputStream
的作用是为另一个输入流添加一些功能,例如提供“缓冲功能”以及支持mark()
标记和reset()
重置方法。BufferedInputStream
本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream
后,当我们通过read()
读取输入流的数据时,BufferedInputStream
会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。
使用 | |
---|---|
BufferedInputStream(InputStream in, int size) | 使用指定buf大小、底层字节输入流构建bis |
BufferedOutputStream(OutputStream out, int size); | 使用指定大小、底层字节输出流构造bos |
注意:
我们需要手动的去刷新缓冲区。调用包装流的 flush()
方法。这个方法的作用就是将缓存区的内容写入到硬盘上并清空缓存区。
当然能不能不写 flush()
方法也让它刷新呢?
答:可以,调用 close()
方法关闭流即可。当你调用 close()
方法时它会先刷新缓存区。这就是刚才上面所说要记得用完之后要关闭流。不过最好有使用到包装流的时候两个方法都记得写上。
void write(char ch);//写入单个字符。
void write(char []cbuf,int off,int len)//写入字符数据的某一部分。
void write(String s,int off,int len)//写入字符串的某一部分。
void newLine()//写入一个行分隔符。
void flush();//刷新该流中的缓冲。将缓冲数据写到目的文件中去。
void close();//关闭此流,再关闭前会先刷新他。
Eg.复制文件
import java.io.*;
public class A1 {
public static void main(String[] args) throws IOException {
String oldFile = "1.JPEG";
String newFile = "2.JPG";
InputStream inputStream = new FileInputStream( oldFile ) ;
BufferedInputStream bufferedInputStream = new BufferedInputStream( inputStream ) ;
OutputStream outputStream = new FileOutputStream( newFile ) ;
BufferedOutputStream 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();
}
}
缓冲流 BufferedReader/BufferedWriter
构造函数 | |
---|---|
BufferedReader(Reader in, int sz) | 创建一个使用指定大小输入缓冲区的缓冲字符输入流。 |
BufferedWriter(Writer out, int sz) | 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 |
方法:
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() 操作(它一定支持)。
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) //写入字符串的某一部分。
Eg.键入存文件
import java.io.*;
public class A1 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("a.txt")));
String s;
while ((s = br.readLine()).length() > 0) {
bw.write(s, 0, s.length());
bw.flush();
}
bw.close();
}
}
转换流 InputStreamReader/OutputStreamWriter
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。
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。
注意:InputStreamReader
、OutputStreamWriter
实现从字节流到字符流之间的转换,使得流的处理效率得到提升,但是如果我们想要达到最大的效率,我们应该考虑使用缓冲字符流包装转换流的思路来解决问题。
比如:BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
utStreamWriter(OutputStream out, CharsetEncoder enc) //创建使用给定字符集编码器的 OutputStreamWriter。
参考链接:
Java IO流学习总结
java输入输出流详细讲解(入门经典),详细讲解JAVA中的IO流