JavaIo(3) - Writer

xiaoxiao2025-04-06  31

文章目录

Writer源码:二,OutputStreamWriter源码BufferedWriter源码 设计Reader和Writer继承层次结构主要是为了国际化。老的IO流继承层次结构仅支持8位的字节流,并且不能很好的处理16位的Unicode字符,所以添加Reader和Writer继承层次结构就是为了在所有的IO操作中都支持Unicode。

然而在某些场合,我们不得不面临着字符编码的问题,即字符和字节之间按照什么编码方式(GBK,UTF-8,ISO-8859-1等)来编解码的问题。这时我们将用到OutputStreamWriter和InputStreamReader这两个字符流,它们分别是字符通向字节的桥梁和字节通向字符的桥梁。 关于**“装饰者模式”**,字符流Reader和Writer的类继承层次结构沿用与字节流相同的思想,但是并不完全相同。造成这种差异的原因是因为类的组织形式不同,尽管BufferedOutputStream是FilterOutputStream的子类,但是BufferedWriter并不是FilterWriter的子类,但它仍然是其他Writer类的装饰器类。尽管FilterWriter是抽象类,但是没有任何子类,把它放在那里也只是把它作为一个占位符,或者仅仅让我们不会对它所在的地方产生疑惑。

Writer源码:

