一:BufferedWriter

1、类功能简介:

BufferedWriter、缓存字符输出流、他的功能是为传入的底层字符输出流提供缓存功能、同样当使用底层字符输出流向目的地中写入字符或者字符数组时、每写入一次就要打开一次到目的地的连接、这样频繁的访问不断效率底下、也有可能会对存储介质造成一定的破坏、比如当我们向磁盘中不断的写入字节时、夸张一点、将一个非常大单位是G的字节数据写入到磁盘的指定文件中的、没写入一个字节就要打开一次到这个磁盘的通道、这个结果无疑是恐怖的、而当我们使用BufferedWriter将底层字符输出流、比如FileReader包装一下之后、我们可以在程序中先将要写入到文件中的字符写入到BufferedWriter的内置缓存空间中、然后当达到一定数量时、一次性写入FileReader流中、此时、FileReader就可以打开一次通道、将这个数据块写入到文件中、这样做虽然不可能达到一次访问就将所有数据写入磁盘中的效果、但也大大提高了效率和减少了磁盘的访问量!这就是其意义所在、 他的具体工作原理在这里简单提一下:这里可能说的比较乱、具体可以看源码、不懂再回头看看这里、当程序中每次将字符或者字符数组写入到BufferedWriter中时、都会检查BufferedWriter中的缓存字符数组buf(buf的大小是默认的或者在创建bw时指定的、一般使用默认的就好)是否存满、如果没有存满则将字符写入到buf中、如果存满、则调用底层的writer(char[] b, int off, int len)将buf中的所有字符一次性写入到底层out中、如果写入的是字符数组、如果buf中已满则同上面满的时候的处理、如果能够存下写入的字符数组、则存入buf中、如果存不下、并且要写入buf的字符个数小于buf的长度、则将buf中所有字符写入到out中、然后将要写入的字符存放到buf中(从下标0开始存放)、如果要写入out中的字符超过buf的长度、则直接写入out中、

2、BufferedWriter  API简介:

A:关键字

private Writer out; 底层字符输出流

private char cb[]; 缓冲数组

private int nChars, nextChar; nChars--cb的size,nextChar--cb中下一个字符的下标

private static int defaultCharBufferSize = 8192; 默认cb大小

private String lineSeparator;换行符、用于newLine方法。不同平台具有不同的值。

B:构造方法

BufferedWriter(Writer out)使用默认cb大小创建BufferedWriter bw。

BufferedWriter(Writer out, int sz)使用默认cb大小创建BufferedWriter bw。

C:一般方法

void close()关闭此流、释放与此流有关的资源。

void flushBuffer()将cb中缓存的字符flush到底层out中、

void flush()刷新此流、同时刷新底层out流

void newLine()写入一个换行符。

void write(int c)将一个单个字符写入到cb中。

void write(char cbuf[], int off, int len)将一个从下标off开始长度为len个字符写入cb中

void write(String s, int off, int len)将一个字符串的一部分写入cb中

3、源码分析

package com.chy.io.original.code;

import java.io.IOException;

import java.io.PrintWriter;

/**

* 为字符输出流提供缓冲功能、提高效率。可以使用指定字符缓冲数组大小也可以使用默认字符缓冲数组大小。

*/

public class BufferedWriter extends Writer {

//底层字符输出流

private Writer out;

//缓冲数组

private char cb[];

//nChars--cb中总的字符数,nextChar--cb中下一个字符的下标

private int nChars, nextChar;

//默认cb大小

private static int defaultCharBufferSize = 8192;

/**

* Line separator string. This is the value of the line.separator

* property at the moment that the stream was created.

* 换行符、用于newLine方法。不同平台具有不同的值。

*/

private String lineSeparator;

/**

* 使用默认cb大小创建BufferedWriter bw。

*/

public BufferedWriter(Writer out) {

this(out, defaultCharBufferSize);

}

/**

* 使用指定cb大小创建br、初始化相关字段

*/

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 =(String) java.security.AccessController.doPrivileged(

new sun.security.action.GetPropertyAction("line.separator"));

}

