天天看点

Java的IO系统:字符流与顶级父类Reader Writer

与字节流一次处理1个字节相比,字符流每次处理2个字节,除此之外,还有一些区别:

1.字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串

2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以

---摘抄自Linux公社.

我们可以看看字节流和字符流的结构,可以发现字节流的类比字符流多,下面两张图片拍摄自<Java核心技术>第10版 卷2:

字节流:

Java的IO系统:字符流与顶级父类Reader Writer

字符流:

Java的IO系统:字符流与顶级父类Reader Writer

 不过在写入上,两者还有一点区别,下面来看看,分别用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
    }