package java.io; /** * Writer是写入字符流的抽象类。定义了流的最基本的功能。 * 子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。 * 但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 */ public abstract class Writer implements Appendable, Closeable, Flushable { /** * 字符buffer */ private char[] writeBuffer; /** * 字符buffer的默认大小 */ private static final int WRITE_BUFFER_SIZE = 1024; /** * 用于同步针对此流的操作的对象 */ protected Object lock; /** * 构造方法1,使用本类对象锁 */ protected Writer() { this.lock = this; } /** * 构造方法2,使用指定的锁对象 */ protected Writer(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; } /** * 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 * 用于支持高效单字符输出的子类应重写此方法。 */ public void write(int c) throws IOException { synchronized (lock) { // write()方法内是同步代码块 if (writeBuffer == null){ writeBuffer = new char[WRITE_BUFFER_SIZE]; } writeBuffer[0] = (char) c; write(writeBuffer, 0, 1); } } /** * 写入字符数组。 */ public void write(char cbuf[]) throws IOException { write(cbuf, 0, cbuf.length); } /** * 写入字符数组的某一部分。子类需实现该方法 */ abstract public void write(char cbuf[], int off, int len) throws IOException; /** * 写入字符串。 */ public void write(String str) throws IOException { write(str, 0, str.length()); } /** * 写入字符串的某一部分。 */ 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); write(cbuf, 0, len); } } /** * 将指定字符序列添加到此 writer */ public Writer append(CharSequence csq) throws IOException { if (csq == null) write("null"); else write(csq.toString()); return this; } /** * 将指定字符序列的子序列添加到此 writer。 */ public Writer append(CharSequence csq, int start, int end) throws IOException { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } /** * 将指定字符添加到此 writer。 */ public Writer append(char c) throws IOException { write(c); return this; } /** * 刷新该流的缓冲。 */ abstract public void flush() throws IOException; /** * 关闭此流,但要先刷新它。 * 在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException。 */ abstract public void close() throws IOException; }

二,OutputStreamWriter源码

package java.io; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import sun.nio.cs.StreamEncoder; /** * OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。 * 它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 * 每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。 * 在写入底层输出流之前,得到的这些字节将在缓冲区中累积。 * 为了获得最高效率,可考虑将OutputStreamWriter包装到BufferedWriter中,以避免频繁调用转换器。 * 例如:Writer out = new BufferedWriter(new OutputStreamWriter(System.out)); */ public class OutputStreamWriter extends Writer { /** * 本类所实现的由字符到字节的编码严重依赖StreamEncoder类及其方法。 * 本文并不打算讲解StreamEncoder类,只介绍其方法达到什么目的。 */ private final StreamEncoder se; /** * 创建使用指定字符集的 OutputStreamWriter。 */ public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException { super(out); if (charsetName == null) throw new NullPointerException("charsetName"); se = StreamEncoder.forOutputStreamWriter(out, this, charsetName); } /** * 创建使用默认字符编码的 OutputStreamWriter */ public OutputStreamWriter(OutputStream out) { super(out); try { se = StreamEncoder.forOutputStreamWriter(out, this, (String)null); } catch (UnsupportedEncodingException e) { throw new Error(e); } } /** * 创建使用给定字符集的 OutputStreamWriter。 */ public OutputStreamWriter(OutputStream out, Charset cs) { super(out); if (cs == null) throw new NullPointerException("charset"); se = StreamEncoder.forOutputStreamWriter(out, this, cs); } /** * 创建使用给定字符集编码器的 OutputStreamWriter。 */ public OutputStreamWriter(OutputStream out, CharsetEncoder enc) { super(out); if (enc == null) throw new NullPointerException("charset encoder"); se = StreamEncoder.forOutputStreamWriter(out, this, enc); } /** * 返回此流使用的字符编码的名称。 */ public String getEncoding() { return se.getEncoding(); } /** * 刷新buffer */ void flushBuffer() throws IOException { se.flushBuffer(); } /** * 写入单个字符。 */ public void write(int c) throws IOException { se.write(c); } /** * 写入字符数组的某一部分。 */ public void write(char cbuf[], int off, int len) throws IOException { se.write(cbuf, off, len); } /** * 写入字符串的某一部分。 */ public void write(String str, int off, int len) throws IOException { se.write(str, off, len); } /** * 刷新该流的缓冲。 */ public void flush() throws IOException { se.flush(); } /** * 关闭此流,但要先刷新它。 */ public void close() throws IOException { se.close(); } }

BufferedWriter源码

package java.io; /** * 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 * 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。 * 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念。 * */ public class BufferedWriter extends Writer { private Writer out; // 持有父类对象 private char cb[]; // buffer private int nChars, nextChar; private static int defaultCharBufferSize = 8192; // 默认buffer大小 /** * 换行符 */ private String lineSeparator; /** * 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 */ public BufferedWriter(Writer out) { this(out, defaultCharBufferSize); } /** * 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 */ public BufferedWriter(Writer out, int sz) { super(out); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); this.out = out; cb = new char[sz]; nChars = sz; nextChar = 0; lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); } /** 检查流是否打开 */ private void ensureOpen() throws IOException { if (out == null) throw new IOException("Stream closed"); } /** * 刷新缓冲区。 */ void flushBuffer() throws IOException { synchronized (lock) { ensureOpen(); if (nextChar == 0) return; out.write(cb, 0, nextChar); nextChar = 0; } } /** * 写入单个字符。 * 底层调用了Writer类型的write()方法,但是本类方法为其进行了"装饰",即加入了缓冲区技术。 */ public void write(int c) throws IOException { synchronized (lock) { ensureOpen(); if (nextChar >= nChars) flushBuffer(); cb[nextChar++] = (char) c; } } /** * 内部方法 */ private int min(int a, int b) { if (a < b) return a; return b; } /** * 写入字符数组的某一部分。 同样对老write()方法进行了"缓冲区装饰" */ public void write(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; } if (len >= nChars) { /* If the request length exceeds the size of the output buffer, flush the buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ flushBuffer(); out.write(cbuf, off, len); return; } int b = off, t = off + len; while (b < t) { int d = min(nChars - nextChar, t - b); System.arraycopy(cbuf, b, cb, nextChar, d); b += d; nextChar += d; if (nextChar >= nChars) flushBuffer(); } } } /** * 写入字符串的某一部分。同样对老write()方法进行了"缓冲区装饰"。 */ public void write(String s, int off, int len) throws IOException { synchronized (lock) { ensureOpen(); int b = off, t = off + len; while (b < t) { int d = min(nChars - nextChar, t - b); s.getChars(b, b + d, cb, nextChar); b += d; nextChar += d; if (nextChar >= nChars) flushBuffer(); } } } /** * 写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义 */ public void newLine() throws IOException { write(lineSeparator); } /** * 刷新该流的缓冲。 */ public void flush() throws IOException { synchronized (lock) { flushBuffer(); out.flush(); } } /** * 关闭此流,但要先刷新它。 */ @SuppressWarnings("try") public void close() throws IOException { synchronized (lock) { if (out == null) { return; } try (Writer w = out) { flushBuffer(); } finally { out = null; cb = null; } } } }

一般来说,FileWriter, FileReader的操作:

File file = new File("testWriter.txt"); String a = "这是字符型"; FileWriter out = new FileWriter( file ); out.write( a,0,a.length() ); out.write("\r\n"); out.close(); char[] b = new char[10]; FileReader in = new FileReader( file ); in.read( b ); System.out.println(b); in.close();

更常用的是:

FileWriter outOne = new FileWriter( file ,true ); BufferedWriter out2 = new BufferedWriter( outOne ); String a2 = "缓冲区字符输入更高效"; out2.write( a2 ); out2.newLine(); out2.write("测试换行行不行"); out2.close(); BufferedReader in2 = new BufferedReader( new FileReader(file) ); String str; while( (str = in2.readLine()) != null ) { System.out.println(str); }
转载请注明原文地址: https://www.6miu.com/read-5027602.html

最新回复(0)