/** 检测底层字符输出流是否关闭*/

private void ensureOpen() throws IOException {

if (out == null)

throw new IOException("Stream closed");

}

/**

* 将cb中缓存的字符flush到底层out中、但是不flush底层out中的字符。

* 并且将cb清空。

*/

void flushBuffer() throws IOException {

synchronized (lock) {

ensureOpen();

if (nextChar == 0)

return;

out.write(cb, 0, nextChar);

nextChar = 0;

}

}

/**

* 将一个单个字符写入到cb中。

*/

public void write(int c) throws IOException {

synchronized (lock) {

ensureOpen();

if (nextChar >= nChars)

flushBuffer();

cb[nextChar++] = (char) c;

}

}

/**

* Our own little min method, to avoid loading java.lang.Math if we've run

* out of file descriptors and we're trying to print a stack trace.

*/

private int min(int a, int b) {

if (a < b) return a;

return b;

}

/**

* 将一个从下标off开始长度为len个字符写入cb中

*/

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) {

/* 如果len大于cb的长度、那么就直接将cb中现有的字符和cbuf中的字符写入out中、

* 而不是写入cb、再写入out中 。

*/

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();

}

}

}

/**

* 将一个字符串的一部分写入cb中

*/

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();

}

}

}

/**

* 写入一个换行符。

*/

public void newLine() throws IOException {

write(lineSeparator);

}

/**

* 刷新此流、同时刷新底层out流

*/

public void flush() throws IOException {

synchronized (lock) {

flushBuffer();

out.flush();

}

}

/**

* 关闭此流、释放与此流有关的资源。

*/

public void close() throws IOException {

synchronized (lock) {

if (out == null) {

return;

}

try {

flushBuffer();

} finally {

out.close();

out = null;

cb = null;

}

}

}

}

4、实例演示:与下面的BufferedReader结合使用实现字符类型的文件的拷贝。

二:BufferedReader

1、类功能简介:

缓冲字符输入流、他的功能是为传入的底层字符输入流提供缓冲功能、他会通过底层字符输入流(in)中的字符读取到自己的buffer中(内置缓存字符数组)、然后程序调用BufferedReader的read方法将buffer中的字符读取到程序中、当buffer中的字符被读取完之后、BufferedReader会从in中读取下一个数据块到buffer中供程序读取、直到in中数据被读取完毕、这样做的好处一是提高了读取的效率、二是减少了打开存储介质的连接次数、详细的原因下面BufferedWriter有说到。其有个关键的方法fill()就是每当buffer中数据被读取完之后从in中将数据填充到buffer中、程序从内存中读取数据的速度是从磁盘中读取的十倍!这是一个很恐怖的效率的提升、同时我们也不能无禁止的指定BufferedReader的buffer大小、毕竟、一次性读取in中耗时较长、二是内存价格相对昂贵、我们能做的就是尽量在其中找到合理点。一般也不用我们费这个心、创建BufferedReader时使用buffer的默认大小就好。

2、BufferedReader  API简介:

A:构造方法

BufferedReader(Reader in, int sz)根据指定大小和底层字符输入流创建BufferedReader。br

BufferedReader(Reader in)使用默认大小创建底层输出流的缓冲流

B:一般方法

void close()关闭此流、释放与此流有关的所有资源

void mark(int readAheadLimit)标记此流此时的位置

boolean markSupported()判断此流是否支持标记

void reset()重置in被最后一次mark的位置

boolean ready()判断此流是否可以读取字符

int read()读取单个字符、以整数形式返回。如果读到in的结尾则返回-1。

int read(char[] cbuf, int off, int len)将in中len个字符读取到cbuf从下标off开始长度len中

String readLine()读取一行

long skip(long n)丢弃in中n个字符

3、源码分析

package com.chy.io.original.code;

import java.io.IOException;

/**

* 为底层字符输入流添加字符缓冲cb数组。提高效率

* @version 1.1, 13/11/17

* @authorandyChen

*/

