Java BufferedWriter BufferedReader 源码分析
一:BufferedWriter
1、类功能简介:
BufferedWriter、缓存字符输出流、他的功能是为传入的底层字符输出流提供缓存功能、同样当使用底层字符输出流向目的地中写入字符或者字符数组时、每写入一次就要打开一次到目的地的连接、这样频繁的访问不断效率底下、也有可能会对存储介质造成一定的破坏、比如当我们向磁盘中不断的写入字节时、夸张一点、将一个非常大单位是G的字节数据写入到磁盘的指定文件中的、没写入一个字节就要打开一次到这个磁盘的通道、这个结果无疑是恐怖的、而当我们使用BufferedWriter将底层字符输出流、比如FileReader包装一下之后、我们可以在程序中先将要写入到文件中的字符写入到BufferedWriter的内置缓存空间中、然后当达到一定数量时、一次性写入FileReader流中、此时、FileReader就可以打开一次通道、将这个数据块写入到文件中、这样做虽然不可能达到一次访问就将所有数据写入磁盘中的效果、但也大大提高了效率和减少了磁盘的访问量!这就是其意义所在、他的具体工作原理在这里简单提一下:这里可能说的比较乱、具体可以看源码、不懂再回头看看这里、当程序中每次将字符或者字符数组写入到BufferedWriter中时、都会检查BufferedWriter中的缓存字符数组buf(buf的大小是默认的或者在创建bw时指定的、一般使用默认的就好)是否存满、如果没有存满则将字符写入到buf中、如果存满、则调用底层的writer(char[]b,intoff,intlen)将buf中的所有字符一次性写入到底层out中、如果写入的是字符数组、如果buf中已满则同上面满的时候的处理、如果能够存下写入的字符数组、则存入buf中、如果存不下、并且要写入buf的字符个数小于buf的长度、则将buf中所有字符写入到out中、然后将要写入的字符存放到buf中(从下标0开始存放)、如果要写入out中的字符超过buf的长度、则直接写入out中、
2、BufferedWriter API简介:
A:关键字 privateWriterout; 底层字符输出流 privatecharcb[]; 缓冲数组 privateintnChars,nextChar; nChars--cb的size,nextChar--cb中下一个字符的下标 privatestaticintdefaultCharBufferSize=8192; 默认cb大小 privateStringlineSeparator; 换行符、用于newLine方法。不同平台具有不同的值。 B:构造方法 BufferedWriter(Writerout) 使用默认cb大小创建BufferedWriterbw。 BufferedWriter(Writerout,intsz) 使用默认cb大小创建BufferedWriterbw。 C:一般方法 voidclose() 关闭此流、释放与此流有关的资源。 voidflushBuffer() 将cb中缓存的字符flush到底层out中、 voidflush() 刷新此流、同时刷新底层out流 voidnewLine() 写入一个换行符。 voidwrite(intc) 将一个单个字符写入到cb中。 voidwrite(charcbuf[],intoff,intlen) 将一个从下标off开始长度为len个字符写入cb中 voidwrite(Strings,intoff,intlen) 将一个字符串的一部分写入cb中
3、源码分析
packagecom.chy.io.original.code; importjava.io.IOException; importjava.io.PrintWriter; /** *为字符输出流提供缓冲功能、提高效率。可以使用指定字符缓冲数组大小也可以使用默认字符缓冲数组大小。 */ publicclassBufferedWriterextendsWriter{ //底层字符输出流 privateWriterout; //缓冲数组 privatecharcb[]; //nChars--cb中总的字符数,nextChar--cb中下一个字符的下标 privateintnChars,nextChar; //默认cb大小 privatestaticintdefaultCharBufferSize=8192; /** *Lineseparatorstring.Thisisthevalueoftheline.separator *propertyatthemomentthatthestreamwascreated. *换行符、用于newLine方法。不同平台具有不同的值。 */ privateStringlineSeparator; /** *使用默认cb大小创建BufferedWriterbw。 */ publicBufferedWriter(Writerout){ this(out,defaultCharBufferSize); } /** *使用指定cb大小创建br、初始化相关字段 */ publicBufferedWriter(Writerout,intsz){ super(out); if(sz<=0) thrownewIllegalArgumentException("Buffersize<=0"); this.out=out; cb=newchar[sz]; nChars=sz; nextChar=0; //获取不同平台下的换行符表示方式。 lineSeparator= (String)java.security.AccessController.doPrivileged( newsun.security.action.GetPropertyAction("line.separator")); } /**检测底层字符输出流是否关闭*/ privatevoidensureOpen()throwsIOException{ if(out==null) thrownewIOException("Streamclosed"); } /** *将cb中缓存的字符flush到底层out中、但是不flush底层out中的字符。 *并且将cb清空。 */ voidflushBuffer()throwsIOException{ synchronized(lock){ ensureOpen(); if(nextChar==0) return; out.write(cb,0,nextChar); nextChar=0; } } /** *将一个单个字符写入到cb中。 */ publicvoidwrite(intc)throwsIOException{ synchronized(lock){ ensureOpen(); if(nextChar>=nChars) flushBuffer(); cb[nextChar++]=(char)c; } } /** *Ourownlittleminmethod,toavoidloadingjava.lang.Mathifwe'verun *outoffiledescriptorsandwe'retryingtoprintastacktrace. */ privateintmin(inta,intb){ if(a<b)returna; returnb; } /** *将一个从下标off开始长度为len个字符写入cb中 */ publicvoidwrite(charcbuf[],intoff,intlen)throwsIOException{ synchronized(lock){ ensureOpen(); if((off<0)||(off>cbuf.length)||(len<0)|| ((off+len)>cbuf.length)||((off+len)<0)){ thrownewIndexOutOfBoundsException(); }elseif(len==0){ return; } if(len>=nChars){ /*如果len大于cb的长度、那么就直接将cb中现有的字符和cbuf中的字符写入out中、 *而不是写入cb、再写入out中。 */ flushBuffer(); out.write(cbuf,off,len); return; } intb=off,t=off+len; while(b<t){ intd=min(nChars-nextChar,t-b); System.arraycopy(cbuf,b,cb,nextChar,d); b+=d; nextChar+=d; if(nextChar>=nChars) flushBuffer(); } } } /** *将一个字符串的一部分写入cb中 */ publicvoidwrite(Strings,intoff,intlen)throwsIOException{ synchronized(lock){ ensureOpen(); intb=off,t=off+len; while(b<t){ intd=min(nChars-nextChar,t-b); s.getChars(b,b+d,cb,nextChar); b+=d; nextChar+=d; if(nextChar>=nChars) flushBuffer(); } } } /** *写入一个换行符。 */ publicvoidnewLine()throwsIOException{ write(lineSeparator); } /** *刷新此流、同时刷新底层out流 */ publicvoidflush()throwsIOException{ synchronized(lock){ flushBuffer(); out.flush(); } } /** *关闭此流、释放与此流有关的资源。 */ publicvoidclose()throwsIOException{ 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(Readerin,intsz) 根据指定大小和底层字符输入流创建BufferedReader。br BufferedReader(Readerin) 使用默认大小创建底层输出流的缓冲流 B:一般方法 voidclose() 关闭此流、释放与此流有关的所有资源 voidmark(intreadAheadLimit) 标记此流此时的位置 booleanmarkSupported() 判断此流是否支持标记 voidreset() 重置in被最后一次mark的位置 booleanready() 判断此流是否可以读取字符 intread() 读取单个字符、以整数形式返回。如果读到in的结尾则返回-1。 intread(char[]cbuf,intoff,intlen) 将in中len个字符读取到cbuf从下标off开始长度len中 StringreadLine() 读取一行 longskip(longn) 丢弃in中n个字符
3、源码分析
packagecom.chy.io.original.code; importjava.io.IOException; /** *为底层字符输入流添加字符缓冲cb数组。提高效率 *@version 1.1,13/11/17 *@author andyChen */ publicclassBufferedReaderextendsReader{ privateReaderin; privatecharcb[]; privateintnChars,nextChar; privatestaticfinalintINVALIDATED=-2; privatestaticfinalintUNMARKED=-1; privateintmarkedChar=UNMARKED; privateintreadAheadLimit=0;/*ValidonlywhenmarkedChar>0*/ /**Ifthenextcharacterisalinefeed,skipit*/ privatebooleanskipLF=false; /**TheskipLFflagwhenthemarkwasset*/ privatebooleanmarkedSkipLF=false; privatestaticintdefaultCharBufferSize=8192; privatestaticintdefaultExpectedLineLength=80; /** *根据指定大小和底层字符输入流创建BufferedReader。br */ publicBufferedReader(Readerin,intsz){ super(in); if(sz<=0) thrownewIllegalArgumentException("Buffersize<=0"); this.in=in; cb=newchar[sz]; nextChar=nChars=0; } /** *使用默认大小创建底层输出流的缓冲流 */ publicBufferedReader(Readerin){ this(in,defaultCharBufferSize); } /**检测底层字符输入流in是否关闭*/ privatevoidensureOpen()throwsIOException{ if(in==null) thrownewIOException("Streamclosed"); } /** *填充cb。 */ privatevoidfill()throwsIOException{ intdst; if(markedChar<=UNMARKED){ /*Nomark*/ dst=0; }else{ /*Marked*/ intdelta=nextChar-markedChar; if(delta>=readAheadLimit){ /*Gonepastread-aheadlimit:Invalidatemark*/ markedChar=INVALIDATED; readAheadLimit=0; dst=0; }else{ if(readAheadLimit<=cb.length){ /*Shuffleinthecurrentbuffer*/ System.arraycopy(cb,markedChar,cb,0,delta); markedChar=0; dst=delta; }else{ /*Reallocatebuffertoaccommodateread-aheadlimit*/ charncb[]=newchar[readAheadLimit]; System.arraycopy(cb,markedChar,ncb,0,delta); cb=ncb; markedChar=0; dst=delta; } nextChar=nChars=delta; } } intn; do{ n=in.read(cb,dst,cb.length-dst); }while(n==0); if(n>0){ nChars=dst+n; nextChar=dst; } } /** *读取单个字符、以整数形式返回。如果读到in的结尾则返回-1。 */ publicintread()throwsIOException{ synchronized(lock){ ensureOpen(); for(;;){ if(nextChar>=nChars){ fill(); if(nextChar>=nChars) return-1; } if(skipLF){ skipLF=false; if(cb[nextChar]=='\n'){ nextChar++; continue; } } returncb[nextChar++]; } } } /** *将in中len个字符读取到cbuf从下标off开始长度len中 */ privateintread1(char[]cbuf,intoff,intlen)throwsIOException{ if(nextChar>=nChars){ /*Iftherequestedlengthisatleastaslargeasthebuffer,and ifthereisnomark/resetactivity,andiflinefeedsarenot beingskipped,donotbothertocopythecharactersintothe localbuffer.Inthiswaybufferedstreamswillcascade harmlessly.*/ if(len>=cb.length&&markedChar<=UNMARKED&&!skipLF){ returnin.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; } } intn=Math.min(len,nChars-nextChar); System.arraycopy(cb,nextChar,cbuf,off,n); nextChar+=n; returnn; } /** *将in中len个字符读取到cbuf从下标off开始长度len中 */ publicintread(charcbuf[],intoff,intlen)throwsIOException{ synchronized(lock){ ensureOpen(); if((off<0)||(off>cbuf.length)||(len<0)|| ((off+len)>cbuf.length)||((off+len)<0)){ thrownewIndexOutOfBoundsException(); }elseif(len==0){ return0; } intn=read1(cbuf,off,len); if(n<=0)returnn; while((n<len)&&in.ready()){ intn1=read1(cbuf,off+n,len-n); if(n1<=0)break; n+=n1; } returnn; } } /** *从in中读取一行、是否忽略换行符 */ StringreadLine(booleanignoreLF)throwsIOException{ StringBuffers=null; intstartChar; synchronized(lock){ ensureOpen(); booleanomitLF=ignoreLF||skipLF; bufferLoop: for(;;){ if(nextChar>=nChars) fill(); if(nextChar>=nChars){/*EOF*/ if(s!=null&&s.length()>0) returns.toString(); else returnnull; } booleaneol=false; charc=0; inti; /*Skipaleftover'\n',ifnecessary*/ 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; breakcharLoop; } } startChar=nextChar; nextChar=i; if(eol){ Stringstr; if(s==null){ str=newString(cb,startChar,i-startChar); }else{ s.append(cb,startChar,i-startChar); str=s.toString(); } nextChar++; if(c=='\r'){ skipLF=true; } returnstr; } if(s==null) s=newStringBuffer(defaultExpectedLineLength); s.append(cb,startChar,i-startChar); } } } /** *从in中读取一行、 */ publicStringreadLine()throwsIOException{ returnreadLine(false); } /** *丢弃in中n个字符 */ publiclongskip(longn)throwsIOException{ if(n<0L){ thrownewIllegalArgumentException("skipvalueisnegative"); } synchronized(lock){ ensureOpen(); longr=n; while(r>0){ if(nextChar>=nChars) fill(); if(nextChar>=nChars) /*EOF*/ break; if(skipLF){ skipLF=false; if(cb[nextChar]=='\n'){ nextChar++; } } longd=nChars-nextChar; if(r<=d){ nextChar+=r; r=0; break; } else{ r-=d; nextChar=nChars; } } returnn-r; } } /** *判断cb中是否为空、或者底层in中是否有可读字符。 */ publicbooleanready()throwsIOException{ synchronized(lock){ ensureOpen(); /* *Ifnewlineneedstobeskippedandthenextchartoberead *isanewlinecharacter,thenjustskipitrightaway. */ if(skipLF){ /*Notethatin.ready()willreturntrueifandonlyifthenext *readonthestreamwillnotblock. */ if(nextChar>=nChars&&in.ready()){ fill(); } if(nextChar<nChars){ if(cb[nextChar]=='\n') nextChar++; skipLF=false; } } return(nextChar<nChars)||in.ready(); } } /** *判断此流是否支持标记 */ publicbooleanmarkSupported(){ returntrue; } /** *标记此流此时的位置、当调用reset方法失效前最多允许读取readAheadLimit个字符。 */ publicvoidmark(intreadAheadLimit)throwsIOException{ if(readAheadLimit<0){ thrownewIllegalArgumentException("Read-aheadlimit<0"); } synchronized(lock){ ensureOpen(); this.readAheadLimit=readAheadLimit; markedChar=nextChar; markedSkipLF=skipLF; } } /** *重置in被最后一次mark的位置。即下一个字符从被最后一次mark的位置开始读取。 */ publicvoidreset()throwsIOException{ synchronized(lock){ ensureOpen(); if(markedChar<0) thrownewIOException((markedChar==INVALIDATED) ?"Markinvalid" :"Streamnotmarked"); nextChar=markedChar; skipLF=markedSkipLF; } } //关闭此流、释放与此流有关的所有资源 publicvoidclose()throwsIOException{ synchronized(lock){ if(in==null) return; in.close(); in=null; cb=null; } } }
4、实例演示:
packagecom.chy.io.original.test; importjava.io.BufferedReader; importjava.io.BufferedWriter; importjava.io.File; importjava.io.FileReader; importjava.io.FileWriter; importjava.io.IOException; publicclassBufferedWriterAndBufferedReaderTest{ /** *这里对这两个类的测试比较简单、就是对文件字符流进行包装、实现文件拷贝 *有兴趣的可以测试一下效率、、偷个懒、、可无视 */ publicstaticvoidmain(String[]args)throwsIOException{ FileresouceFile=newFile("D:\\test.txt"); FiletargetFile=newFile("E:\\copyOftest.txt"); BufferedReaderbr=newBufferedReader(newFileReader(resouceFile)); BufferedWriterbw=newBufferedWriter(newFileWriter(targetFile)); char[]cbuf=newchar[1024]; intn=0; while((n=br.read(cbuf))!=-1){ bw.write(cbuf,0,n); } //不要忘记刷新和关闭流、否则一方面资源没有及时释放、另一方面有可能照成数据丢失 br.close(); bw.flush(); bw.close(); } }
总结:
对于BufferedReader、BufferedWriter、本质就是为底层字符输入输出流添加缓冲功能、先将底层流中的要读取或者要写入的数据先以一次读取一组的形式来讲数据读取或者写入到buffer中、再对buffer进行操作、这样不但效率、还能节省资源。最后、在程序中、出于效率的考虑、也应为低级流使用这两个类进行装饰一下、而不是直接拿着流直接上、觉得能实现就行。