`
wdhdmx
  • 浏览: 300531 次
  • 性别: Icon_minigender_1
  • 来自: 山西
博客专栏
D4bbb5f7-9aa4-3e66-8194-f61b3f0241c2
天天编程
浏览量:21544
社区版块
存档分类
最新评论

字符流(二)BufferedReader和BufferedWriter源码理解

阅读更多

1.BufferedReader

1.1 继承关系

public class BufferedReader extends Reader {
//这个又是装饰模式
private Reader in;
}

1.2 构造方法

public BufferedReader(Reader in) {
	this(in, defaultCharBufferSize);
}
//默认缓存数组的大小
private static int defaultCharBufferSize = 8192;
//构造方法
public BufferedReader(Reader in, int sz) {
        //这个方法可参考前面的Writer源码,只要是将锁赋值
	super(in);
	if (sz <= 0)
	    throw new IllegalArgumentException("Buffer size <= 0");
	//装饰模式。。
	this.in = in;
	cb = new char[sz];
	nextChar = nChars = 0;
}
//两个上面用到的参数,用于缓存数据,是字符(char)数组,不是字节(byte)数组。
private char cb[];
private int nChars, nextChar;

1.3 标记有关

在看read方法之前先看一眼 标记mark有关的方法有点帮助。为看懂read做铺垫

//标记流中的当前位置,带入的参数表示标记所占的空间
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;
	}
}
//回到标记位置
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;
	}
}

 

1.4 read

这个方法中fill()是重点,有点绕,但看懂后就觉得很清晰,能完全理解bufferedReader的原理。

看完这个方法再回去看3.3的标记部分,就很容易看懂。

public int read() throws IOException {
        //锁,看来读得时候也只能一个方法读。
	synchronized (lock) {
	    //确保输入流不是空。
	    ensureOpen();
	    //这个循环和while一样。
	    for (;;) {
                //下面的判断为是否下一个读取的字符超出了缓存数组中实际包含数据的大小。
		if (nextChar >= nChars) {
                    //下一个字符超出或者等于缓存数组的大小
                    //这个是核心的方法,里面有标记的内容,详细的看下面内容。
		    fill();
                    //如果还是超出,则表示输入流读完了。
		    if (nextChar >= nChars)
			return -1;
		}
                //如果下一个字符是换行符.这个变量只有在readLine里面才变为true。和\n\r有关,可忽略。针对不同的平台的
		if (skipLF) {
		    skipLF = false;
		    if (cb[nextChar] == '\n') {
			nextChar++;
			continue;
		    }
		}
                //返回当前读的字符,并将要读字符+1
		return cb[nextChar++];
	    }
	}
}

//下面的变量是用于fill方法里的
//下面两个变量是标记的状态, -1为未启动标记,-2为标记失效。
private static final int INVALIDATED = -2;
private static final int UNMARKED = -1;
//标记的位置
private int markedChar = UNMARKED;
//nChars表示现在缓存数组中已经存在多少个字符。
//nextChar表示下一个读取的位置,从0开始,这个只是缓存数组中的位置,并不是读取流的位置。
private int nChars, nextChar;
//标记分配的空间大小。超出后,如果缓存数组重新处置,则标记失效。
private int readAheadLimit = 0;

//将字符数组读满,然后直接返回数组中的某个值。里面主要考虑的是标记的问题。
//这个和BufferedInputStream差不多,一个是byte[],这个是char[]
private void fill() throws IOException {
        //计算这次缓存数据的起始位置,起始位置之前保存的是标记的内容。
	int dst;
	if (markedChar <= UNMARKED) {
	    //这里表示没有使用标记,或者标记失效。
	    dst = 0;
	} else {
	    //表示使用标记
	    //这个变量表示标记之后实际使用了多少空间
	    int delta = nextChar - markedChar;
	    if (delta >= readAheadLimit) {
	        //如果超过了标记初始的空间。
                //标记失效
                markedChar = INVALIDATED;
                //标记空间赋0
		readAheadLimit = 0;
		//缓存数据起点0
		dst = 0;
	    } else {
	        //如果未超过标记初始的空间。
	        if (readAheadLimit <= cb.length) {
                    //分配的标记空间小于缓存数组的长度
		    //将标记后实际使用长度复制到数组的开始。
		    System.arraycopy(cb, markedChar, cb, 0, delta);
		    //将标记的位置赋0,标记所占空间仍然是原来的空间,不会缩小。
		    markedChar = 0;
		    //数据缓存的起点
		    dst = delta;
		} else {
		    //长度不够,新建一个。
		    char ncb[] = new char[readAheadLimit];
		    //和上面一样,复制标记到最前面
		    System.arraycopy(cb, markedChar, ncb, 0, delta);
		    //将引用更新
		    cb = ncb;
		    markedChar = 0;
		    dst = delta;
		}
                nextChar = nChars = delta;
	    }
	}
	//下面是读数据,读出一定长度,默认cb的长度8192,cb在BufferedReader中是唯一的缓存数组。
	int n;
	//这个地方读的方法中不可能返回0.所以只会执行一次
	do {
	    //从标签之后读,读满cb字符数组,注意,这里是调用in的读方法。
	    n = in.read(cb, dst, cb.length - dst);
	} while (n == 0);
        //读到数据的情况,没有读到的话就不做任何操作。
	if (n > 0) {
            //现在缓存空间中已有的真实缓存数量
	    nChars = dst + n;
            //下一个读取的位置。
	    nextChar = dst;
	}
}

其它的read方法和这个类似。

一次读出很多字符的时候,处理的策略是:

a.缓存数组不够,就用in直接读,不经过缓存.

b.缓存数组够,就将缓存中读出。

c.缓存数组够,但读完后还没读满,则继续从in中接着读,不够的部分不过缓存数组。

 

1.5 readLine

这个是用的比较多的方法,所以列出来。这个方法在有上面的基础上,还是很好懂的。

String readLine(boolean ignoreLF) throws IOException {
        //传入的布尔值默认为false
	StringBuffer s = null;
	int startChar;

        synchronized (lock) {
            ensureOpen();
	    boolean omitLF = ignoreLF || skipLF;
        //这个是什么?goto?
	bufferLoop:
            //while
	    for (;;) {
                //下一个字符超出缓存数组大小,这里nextChar是从0开始的,所以相等的时候就代表已经超出了缓存数组范围。
		if (nextChar >= nChars)
		    fill();
		//下面的if是判断流的末尾,读完了就返回null,或者将之前读的内容返回
		if (nextChar >= nChars) {
		    if (s != null && s.length() > 0)
			return s.toString();
		    else
			return null;
		}
		//表示没有到末尾.
		boolean eol = false;
		char c = 0;
		int i;
                //这个是处理\r\n的情况,不进行两次判断,忽略
		if (omitLF && (cb[nextChar] == '\n')) 
                    nextChar++;
		skipLF = false;
		omitLF = false;

	    charLoop:
		//遍历缓存数组,直到\n或者\r
		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);
	    }
        }
    }

1.6 其它方法

skip 就是先把缓存数组中跳过去,如果缓存数组不够,就再将数据读入缓存数组,再跳,一直循环。

ready 表示缓存是否读完了,没什么用处。

markSupported 是否支持标记

close 流等需要关闭的东西都关闭。

 

2.BufferedWriter

2.1 继承关系

public class BufferedWriter extends Writer {
    //装饰模式
    private Writer out;
}

2.2 构造函数

将缓存数组初始化,并且根据平台初始化换行符号。

    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;
        //获取换行的符号\n \r,和方法System.getProperty("line.separator")一样
	lineSeparator =	(String) java.security.AccessController.doPrivileged(
               new sun.security.action.GetPropertyAction("line.separator"));
    }

2.3 write有关

public void write(int c) throws IOException {
	synchronized (lock) {
            //判断是否有输出流
	    ensureOpen();
            //如果缓存数组写满了,就flush数组。
	    if (nextChar >= nChars)
		flushBuffer();
            //将内容写入缓存数组中
	    cb[nextChar++] = (char) c;
	}
}
//flush,这个方法就知道flush的重要性,
void flushBuffer() throws IOException {
	synchronized (lock) {
	    ensureOpen();
	    if (nextChar == 0)
		return;
            //将数据写入流
	    out.write(cb, 0, nextChar);
	    nextChar = 0;
	}
}

用的比较多的写字符串。就是将字符串转变成字符数组。

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

2.4 其它

a.writeLine 写一个换行

public void newLine() throws IOException {
        //同样写到缓存数组里
	write(lineSeparator);
}

b.flush,这个也不多说了。

public void flush() throws IOException {
	synchronized (lock) {
	    flushBuffer();
	    out.flush();
	}
}

c.close 关闭所有该关闭的.

public void close() throws IOException {
	synchronized (lock) {
	    if (out == null) {
		return;
	    }
	    try {
                //最后还释放了一次。不过没有执行flush方法,所以在close前还是要执行一次flush!
	        flushBuffer();
	    } finally {
	        out.close();
	        out = null;
	        cb = null;
	    }
	}
}

3.结束

看完这个字符流,清晰了好多,开始看的比较慢,但是后来越来越快,水到渠成。

 

 

 

 

 

 

1
0
分享到:
评论

相关推荐

    BufferedReader 和BufferedWriter 实例

    NULL 博文链接:https://lisanlai.iteye.com/blog/673253

    Java中BufferedReader与BufferedWriter类的使用示例

    BufferedReader与BufferedWriter分别继承于Reader和Writer类,分别为字符的读取和写入添加缓冲功能,这里我们就来看一下Java中BufferedReader与BufferedWriter类的使用示例:

    文件读写 BufferedReader BufferedWriter 示例 去除代码后面多余空格

    文件读写 BufferedReader BufferedWriter 去除代码后面空格 简单 readLine newLine flush

    IO基础(字符流)

    文章目录IO基础(字符流)一、字符流二、常用的字符流类的继承关系三、字符流 FileReader 和 FileWriter四、字符缓冲流 BufferedReader 和 BufferedWriter五、 字符字节转换流(InputStreamReader/...

    用java IO流复制文本内容(包含中文字符)

    BufferedReader/BufferedWriter 实现从字节流到字符流的读取。代码用来实现文本内容的复制!

    Java BufferedWriter BufferedReader 源码分析

    本文是关于Java BufferedWriter ,BufferedReader 简介、分析源码 对Java IO 流深入了解,希望看到的同学对你有所帮助

    文件流的读取与写入

    这是一个文件流的读取与写入程序(内部是源码) 是利用BufferedReader 和 BufferedWriter 来进行读取与写入的

    IO流中数据传递的字节流和字符流

    IO的字符和字节流的运用字符流(适用于中文内容读入写出)读入:FileReader\BufferedReader写出:FileWriter\BufferedWriter字节流(适用于少量中文;主用图片,视频)第一种:用 FIle类搭配 FIleOutputStream的方法进行...

    java入门之io包

    运用File类进行文件操作 理解流,标准输入/输出流的概念 运用FileInputStream和FileOutputStream类读写...运用BufferedReader和BufferedWriter类读写文本文件 运用DataInputStream和DataOutputStream类 读写二进制文件

    java学习SWING

    SWING-III.pptJava学习资料 运用File类进行文件操作 理解流,标准输入/输出流的概念 ...运用BufferedReader和BufferedWriter类读写文本文件 运用DataInputStream和DataOutputStream类 读写二进制文件

    javaIo与流 读写文件

    1、在读写文件时必须的 2、File类 3、流 4、读写文件 运用FileInputStream和FileOutputStream类读写文本...运用BufferedReader和BufferedWriter类读写文本文件 运用DataInputStream和DataOutputStream类读写二进制 文件

    BufferedReader 介绍_动力节点Java学院整理

    BufferedReader 是缓冲字符输入流。它继承于Reader。 BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。

    JAVA IO流缓冲字节流缓冲字符流等流经典代码示例加注释总结.rar

    2、常用21个IO流:FileWriter、FileReader、CharArrayReader、CharArrayWriter、CharSequence、OutputStreamWriter、FileOutputStream、InputStreamReader、PrintWriter、BufferedReader、InputStream、...

    基础深化和提高-常用类

    FileInputStream、FileOutputStream、BufferedReader、BufferedWriter等:用于进行文件和流的输入输出操作,可以读取、写入文件和处理数据流。 字符串处理类: String、StringBuffer、StringBuilder等:用于处理...

    Java 详解BufferedReader

    详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader详解BufferedReader

    Java文件读写操作函数实现

    * InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。 * * 构造方法: * * InputStreamReader isr = new ...

    BufferedReader 结构

    BufferedReader 开发的结构

    java IO流学习笔记——(3)字节缓冲流&字符缓冲流&转换流&数据流

    目录java IO流学习笔记——(3)字节缓冲流&字符缓冲流&转换流&数据流字节缓冲流 —BufferedInputStream&BufferedOutputStream字符缓冲流—BufferedReader&BufferedWriter转换流—InputStreamReader&...

    java.io常用方法(基本)

    我自己写的一些关于IO中的见解,可以用来参考。 File的增、删、改、查,FileReader和FileWriter类,BufferedReader和BufferedWriter类,ObjectOutputStream和ObjectInputStream类

    java io包课件

    了解java.io包 ...运用FileReader类和FileWriter类配合BufferedReader类和BufferedWriter类读/写字符文件 使用BufferedReader类从控制台接受输入 运用DataInputStream类和DataOutputStream类读写数据文件

Global site tag (gtag.js) - Google Analytics