public class BufferedReader extends Reader {

private Reader in;

private char cb[];

private int nChars, nextChar;

private static final int INVALIDATED = -2;

private static final int UNMARKED = -1;

private int markedChar = UNMARKED;

private int readAheadLimit = 0; /* Valid only when markedChar > 0 */

/** If the next character is a line feed, skip it */

private boolean skipLF = false;

/** The skipLF flag when the mark was set */

private boolean markedSkipLF = false;

private static int defaultCharBufferSize = 8192;

private static int defaultExpectedLineLength = 80;

/**

* 根据指定大小和底层字符输入流创建BufferedReader。br

*/

public BufferedReader(Reader in, int sz) {

super(in);

if (sz <= 0)

throw new IllegalArgumentException("Buffer size <= 0");

this.in = in;

cb = new char[sz];

nextChar = nChars = 0;

}

/**

* 使用默认大小创建底层输出流的缓冲流

*/

public BufferedReader(Reader in) {

this(in, defaultCharBufferSize);

}

/** 检测底层字符输入流in是否关闭 */

private void ensureOpen() throws IOException {

if (in == null)

throw new IOException("Stream closed");

}

/**

* 填充cb。

*/

private void fill() throws IOException {

int dst;

if (markedChar <= UNMARKED) {

/* No mark */

dst = 0;

} else {

/* Marked */

int delta = nextChar - markedChar;

if (delta >= readAheadLimit) {

/* Gone past read-ahead limit: Invalidate mark */

markedChar = INVALIDATED;

readAheadLimit = 0;

dst = 0;

} else {

if (readAheadLimit <= cb.length) {

/* Shuffle in the current buffer */

System.arraycopy(cb, markedChar, cb, 0, delta);

markedChar = 0;

dst = delta;

} else {

/* Reallocate buffer to accommodate read-ahead limit */

char ncb[] = new char[readAheadLimit];

System.arraycopy(cb, markedChar, ncb, 0, delta);

cb = ncb;

markedChar = 0;

dst = delta;

}

nextChar = nChars = delta;

}

}

int n;

do {

n = in.read(cb, dst, cb.length - dst);

} while (n == 0);

if (n > 0) {

nChars = dst + n;

nextChar = dst;

}

}

/**

* 读取单个字符、以整数形式返回。如果读到in的结尾则返回-1。

*/

public int read() throws IOException {

synchronized (lock) {

ensureOpen();

for (;;) {

if (nextChar >= nChars) {

fill();

if (nextChar >= nChars)

return -1;

}

if (skipLF) {

skipLF = false;

if (cb[nextChar] == '\n') {

nextChar++;

continue;

}

}

return cb[nextChar++];

}

}

}

/**

* 将in中len个字符读取到cbuf从下标off开始长度len中

*/

private int read1(char[] cbuf, int off, int len) throws IOException {

if (nextChar >= nChars) {

/* If the requested length is at least as large as the buffer, and

if there is no mark/reset activity, and if line feeds are not

being skipped, do not bother to copy the characters into the

local buffer. In this way buffered streams will cascade

harmlessly. */

if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {

return in.read(cbuf, off, len);

}

fill();

}

if (nextChar >= nChars) return -1;

if (skipLF) {

skipLF = false;

if (cb[nextChar] == '\n') {

nextChar++;

if (nextChar >= nChars)

fill();

if (nextChar >= nChars)

return -1;

}

}

int n = Math.min(len, nChars - nextChar);

System.arraycopy(cb, nextChar, cbuf, off, n);

nextChar += n;

return n;

}

/**

* 将in中len个字符读取到cbuf从下标off开始长度len中

*/

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;

}

int n = read1(cbuf, off, len);

if (n <= 0) return n;

while ((n < len) && in.ready()) {

int n1 = read1(cbuf, off + n, len - n);

if (n1 <= 0) break;

n += n1;

}

return n;

}

}

/**

* 从in中读取一行、是否忽略换行符

*/

