介绍:

Writer是字符输出流的基类,Reader是字符输入流的基类。这两个类的实现类基本都是成对存在的,下面就成对讲解。

字节流与字符流区别请查看:字节流和字符流的区别

体系图:

对常见实现类API的讲解:

OutputStreamWriter和InputStreamReader:

这两个类是转换流类,OutputStreamWriter可以将字节输出流转换成字符输出流,InputStreamReader可以将字节输入流转换成字符输入流。

//以下是InputStreamReader类的API解析://构造器://传入一个字节输入流,就是把该输入流转换成字符输入流。
public InputStreamReader(InputStream in)//传入一个字符输入流并编码编码规则。
public InputStreamReader(InputStream in, String charsetName)//传入一个字符输入流并编码编码规则。
public InputStreamReader(InputStream in, Charset cs)//获取该流是否已经准备好可以读取,当缓冲区数据不为空或者底层有数据可供读取
//(也就是没有达到文件尾)时返回true。
public boolean java.io.InputStreamReader.ready() throws java.io.IOException/**
* 从缓冲区读取若干数据到char数组中。
* 参数:
* 1. char[]数组 缓冲区的数据会读取到该数组里面。
* 2. int : 读取到char[]数组的偏移量。
* 3. int : 要读取的数据的长度。
* 返回的是读取到的数据长度,-1表示文件尾。
*/
public int java.io.InputStreamReader.read(char[],int,int) throws java.io.IOException
/**
* 从缓冲区中读取一个字符。
* 返回值是读取到的数据,-1表示到达文件尾。
*/
public int java.io.InputStreamReader.read() throws java.io.IOException//关闭流
public void java.io.InputStreamReader.close() throws java.io.IOException//获取编码规则名称
public java.lang.String java.io.InputStreamReader.getEncoding()//读取若干个字符到char数组中,若干个是指 char数组长度与缓冲区大小取小值。
public int java.io.Reader.read(char[]) throws java.io.IOException//读取数据到charBuffer中。
public int java.io.Reader.read(java.nio.CharBuffer) throws java.io.IOException//这个方法不可用,直接抛异常。
public void java.io.Reader.mark(int) throws java.io.IOException//是否支持mark,总是返回false。
public boolean java.io.Reader.markSupported()//这个方法不可用,直接抛异常。
public void java.io.Reader.reset() throws java.io.IOException//过滤一定长度的数据,底层是使游标移动一定长度。
public long java.io.Reader.skip(long) throws java.io.IOException
//以下是OutputStreamWriter类的API解析://构造器:
//传入一个字节输出流,把这个字节输出流转成字节输入流
public OutputStreamWriter(OutputStream out) //传入一个字节输出流,并指定编码规则
public OutputStreamWriter(OutputStream out, String charsetName)//传入一个字节输出流,并指定编码规则
public OutputStreamWriter(OutputStream out, Charset cs)/**
*把char数组的数据写到缓冲区中。
*参数:
*1. char[] : 要写到缓冲区的字符数组。
*2. int off: char[] 的偏移量。
*3. int len: 写入数据的长度,写入的数据时chars[off]开始len长度的数据。
*/
public void java.io.OutputStreamWriter.write(char[],int,int) throws java.io.IOException//把一个字符数据写到缓冲区,参数就是要写入的字符数据。
public void java.io.OutputStreamWriter.write(int) throws java.io.IOException/**
* 把一个String对象写入到缓冲区(会转换成char数组的)
* 参数:
* 1. String : 要写入的字符串。
* 2. off:偏移量
* 3. len场地
*/
public void java.io.OutputStreamWriter.write(java.lang.String,int,int) throws java.io.IOException//关闭流,伴随刷盘行为
public void java.io.OutputStreamWriter.close() throws java.io.IOException//强制把缓冲区数据刷到存储设备上
public void java.io.OutputStreamWriter.flush() throws java.io.IOException//获取该字符流的编码规则
public java.lang.String java.io.OutputStreamWriter.getEncoding()//返回自身writer,可以链式调用writer.append("abcd").append("efg")
//进行向缓冲区添加字符
public java.io.Writer java.io.Writer.append(java.lang.CharSequence) throws java.io.IOException//链式添加,可以指定开始位置和结束位置。
public java.io.Writer
java.io.Writer.append(java.lang.CharSequence,int,int) throws java.io.IOException//链式添加一个字符
public java.io.Writer java.io.Writer.append(char) throws java.io.IOException//链式添加
public java.io.Writer java.io.Writer.append(java.lang.CharSequence) throws java.io.IOException//向缓冲区写入多个字符
public void java.io.Writer.write(char[]) throws java.io.IOException//向缓冲区写入字符
public void java.io.Writer.write(java.lang.String) throws java.io.IOException

