字节流
在java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。(这四个都是抽象类)
字节输出流:OutputStream
FileOutputStream类的定义:
public abstract class OutputStream extends Object implements Closeable,Flushable
从以上的定义可以发现,此类是一个抽象类,如果想要使用此类的话,则首先必须通过子类实例化对象,那么如果现在要操作的是一个文件,则可以使用:FileOutputStream类。通过向上转型之后,可以为OutputStream实例化
Closeable表示可以关闭的操作,因为程序运行到最后肯定要关闭
Flushable:表示刷新,清空内存中的数据
FileOutputStream类的构造方法如下:
public FileOutputStream(File file)throws FileNotFoundException
使用不同输出流对同一文件进行输出会覆盖其他流的输出内容,如果要追加的话,请看FileOutputStream类的另一个构造方法:
public FileOutputStream(File file,boolean append)throws FileNotFoundException
它会锁定文件尾部添加。
字节输入流:InputStream
InputStream类的定义:
public abstract class InputStream extends Object implements Closeable
与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类,如果现在是从文件中读取,就用FileInputStream来实现。
观察FileInputStream类的构造方法:
public FileInputStream(File file)throws FileNotFoundException
主要方法:
read():
public abstract int read() throws IOException;
read(byte b[]):相当于 read(b, 0, b.length)
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
read(byte b[], int off, int len):返回 0 表示存放字节数组为空,返回 -1 表示流末尾无法读取,返回读取的字节数(i + 1)≤ 存放字节数组长度
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
Tips:根据文件的大小来定义字节数组的大小,使用 File类中的方法:public long length()
字符流
在程序中一个字符等于两个字节,那么java提供了Reader、Writer两个专门操作字符流的类。
字符输出流:Writer
Writer本身是一个字符流的输出类,此类的定义如下:
public abstract class Writer extends Object implements Appendable,Closeable,Flushable
此类本身也是一个抽象类,如果要使用此类,则肯定要使用其子类,此时如果是向文件中写入内容,所以应该使用FileWriter的子类。
FileWriter类的构造方法定义如下:
public FileWriter(File file)throws IOException
使用 FileWriter(File file, boolean append) 在文件末尾输入
字符输入流:Reader
Reader是使用字符的方式从文件中取出数据,Reader类的定义如下:
public abstract class Reader extends Objects implements Readable,Closeable
Reader本身也是抽象类,需要子类实现其功能,InputStreamReader 就是一个实现类
public class InputStreamReader extends Reader {
- 使用方式 1:
reader = new InputStreamReader(fip, "UTF-8");
while (reader.ready()) {
System.out.println((char) reader.read());
}
- 使用方式 2:
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
int in;
while ((in = reader.read()) != -1) {
System.out.println((char) in);
}
ready() 的实现:
指示此流是否准备好读取。如果inputstreamreader的输入缓冲区不是空的,或者如果可以从底层字节流读取字节,那么它就准备好了。
public boolean ready() throws IOException {
return sd.ready();
}
private final StreamDecoder sd;
StreamDecoder
public boolean ready() throws IOException {
synchronized(this.lock) {
this.ensureOpen();
return this.haveLeftoverChar || this.implReady();
}
}
boolean implReady() {
return this.bb.hasRemaining() || this.inReady();
}
private boolean inReady() {
try {
return this.in != null && this.in.available() > 0 || this.ch instanceof FileChannel;
} catch (IOException var2) {
return false;
}
}
private ByteBuffer bb;
ByteBuffer
缓存区当前位置和缓存限制位数之间是否至少有一个以上元素
public final boolean hasRemaining() {
return position < limit;
}
如果现在要从文件中读取内容,则可以直接使用FileReader子类。
FileReader 类的定义:
public class FileReader extends InputStreamReader {
FileReader的构造方法定义如下:
public FileReader(File file)throws FileNotFoundException
使用方式:
Reader input=new FileReader(f);
int in;
while((in = input.read()) != -1){
System.out.println((char) in);
}
字节流与字符流的区别
字节流和字符流使用是非常相似的,那么除了操作代码的不同之外,还有哪些不同呢?
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的
字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
那开发中究竟用字节流好还是用字符流好呢?
在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。
如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式(节省内存)。
原文:字节流与字符流的区别详解
InputStreamReader
java.lang.Object
java.io.Reader
java.io.InputStreamReader
InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码。
参数 in 对象通过 InputStream in = System.in 用来读取键盘上的数据。
或者 InputStream in = new FileInputStream(String fileName) 用来读取文件中的数据。可以看出 FileInputStream 为InputStream的子类。
主要方法:
int read(); //读取单个字符,返回值为字符 ASCII 码,-1 表示到达流的末尾。
int read(char []cbuf); //将读取到的字符存到数组中。返回读取的字符数。
BufferedReader
java.lang.Object
java.io.Reader
java.io.BufferedReader
提供缓存读取方式,提高效率。
FileReader
FileReader 继承 InputStreamReader,用来包装 FileInputStream 流:
public class FileReader extends InputStreamReader
实际是使用 InputStreamReader 的默认构造函数,传入新建 FileInputStream 流进行构造:
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
优点是减少代码,提高简洁性:不用新建 InputStreamReader
缺点是使用系统默认编码,指定编码还需新建 InputStreamReader:
InputStreamReader inReader = new InputStreamReader(file, "UTF-8");
OutputStreamWriter
java.lang.Object
java.io.Reader
java.io.OutputStreamReader
同 InputStreamReader,可以提供编码解码方式。
BufferedWriter
java.lang.Object
java.io.Reader
java.io.BufferedWriter
提供缓存写入方式,提高效率,降低硬盘的磨损。
FileWriter
java.lang.Object
java.io.Writer
java.io.OutputStreamWriter
java.io.FileWriter
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter(也就是说,使用默认字符编码,就不需要构造 OutputStreamWriter 而直接使用了)。
示例:
/**
* 使用 FileWriter 写入文件
*/
public void writeToFileUseFileWriter(String fileName, String content) {
try {
// 打开一个写文件器,构造函数中的第二个参数 true 表示以追加形式写文件
FileWriter writer = new FileWriter(fileName, true);
writer.write(content);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
PrintWriter
java.lang.Object
java.io.Writer
java.io.PrintWriter
使用 println 方法用平台行分隔符自动换行。
PrintWriter有含有 OutputStream、File、Writer 的构造方法。BufferedWriter 只接受 writer。
基本上这两个类都差不多,用哪个都可以,PrintWriter 稍好