String readLine(boolean ignoreLF) throws IOException {

StringBuffer s = null;

int startChar;

synchronized (lock) {

ensureOpen();

boolean omitLF = ignoreLF || skipLF;

bufferLoop:

for (;;) {

if (nextChar >= nChars)

fill();

if (nextChar >= nChars) { /* EOF */

if (s != null && s.length() > 0)

return s.toString();

else

return null;

}

boolean eol = false;

char c = 0;

int i;

/* Skip a leftover '\n', if necessary */

if (omitLF && (cb[nextChar] == '\n'))

nextChar++;

skipLF = false;

omitLF = false;

charLoop:

for (i = nextChar; i < nChars; i++) {

c = cb[i];

if ((c == '\n') || (c == '\r')) {

eol = true;

break charLoop;

}

}

startChar = nextChar;

nextChar = i;

if (eol) {

String str;

if (s == null) {

str = new String(cb, startChar, i - startChar);

} else {

s.append(cb, startChar, i - startChar);

str = s.toString();

}

nextChar++;

if (c == '\r') {

skipLF = true;

}

return str;

}

if (s == null)

s = new StringBuffer(defaultExpectedLineLength);

s.append(cb, startChar, i - startChar);

}

}

}

/**

* 从in中读取一行、

*/

public String readLine() throws IOException {

return readLine(false);

}

/**

* 丢弃in中n个字符

*/

public long skip(long n) throws IOException {

if (n < 0L) {

throw new IllegalArgumentException("skip value is negative");

}

synchronized (lock) {

ensureOpen();

long r = n;

while (r > 0) {

if (nextChar >= nChars)

fill();

if (nextChar >= nChars)/* EOF */

break;

if (skipLF) {

skipLF = false;

if (cb[nextChar] == '\n') {

nextChar++;

}

}

long d = nChars - nextChar;

if (r <= d) {

nextChar += r;

r = 0;

break;

}

else {

r -= d;

nextChar = nChars;

}

}

return n - r;

}

}

/**

* 判断cb中是否为空、或者底层in中是否有可读字符。

*/

public boolean ready() throws IOException {

synchronized (lock) {

ensureOpen();

/*

* If newline needs to be skipped and the next char to be read

* is a newline character, then just skip it right away.

*/

if (skipLF) {

/* Note that in.ready() will return true if and only if the next

* read on the stream will not block.

*/

if (nextChar >= nChars && in.ready()) {

fill();

}

if (nextChar < nChars) {

if (cb[nextChar] == '\n')

nextChar++;

skipLF = false;

}

}

return (nextChar < nChars) || in.ready();

}

}

/**

* 判断此流是否支持标记

*/

public boolean markSupported() {

return true;

}

/**

* 标记此流此时的位置、当调用reset方法失效前最多允许读取readAheadLimit个字符。

*/

public void mark(int readAheadLimit) throws IOException {

if (readAheadLimit < 0) {

throw new IllegalArgumentException("Read-ahead limit < 0");

}

synchronized (lock) {

ensureOpen();

this.readAheadLimit = readAheadLimit;

markedChar = nextChar;

markedSkipLF = skipLF;

}

}

/**

* 重置in被最后一次mark的位置。即下一个字符从被最后一次mark的位置开始读取。

*/

public void reset() throws IOException {

synchronized (lock) {

ensureOpen();

if (markedChar < 0)

throw new IOException((markedChar == INVALIDATED)

? "Mark invalid"

: "Stream not marked");

nextChar = markedChar;

skipLF = markedSkipLF;

}

}

//关闭此流、释放与此流有关的所有资源

public void close() throws IOException {

synchronized (lock) {

if (in == null)

return;

in.close();

in = null;

cb = null;

}

}

}

4、实例演示:

package com.chy.io.original.test;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public class BufferedWriterAndBufferedReaderTest {

/**

* 这里对这两个类的测试比较简单、就是对文件字符流进行包装、实现文件拷贝

* 有兴趣的可以测试一下效率、、偷个懒、、可无视

*/

public static void main(String[] args) throws IOException{

File resouceFile = new File("D:\\test.txt");

File targetFile = new File("E:\\copyOftest.txt");

BufferedReader br = new BufferedReader(new FileReader(resouceFile));

BufferedWriter bw = new BufferedWriter(new FileWriter(targetFile));

char[] cbuf = new char[1024];

int n = 0;

while((n = br.read(cbuf)) != -1){

bw.write(cbuf, 0, n);

}

//不要忘记刷新和关闭流、否则一方面资源没有及时释放、另一方面有可能照成数据丢失

br.close();

bw.flush();

bw.close();

}

}