以下是使用OutputStreamWriter 和 InputStreamReader将一个文件字母转换成大写,并生成另一个文件的demo:

public class StreamConvertToCharDemo {public static void main(String[] args) {try (InputStreamReader reader = new InputStreamReader(new FileInputStream("1.txt"));OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("3.txt"))){int len = -1;while ((len = reader.read()) != -1){writer.write(Character.toUpperCase(len));}}catch (IOException e){}}
}

运行结果:

CharArrayWriter和CharArrayReader:

这个是字符数组的输出流和输入流,是纯内存的操作,不涉及到磁盘。主要用途是在要创建临时文件,数据网络传输等情况下可以避免访问磁盘,直接在内存操作,提高运行效率。该类也不涉及底层操作,纯java代码。这里直接就用源码来讲吧。

CharArrayReader:

public class CharArrayReader extends Reader {//缓冲区protected char buf[];//游标,也就是buf[]下一个要读取的字符的下标值。protected int pos;//标记为,重置时游标会重置到该值protected int markedPos = 0;//数据长度protected int count;//指定字符数组作为缓冲区public CharArrayReader(char buf[]) {this.buf = buf;this.pos = 0;this.count = buf.length;}//指定buf字符缓冲区,并且指定传进来的字符数组的偏移量和长度public CharArrayReader(char buf[], int offset, int length) {//参数校验if ((offset < 0) || (offset > buf.length) || (length < 0) ||((offset + length) < 0)) {throw new IllegalArgumentException();}this.buf = buf;//将游标设为指定的偏移量this.pos = offset;//取偏移量+长度    和 传进来的buf数组的长度的较小值作为缓冲区的数据长度。this.count = Math.min(offset + length, buf.length);//将重置标记置为偏移量。this.markedPos = offset;}//判断流是否已经打开了,缓冲区为空表示未打开。抛出IOExceptionprivate void ensureOpen() throws IOException {if (buf == null)throw new IOException("Stream closed");}//同步方法,读取一个字符public int read() throws IOException {synchronized (lock) {//验证流是否已经打开ensureOpen();//判断是否到达文件尾if (pos >= count)return -1;else//返回return buf[pos++];}}//读取多个字符到 b[]字符数组中,指定偏移量和读取长度。public int read(char b[], int off, int len) throws IOException {//同步方法synchronized (lock) {ensureOpen();//边界检查if ((off < 0) || (off > b.length) || (len < 0) ||((off + len) > b.length) || ((off + len) < 0)) {throw new IndexOutOfBoundsException();} else if (len == 0) {//读取长度为0时也是直接返回0return 0;}//判断是否到达文件尾if (pos >= count) {return -1;}//如果读取的长度大于可读取长度,那就把读取长度设置为可读取长度。if (pos + len > count) {len = count - pos;}if (len <= 0) {return 0;}//进行复制System.arraycopy(buf, pos, b, off, len);pos += len;return len;}}//跳过一定长度的数据,同步方法。public long skip(long n) throws IOException {synchronized (lock) {ensureOpen();//如果过滤的长度大于可读取长度,那就把过滤长度设置为可读取长度。if (pos + n > count) {n = count - pos;}if (n < 0) {return 0;}//直接游标+n过滤掉n个数据。pos += n;return n;}}//判断是否可用,未到达文件尾就返回true。public boolean ready() throws IOException {synchronized (lock) {ensureOpen();return (count - pos) > 0;}}//是否支持标记。总是true支持。public boolean markSupported() {return true;}//标记当前游标,参数无作用。public void mark(int readAheadLimit) throws IOException {synchronized (lock) {ensureOpen();markedPos = pos;}}//重置游标到markedPos标记处。public void reset() throws IOException {synchronized (lock) {ensureOpen();pos = markedPos;}}//关闭流。将close置空。public void close() {buf = null;}
}

