与字节流一次处理1个字节相比,字符流每次处理2个字节,除此之外,还有一些区别:
1.字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串
2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以
---摘抄自Linux公社.
我们可以看看字节流和字符流的结构,可以发现字节流的类比字符流多,下面两张图片拍摄自<Java核心技术>第10版 卷2:
字节流:
字符流:
不过在写入上,两者还有一点区别,下面来看看,分别用OutputStream与Writer写入一点内容,之后用查看文件:
public static void main(String[] args) throws IOException{
File byteStrean = new File("byte.txt");
File charStream = new File("char.txt");
byte[] bytes = "123456".getBytes();
OutputStream os = new FileOutputStream(byteStrean);
os.write(bytes);
Writer w = new FileWriter(charStream);
w.write("123456");
}
运行之后,发现两个文件都出现了,不同的是byte.txt中出现了写入的内容,而char.txt并没有内容,这次对字符流调用flush():
public static void main(String[] args) throws IOException {
......
w.write("123456");
w.flush();
}
再次运行,发现两个文件都有内容,说明字符流确实跟缓冲字节流一样,拥有缓冲区,我们来看看Writer的源码,有如下内容:
public abstract class Writer implements Appendable, Closeable, Flushable {
private char[] writeBuffer;
private static final int WRITE_BUFFER_SIZE = 1024;
......
}
可以看到存在一个室友的字符数组,常量字面意思是写入缓冲区大小,再来看看两个Writer的write(),一个参数是字符串,一个是int:
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= WRITE_BUFFER_SIZE) {
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE]; //没有就创建一个.
}
cbuf = writeBuffer;
} else { // Don't permanently allocate very large buffers. 不要永久分配过大的缓冲区
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0); //String中转换为字符数组的方法.
write(cbuf, 0, len); //调用子类write()进行写入.
}
}
public void write(int c) throws IOException {
synchronized (lock) { //同步锁
if (writeBuffer == null){
writeBuffer = new char[WRITE_BUFFER_SIZE]; //创建一个常量(1024)大小的字符数组.
}
writeBuffer[0] = (char) c; //强转为char.
write(writeBuffer, 0, 1); //调用子类write()进行写入.
}
}
abstract public void write(char cbuf[], int off, int len) throws IOException;
//留给子类重写的方法.
可以看到都创建了一个字符数组并针对它进行写入,还有把字符串转换为字符数组的过程,可以看到字符流中存在一个缓冲区,且确实都是针对字符进行操作的.
而Reader中就没有flush(),下面来看看它读取数据的情况,就读取char.txt:
public static void main(String[] args) throws IOException{
File charStream = new File("char.txt");
Reader reader = new FileReader(charStream);
char[] chars = new char[6];
reader.read(chars);
System.out.println(new String(chars));
}
运行后出现了文件内的内容.
Reader和Writer的构造器都是protected的,说明只有子类可见.而在Reader中,还利用了Java的nio技术,实现的是Readable接口中的read():
public int read(java.nio.CharBuffer target) throws IOException {
int len = target.remaining();
char[] cbuf = new char[len];
int n = read(cbuf, 0, len);
if (n > 0)
target.put(cbuf, 0, n);
return n;
}
Writer中的方法
说说除write()之外的方法
Writer append(char c)
Writer append(CharSequence csq)
Writer append(CharSequence csq, int start, int end)
实现自Appendable,由于返回值是Writer,所以可以连续调用.
abstract void close()
关闭流,首先调用flush().
abstract void flush()
冲刷流.
这里主要测试一下append()与write()的不同:
public static void main(String[] args) throws IOException{
File charStream = new File("char.txt");
String write = "1234567890";
Writer writer = new FileWriter(charStream);
writer.write(write);
writer.close();
}
但是writer()的返回值是void,也就是不能出现write().其他方法()的情况,下面来看看append的情况:
public static void main(String[] args) throws IOException{
File charStream = new File("char.txt");
Writer writer = new FileWriter(charStream);
writer.append("123").append("456").append("789").close();
}
由于append()的返回值是writer,所以之后依旧可以调用Writer下的方法.
Reader中的方法
这里说说除read()之外的其他方法,但是mark()和reset()都是不支持:
abstract void close()
关闭流.
void mark(int readAheadLimit)
标记流中readAheadLimit位置.
boolean markSupported()
看看这个流是否支持markSupported的操作.
boolean ready()
告是否已经准备好被读取.
void reset()
将这个流复位.
long skip(long n)
跳过流中的n个字符
public static void main(String[] args) throws IOException{
File charStream = new File("char.txt");
Reader reader = new FileReader(charStream);
System.out.println("是否已经准备被读取:" + reader.ready()); //true
System.out.println("是否支持mark():" + reader.markSupported()); //false
}