总结:

对于BufferedReader、BufferedWriter、本质就是为底层字符输入输出流添加缓冲功能、先将底层流中的要读取或者要写入的数据先以一次读取一组的形式来讲数据读取或者写入到buffer中、再对buffer进行操作、这样不但效率、还能节省资源。最后、在程序中、出于效率的考虑、也应为低级流使用这两个类进行装饰一下、而不是直接拿着流直接上、觉得能实现就行。

java bufferedwrite_Java BufferedWriter BufferedReader 源码分析相关推荐

  1. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  2. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  3. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  4. 【死磕 Java 集合】— LinkedTransferQueue源码分析

    [死磕 Java 集合]- LinkedTransferQueue源码分析 问题 (1)LinkedTransferQueue是什么东东? (2)LinkedTransferQueue是怎么实现阻塞队 ...

  5. 死磕Java集合之BitSet源码分析(JDK18)

    死磕Java集合之BitSet源码分析(JDK18) 文章目录 死磕Java集合之BitSet源码分析(JDK18) 简介 继承体系 存储结构 源码解析 属性 构造方法 set(int bitInde ...

  6. java arraydeque_死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  7. Java集合:Hashtable源码分析

    1. 概述 上次讨论了HashMap的结构,原理和实现,本文来对Map家族的另外一个常用集合HashTable进行介绍.HashTable和HashMap两种集合非常相似,经常被各种面试官问到两者的区 ...

  8. Java并发编程-ReentrantLock源码分析

    一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...

  9. java ee是什么_死磕 java集合之HashSet源码分析

    问题 (1)集合(Collection)和集合(Set)有什么区别? (2)HashSet怎么保证添加元素不重复? (3)HashSet是否允许null元素? (4)HashSet是有序的吗? (5) ...

最新文章

  1. android studio类重复,Android Studio 解决重复依赖
  2. 【转载】Linux下安装、配置、启动Apache
  3. Multi-band Blending
  4. 浅析:精密空调能耗超高的原因!
  5. 函数和构造函数的区别
  6. Verilog定义计算位宽的函数clogb2
  7. Rails安全导读【完】
  8. HDoj-1874-畅通project续-Dijkstra算法
  9. micropython 网络驱动_network_网卡驱动
  10. LeetCode 2192. 有向无环图中一个节点的所有祖先(拓扑排序)
  11. 计算机专业买win,新买的电脑是win10系统,有的人却费尽心思重装成win7,为什么呢...
  12. 字节跳动专家会_年薪30万60万!字节跳动招这个语系的语言专家!
  13. 次数匹配(不需要在前面加“\”)
  14. mysql命令语句来去除掉字段中空格字符的方法
  15. python处理wrf气象数据_气象数据处理:NetCDF文件处理
  16. rj45插座尺寸图_详细介绍RJ45模块(附图解)
  17. unity overrideSprite的使用
  18. Chrome代理设置
  19. 今日分享-ios蓝牙
  20. linux 下查看知网caj文件的方法

热门文章

  1. odbc 函数序列错误_python时间序列:移动窗口函数前篇
  2. ggplot2 图形排版:patchwork 包简单入门
  3. Ensemble-BioMart:得到基因注释信息(有参考基因组的物种)
  4. R语言与数据分析(11)R语言的数据结构
  5. FM:西南民大朱英等-肠道菌群研究促进了朱鹮的迁地保护
  6. 16S预测细菌表型-bugbase:革兰氏阴阳、生物膜、致病力、移动元件、氧气消耗等...
  7. 纯生信发ISME的一次试炼
  8. 基于R的混合线性模型的实现
  9. UCL葡萄酒(red white wine quality)数据集字段解释、数据导入实战
  10. pandas删除dataframe中行内容全是0的数据行(remove rows with all zeros in dataframe)