CharArrayWriter:

public
class CharArrayWriter extends Writer {//缓冲区protected char buf[];//缓冲区数据长度protected int count;//默认构造器,默认缓冲区大小为32public CharArrayWriter() {this(32);}//指定缓冲区大小的构造器public CharArrayWriter(int initialSize) {if (initialSize < 0) {throw new IllegalArgumentException("Negative initial size: "+ initialSize);}buf = new char[initialSize];}//往缓冲区中写入一个字符public void write(int c) {synchronized (lock) {int newcount = count + 1;if (newcount > buf.length) {//两倍扩容或者扩容到newcount 大小,具体看哪个大,取大的那个。buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));}buf[count] = (char)c;count = newcount;}}/*** 往缓冲区写入任意个字符* @param c 要写入的数据* @param off      c字符数组的偏移量* @param len      要写入的数据长度*/public void write(char c[], int off, int len) {if ((off < 0) || (off > c.length) || (len < 0) ||((off + len) > c.length) || ((off + len) < 0)) {//边界检查throw new IndexOutOfBoundsException();} else if (len == 0) {return;}synchronized (lock) {int newcount = count + len;if (newcount > buf.length) {//两倍扩容或者扩容到newcount 大小,具体看哪个大,取大的那个。buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));}//将c数组中的指定数据复制到缓冲区中。System.arraycopy(c, off, buf, count, len);//更新缓冲区大小count = newcount;}}/*** 将字符串的一部分写入的缓冲区中,由off和len决定* @param  要写入的字符串* @param  偏移量* @param  长度*/public void write(String str, int off, int len) {synchronized (lock) {int newcount = count + len;if (newcount > buf.length) {buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));}str.getChars(off, off + len, buf, count);count = newcount;}}/*** 将缓冲区数据写入到其他字符输出流中** @param out       要写入的字符输出流* */public void writeTo(Writer out) throws IOException {synchronized (lock) {out.write(buf, 0, count);}}/*** 链式追加缓冲区,writer.append("dsds").append("ddd"),* 与StringBuilder的append方法类似。*/public CharArrayWriter append(CharSequence csq) {String s = (csq == null ? "null" : csq.toString());write(s, 0, s.length());return this;}/*** 链式追加到缓冲区,可以指定追加内容的范围。*/public CharArrayWriter append(CharSequence csq, int start, int end) {String s = (csq == null ? "null" : csq).subSequence(start, end).toString();write(s, 0, s.length());return this;}/*** 链式追加一个字符到缓冲区*/public CharArrayWriter append(char c) {write(c);return this;}/***重置缓冲区,实际上就是把缓冲区大小count 置0,重新写入。*/public void reset() {count = 0;}/*** 返回缓冲区char数组。但不是返回缓冲区,而是将缓冲区0-count的数据返  * 回*/public char toCharArray()[] {synchronized (lock) {return Arrays.copyOf(buf, count);}}/*** 返回缓冲区大小*/public int size() {return count;}/*** 将缓冲区字符转化为String对象返回*/public String toString() {synchronized (lock) {return new String(buf, 0, count);}}/*** 刷盘,纯内存操作,空方法*/public void flush() { }/*** 关闭流,空方法。*/public void close() { }}

FilterWriter和FilterReader:

FilterWriter是字符输出流包装器的基类,FilterReader是字符输入流的包装器的基类,这两个是抽象类,对流进行包装的类可以继承该类。这两个类可以看成是辅助类。

FileWriter和FileReader:

FileWriter和FileReader是对磁盘文件操作的字符输出流和字符输入流,连通着磁盘与内存。

FileWriter继承自OutputStreamWriter,FileReader继承自InputStreamReader,除了写了一些构造器外,剩下的所有方法都没有重写,而是直接复用父类的方法,所以下面就讲讲构造器,具体api查看上面的OutputStreamWriter和InputStreamWriter即可。

以下是FileReader源码:

public class FileReader extends InputStreamReader {//参数传入的是文件路径public FileReader(String fileName) throws FileNotFoundException {//也是调用父类的构造器,跟 new InputStreamReader(new FileInputStream(fileName)) 没区别。super(new FileInputStream(fileName));}//直接传入文件public FileReader(File file) throws FileNotFoundException {super(new FileInputStream(file));}//传入FileDescriptor 对象。public FileReader(FileDescriptor fd) {super(new FileInputStream(fd));}}

以下是FileWriter的源码:

public class FileWriter extends OutputStreamWriter {//文件路径public FileWriter(String fileName) throws IOException {super(new FileOutputStream(fileName));}//append表示是否进行追加还是重写文件public FileWriter(String fileName, boolean append) throws IOException {super(new FileOutputStream(fileName, append));}//直接传入文件public FileWriter(File file) throws IOException {super(new FileOutputStream(file));}//public FileWriter(File file, boolean append) throws IOException {super(new FileOutputStream(file, append));}public FileWriter(FileDescriptor fd) {super(new FileOutputStream(fd));}}

BufferedWriter和BufferedReader:

BufferedWriter是Writer类的装饰器,BufferedReader是Reader类的装饰器,添加了缓冲区功能,提高读写的效率。

//写一个字符数组到缓冲区中,可以设置偏移量和要写入长度
public void java.io.BufferedWriter.write(char[],int,int) throws java.io.IOException//写一个字符串到缓冲区中,可以设置偏移量和要写入长度
public void
java.io.BufferedWriter.write(java.lang.String,int,int) throws java.io.IOException//写一个字符到缓冲区
public void java.io.BufferedWriter.write(int) throws java.io.IOException//关闭流,伴随刷盘操作
public void java.io.BufferedWriter.close() throws java.io.IOException//刷盘,把缓冲区的数据强制刷到磁盘或其他设备
public void java.io.BufferedWriter.flush() throws java.io.IOException
//新建一行,其实就是写一个换行符到缓冲区中。
public void java.io.BufferedWriter.newLine() throws java.io.IOException//链式添加,与StringBuilder类似。把CharSequence写到缓冲区,并可以指定开始和结束位置。
public java.io.Writer java.io.Writer.append(java.lang.CharSequence,int,int) throws java.io.IOException//链式添加,把一个字符添加到缓冲区。
public java.io.Writer java.io.Writer.append(char) throws java.io.IOException//链式添加。把CharSequence写到缓冲区。
public java.io.Writer java.io.Writer.append(java.lang.CharSequence) throws java.io.IOException//把一个字符数组添加到缓冲区中
public void java.io.Writer.write(char[]) throws java.io.IOException//把一个字符串添加到缓冲区中
public void java.io.Writer.write(java.lang.String) throws java.io.IOException

//判断流是否已经关闭
public boolean java.io.BufferedReader.ready() throws java.io.IOException//从缓冲区读取一个字符。
public int java.io.BufferedReader.read() throws java.io.IOException
//从缓冲区读取若干个字符到char[] 数组中。
//第二个int参数:偏移量,读取缓冲区的数据到char数组的从该下标开始。
//第三个int参数:len ,读取数据的长度。
public int java.io.BufferedReader.read(char[],int,int) throws java.io.IOException
//关闭流
public void java.io.BufferedReader.close() throws java.io.IOException
//读取一行数据
//如果到达文件尾,则返回null。
public java.lang.String java.io.BufferedReader.readLine() throws java.io.IOException//过滤掉一定字符长度的数据
public long java.io.BufferedReader.skip(long) throws java.io.IOException
//从缓冲区读取数据到char[]数组中。返回读取到的长度,文件尾-1.
public int java.io.Reader.read(char[]) throws java.io.IOException

//小demo

public class BufferedWriterReaderDemo {public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new FileReader("1.txt"));BufferedWriter writer = new BufferedWriter(new FileWriter("5.txt"))){String s = null;while ((s = reader.readLine())!=null){writer.write(s);}}catch (IOException e){}}
}

StringReader和StreamWriter:

这个类似于CharArrayWriter和CharArrayReader,不过StringReader和StreamWriter是对字符串的操作,CharArrayWriter和StreamWriter是对字符的操作。是纯内存的操作,不涉及到磁盘。主要用途是在要创建临时文件,数据网络传输等可以避免访问磁盘,直接在内存操作,提高运行效率。该类也不涉及底层操作,纯java代码。这里直接就用源码来讲:

StringWriter

public class StringWriter extends Writer {//底层是StringBuffer。也就是说是线程安全的,因为写入是添加的,所以避免//String对象加号添加,提高效率。private StringBuffer buf;public StringWriter() {buf = new StringBuffer();lock = buf;}//可以设定StringBuffer的初始值public StringWriter(int initialSize) {if (initialSize < 0) {throw new IllegalArgumentException("Negative buffer size");}buf = new StringBuffer(initialSize);lock = buf;}//往缓冲区中添加一个字符public void write(int c) {buf.append((char) c);}//往缓冲区中添加一个字符数组,并可以指定偏移量和长度public void write(char cbuf[], int off, int len) {if ((off < 0) || (off > cbuf.length) || (len < 0) ||((off + len) > cbuf.length) || ((off + len) < 0)) {throw new IndexOutOfBoundsException();} else if (len == 0) {return;}buf.append(cbuf, off, len);}//往缓冲区中添加一个字符串public void write(String str) {buf.append(str);}//往缓冲区中添加一个字符串,并可以指定偏移量和长度public void write(String str, int off, int len)  {buf.append(str.substring(off, off + len));}//链式添加public StringWriter append(CharSequence csq) {if (csq == null)write("null");elsewrite(csq.toString());return this;}//链式添加,并可以指定开始和结束的偏移量public StringWriter append(CharSequence csq, int start, int end) {CharSequence cs = (csq == null ? "null" : csq);write(cs.subSequence(start, end).toString());return this;}//链式添加一个字符public StringWriter append(char c) {write(c);return this;}//获取缓冲区并转换成字符串public String toString() {return buf.toString();}//换区缓冲区public StringBuffer getBuffer() {return buf;}//刷盘,因为是纯内纯操作,所以空方法public void flush() {}//关闭流,空方法public void close() throws IOException {}}

StringReader:

public class StringReader extends Reader {//底层private String str;//长度private int length;//下一个读取的字符的游标private int next = 0;//重置时重置标记private int mark = 0;/*** 传入一个字符串*/public StringReader(String s) {this.str = s;this.length = s.length();}//是否已经打开,也就是底层str为空时就抛出异常。private void ensureOpen() throws IOException {if (str == null)throw new IOException("Stream closed");}//读取一个字符public int read() throws IOException {synchronized (lock) {ensureOpen();if (next >= length)return -1;//读取下标字符return str.charAt(next++);}}/*** 读取字符串中的字符到字符数组中,并制定偏移量和读取数据长度,不是缓冲区的* 偏移量是读取到cbuf的偏移量。*/public int read(char cbuf[], int off, int len) throws IOException {synchronized (lock) {ensureOpen();if ((off < 0) || (off > cbuf.length) || (len < 0) ||((off + len) > cbuf.length) || ((off + len) < 0)) {throw new IndexOutOfBoundsException();} else if (len == 0) {return 0;}if (next >= length)return -1;int n = Math.min(length - next, len);str.getChars(next, next + n, cbuf, off);next += n;return n;}}/*** 跳过一定长度数据,返回跳过的长度。*/public long skip(long ns) throws IOException {synchronized (lock) {ensureOpen();//达到尾,返回0.if (next >= length)return 0;// Bound skip by beginning and end of the source//取可读取长度和期望跳过长度的较小值long n = Math.min(length - next, ns);n = Math.max(-next, n);next += n;return n;}}/*** 流是否已经准备好*/public boolean ready() throws IOException {synchronized (lock) {ensureOpen();return true;}}//重置游标到mark值public void reset() throws IOException {synchronized (lock) {ensureOpen();next = mark;}}/*** 关闭流,把缓冲区置空*/public void close() {str = null;}
}

Java I/O体系之Writer和Reader详细讲解相关推荐

  1. Java I/O体系之OutputStream和InputStream详细讲解

    介绍: OutputStream是字节输出流的基类,InputStream是字节输入流的基类.这两个类的实现类基本都是成对存在的,下面就成对讲解. 字节流与字符流区别请查看:字节流与字符流区别 体系图 ...

  2. java如何爬取304_HTTP 304错误的详细讲解

    Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档).服务器告诉客户,原来缓冲的文档还可以继续使用. ...

  3. Java I/O体系详细讲解

    注意: 此文侧重讲解Java的IO主要体系,至于具体API功能会在后面的文章详细讲解. I/O以及I/O流的定义: I/O实际上是Input/Output,也就是输入/输出,输入指的是从存储设备中读取 ...

  4. 【26天高效学习Java编程】Day19:60 多个实例讲解,彻底搞懂Java 多线程 【可查阅,可复习,可面试】

    本专栏将从基础开始,循序渐进,由浅入深讲解Java的基本使用,希望大家都能够从中有所收获,也请大家多多支持. 专栏地址:26天高效学习Java编程 相关软件地址:软件地址 所有代码地址:代码地址 如果 ...

  5. Java输入/输出流体系中常用的流分类

    java输入/输出流体系中常用的流分类 分类 字节输入流 字节输出流 字符输入流 字符输出流 抽象基类 InputStream OutputStream Reader Writer 访问文件 File ...

  6. 简单的Writer和Reader

    Writer用于写出去到文件中,Reader用于将外面的文件读进来控制台 Writer和Reader有许多子类,但是子类大多都是直接运用父类Writer和Reader的方法,而且Writer和Read ...

  7. 遵循Java EE标准体系的开源GIS服务平台之二:平台部署

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  8. 遵循Java EE标准体系的开源GIS服务平台之一:平台架构

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  9. Java Web课程体系

    今天在网上看到一个java web课程体系,觉得很有意思,在这里记录下来.如下图: Java Web课程体系分为5个阶段: 1.初级阶段 2.中级阶段 3.高级阶段 4.资深阶段 5.专家阶段 Jav ...

最新文章

  1. zabbix mysql路径_ZABBIX数据库迁移目录
  2. %@taglib prefix=c uri=http://java.sun.com/jsp/jst1/core%报错
  3. semaphore的使用
  4. mysql性能仪表盘_mysql-笔记-性能
  5. [要记的]excel中把字母变为数字
  6. 什么是数据、元数据、主数据?这可能是大多数人没看过的解释
  7. mysql insert优化_如何优化MySQL insert性能
  8. 开源中文bi_odoo:免费开源ERP入门与实践
  9. 计算机硬盘的内存单位换算,内存的单位换算
  10. 迪尼斯神奇英语全32集含教材
  11. 图片转Word文档怎么转?这两种转换方法学起来
  12. 前端js使浏览器窗口全屏与退出----浏览器全屏时 监测通过esc按键退出全屏 (退出全屏时有页面上的相关处理)
  13. CSS画出半圆,四分之一圆,三角等图形
  14. 数学二、英语二、政治
  15. C++ CoreDump
  16. [SV] ignore_bins 用法
  17. facebook审核流程
  18. ZeroDivisionError:Integer division or modulo by zero
  19. 【电脑问题解决】戴尔游匣7559 16年 顶配 4k触摸屏 960m显卡 4k屏幕分辨率下使用卡顿(非使用4K分辨率进行游戏)
  20. 2004-2020年全国31省环境规制强度

热门文章

  1. VB讲课笔记09:过程
  2. 【BZOJ2342】双倍回文,manacher+并查集优化
  3. 2.图像作为函数 | 噪音概念、图像相减_7
  4. 用python语言模拟微信红包_python 模拟微信抢红包 基础语法实现demo
  5. 2017.5.6 表达式的值 思考记录
  6. [C++] GCC multilib
  7. python解释器有多大_python解释器到底是什么?
  8. 关于C语言可变参数函数的一些研究和总结
  9. linux下c 指针变量大小,C语言sizeof关键字
  10. db2连接工具_ETL工具(kettl)使用系列(一)