Java基础:IO 流中的 flush 作者:verycool

Java设计Io流可谓是煞费苦心,如果你是初学者我敢保证第一次接触Java的IO类,一定会“狂晕!!”,晕,倒不是因为它有多么难学,而是太多,而且及其让人容易迷惑。在编程日子中,尤其是在网络编程中,几乎离不开Java的IO,关于Java的IO流的分类,可以到网上soso,今天跟大家分享一下flush方法。

1. OutputStream类的flush方法

该类实现了Flushable接口,所以重写了flush方法,看看flush()源码,会更加的让你明白:

  1. publicvoidflush()throwsIOException{
  2. }

sorry,该实现为空。就是一个空方法,什么也不做。看清楚啊,该方法不是抽象方法,是一个实实在在的方法。除了方法体中一无所有,其它还好!!!汗!!!看JDK的api如何解释!

  1. flush
  2. publicvoidflush()
  3. throwsIOException
  4. 刷新此输出流并强制写出所有缓冲的输出字节。flush的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用此方法指示应将这些字节立即写入它们预期的目标。
  5. 如果此流的预期目标是由基础操作系统提供的一个抽象(如一个文件),则刷新此流只能保证将以前写入到流的字节传递给操作系统进行写入,但不保证能将这些字节实际写入到物理设备(如磁盘驱动器)。
  6. OutputStream的flush方法不执行任何操作。
  7. 指定者:
  8. 接口Flushable中的flush
  9. 抛出:
  10. IOException-如果发生I/O错误。

开始,我安慰自己,该类是一个抽象类,它的子类肯定重写了该方法。好吧,OutputStream的直接子类有:

  1. ByteArrayOutputStream
  2. FileOutputStream
  3. FilterOutputStream
  4. ObjectOutputStream
  5. OutputStream
  6. PipedOutputStream

注意:这里的子类OutputStream是包org.omg.CORBA.portable的。

对于FileOutputStream、ByteArrayOutputStream、org.omg.CORBA.portable.OutputStream类它们的flush()方法均是从父类继承的flush方法。

FilterOutputStream类重写了flush()方法,但是实质还是调用父类的flush方法。

ObjectOutputStream、PipedOutputStream类重写了flush()方法。

好吧,来两个个小例子,很简单,第一个例子主要是向文本中写入字符串,第二个例子向文本中写入一定字节的数据,如下代码:

  1. packagemark.zhang;
  2. importjava.io.BufferedOutputStream;
  3. importjava.io.DataOutputStream;
  4. importjava.io.File;
  5. importjava.io.FileOutputStream;
  6. publicclassTest{
  7. publicstaticvoidmain(String[]args)throwsException{
  8. Filefile=newFile("text.txt");
  9. if(!file.exists()){
  10. file.createNewFile();
  11. }
  12. FileOutputStreamfos=newFileOutputStream(file);
  13. BufferedOutputStreambos=newBufferedOutputStream(fos);
  14. DataOutputStreamdos=newDataOutputStream(fos);
  15. dos.writeBytes("javaio");
  16. }
  17. }

  1. packagemark.zhang;
  2. importjava.io.BufferedOutputStream;
  3. importjava.io.File;
  4. importjava.io.FileOutputStream;
  5. publicclassTest{
  6. publicstaticvoidmain(String[]args)throwsException{
  7. Filefile=newFile("text.txt");
  8. if(!file.exists()){
  9. file.createNewFile();
  10. }
  11. FileOutputStreamfos=newFileOutputStream(file);
  12. BufferedOutputStreambos=newBufferedOutputStream(fos);
  13. byte[]b=newbyte[1024*8];
  14. bos.write(b);
  15. bos.flush();
  16. }
  17. }

这两段代执行后,分别会在当前目录下产生7字节的文件(内容为java io)和1KB字节的文件。说到这里,有些人会说,这有什么稀奇,至于吗???呵呵,别急,淡定!!现在修改第二个代码,主要是注释掉调用flush()方法,如下:

  1. packagemark.zhang;
  2. importjava.io.BufferedOutputStream;
  3. importjava.io.File;
  4. importjava.io.FileOutputStream;
  5. publicclassTest{
  6. publicstaticvoidmain(String[]args)throwsException{
  7. Filefile=newFile("text.txt");
  8. if(!file.exists()){
  9. file.createNewFile();
  10. }
  11. FileOutputStreamfos=newFileOutputStream(file);
  12. BufferedOutputStreambos=newBufferedOutputStream(fos);
  13. byte[]b=newbyte[1024];
  14. bos.write(b);
  15. //bos.flush();
  16. }
  17. }

ok,再次运行代码,额的神啊???文件大小居然是o字节。why????flush()方法有那么神奇,汗??!!!

仔细的你会发现,第一个代码并没有调用flush()方法,居然可以。为什么第二个就不可以呢?还是看源码,有说服力。

DataOutputStream继承FilterOutputStream,实现了DataOutput接口。我们知道FilterOutputStream类重写了flush()方法,但是实质还是调用父类的flush方法。DataOutputStream类的flush()方法效仿其父类FilterOutputStream的做法,如下:

  1. publicvoidflush()throwsIOException{
  2. out.flush();
  3. }

那么,即使你在代码后面加上dos.flush();与不加是一样的效果,因为它们的父类flush()方法均为空,这就是为什么第一个代码的神奇所在。再看看第二个代码的病因在哪里?先看看BufferedOutputStream类的结构:

  1. publicclassBufferedOutputStreamextendsFilterOutputStream

再看看,它的flush()方法:

  1. publicsynchronizedvoidflush()throwsIOException{
  2. flushBuffer();
  3. out.flush();
  4. }

  1. /**Flushtheinternalbuffer*/
  2. privatevoidflushBuffer()throwsIOException{
  3. if(count>0){
  4. out.write(buf,0,count);
  5. count=0;
  6. }
  7. }

不错,该类重写了flush()方法,不像前面几个那样不是继承就是山寨父类的flush()方法。BufferedOutputStream 类是一个使用了缓冲技术的类。这种类一把都会自己实现flush()方法。

那么,有人会问使用这种类的时候,难道必须使用flush()方法吗,当然不是喽??!!不过有个前提,你的字节数据必须不能小于8KB。实例代码,注意没有flush()方法。

  1. packagemark.zhang;
  2. importjava.io.BufferedOutputStream;
  3. importjava.io.File;
  4. importjava.io.FileOutputStream;
  5. publicclassTest{
  6. publicstaticvoidmain(String[]args)throwsException{
  7. Filefile=newFile("text.txt");
  8. if(!file.exists()){
  9. file.createNewFile();
  10. }
  11. FileOutputStreamfos=newFileOutputStream(file);
  12. BufferedOutputStreambos=newBufferedOutputStream(fos);
  13. byte[]b=newbyte[1024*8];
  14. bos.write(b);
  15. //bos.flush();
  16. }
  17. }

执行代码,会产生8KB的文本文件。当然,怎么可能你每时每刻都知道你的数据一定会不小于8KB呢,所以还是调用flush()方法比较安全。不过,话又说回来,一般用完IO流之后(如果你有一个好的习惯)我们都会去调用close()方法,看源码可以知道该方法也是调用相对应的flush()方法。所以,大多数情况下你不必要担心。这里提醒一下,如果你的文件读写没有达到预期目的,十之八九是因为你没有调用flush()或者close()方法。

另外,字符流类大多数都实现了flush()或者close()方法,只不过,它们调用的是StreamEncoder类的该方法。该类位于sun.nio.cs包下面,其源码在我们jdk中是没有的。源码地址:http://www.docjar.com/html/api/sun/nio/cs/StreamEncoder.java.html。在此,ctrl+v其源码,如下:

  1. /*
  2. *Copyright2001-2005SunMicrosystems,Inc.AllRightsReserved.
  3. *DONOTALTERORREMOVECOPYRIGHTNOTICESORTHISFILEHEADER.
  4. *
  5. *Thiscodeisfreesoftware;youcanredistributeitand/ormodifyit
  6. *underthetermsoftheGNUGeneralPublicLicenseversion2only,as
  7. *publishedbytheFreeSoftwareFoundation.Sundesignatesthis
  8. *particularfileassubjecttothe"Classpath"exceptionasprovided
  9. *bySunintheLICENSEfilethataccompaniedthiscode.
  10. *
  11. *Thiscodeisdistributedinthehopethatitwillbeuseful,butWITHOUT
  12. *ANYWARRANTY;withouteventheimpliedwarrantyofMERCHANTABILITYor
  13. *FITNESSFORAPARTICULARPURPOSE.SeetheGNUGeneralPublicLicense
  14. *version2formoredetails(acopyisincludedintheLICENSEfilethat
  15. *accompaniedthiscode).
  16. *
  17. *YoushouldhavereceivedacopyoftheGNUGeneralPublicLicenseversion
  18. *2alongwiththiswork;ifnot,writetotheFreeSoftwareFoundation,
  19. *Inc.,51FranklinSt,FifthFloor,Boston,MA02110-1301USA.
  20. *
  21. *PleasecontactSunMicrosystems,Inc.,4150NetworkCircle,SantaClara,
  22. *CA95054USAorvisitwww.sun.comifyouneedadditionalinformationor
  23. *haveanyquestions.
  24. */
  25. packagesun.nio.cs;
  26. importjava.io;
  27. importjava.nio;
  28. importjava.nio.channels;
  29. importjava.nio.charset;
  30. publicclassStreamEncoderextendsWriter
  31. {
  32. privatestaticfinalintDEFAULT_BYTE_BUFFER_SIZE=8192;
  33. privatevolatilebooleanisOpen=true;
  34. privatevoidensureOpen()throwsIOException{
  35. if(!isOpen)
  36. thrownewIOException("Streamclosed");
  37. }
  38. //Factoriesforjava.io.OutputStreamWriter
  39. publicstaticStreamEncoderforOutputStreamWriter(OutputStreamout,
  40. Objectlock,
  41. StringcharsetName)
  42. throwsUnsupportedEncodingException
  43. {
  44. Stringcsn=charsetName;
  45. if(csn==null)
  46. csn=Charset.defaultCharset().name();
  47. try{
  48. if(Charset.isSupported(csn))
  49. returnnewStreamEncoder(out,lock,Charset.forName(csn));
  50. }catch(IllegalCharsetNameExceptionx){}
  51. thrownewUnsupportedEncodingException(csn);
  52. }
  53. publicstaticStreamEncoderforOutputStreamWriter(OutputStreamout,
  54. Objectlock,
  55. Charsetcs)
  56. {
  57. returnnewStreamEncoder(out,lock,cs);
  58. }
  59. publicstaticStreamEncoderforOutputStreamWriter(OutputStreamout,
  60. Objectlock,
  61. CharsetEncoderenc)
  62. {
  63. returnnewStreamEncoder(out,lock,enc);
  64. }
  65. //Factoryforjava.nio.channels.Channels.newWriter
  66. publicstaticStreamEncoderforEncoder(WritableByteChannelch,
  67. CharsetEncoderenc,
  68. intminBufferCap)
  69. {
  70. returnnewStreamEncoder(ch,enc,minBufferCap);
  71. }
  72. //--PublicmethodscorrespondingtothoseinOutputStreamWriter--
  73. //Allsynchronizationandstate/argumentcheckingisdoneinthesepublic
  74. //methods;theconcretestream-encodersubclassesdefinedbelowneednot
  75. //doanysuchchecking.
  76. publicStringgetEncoding(){
  77. if(isOpen())
  78. returnencodingName();
  79. returnnull;
  80. }
  81. publicvoidflushBuffer()throwsIOException{
  82. synchronized(lock){
  83. if(isOpen())
  84. implFlushBuffer();
  85. else
  86. thrownewIOException("Streamclosed");
  87. }
  88. }
  89. publicvoidwrite(intc)throwsIOException{
  90. charcbuf[]=newchar[1];
  91. cbuf[0]=(char)c;
  92. write(cbuf,0,1);
  93. }
  94. publicvoidwrite(charcbuf[],intoff,intlen)throwsIOException{
  95. synchronized(lock){
  96. ensureOpen();
  97. if((off<0)||(off>cbuf.length)||(len<0)||
  98. ((off+len)>cbuf.length)||((off+len)<0)){
  99. thrownewIndexOutOfBoundsException();
  100. }elseif(len==0){
  101. return;
  102. }
  103. implWrite(cbuf,off,len);
  104. }
  105. }
  106. publicvoidwrite(Stringstr,intoff,intlen)throwsIOException{
  107. /*Checkthelenbeforecreatingacharbuffer*/
  108. if(len<0)
  109. thrownewIndexOutOfBoundsException();
  110. charcbuf[]=newchar[len];
  111. str.getChars(off,off+len,cbuf,0);
  112. write(cbuf,0,len);
  113. }
  114. publicvoidflush()throwsIOException{
  115. synchronized(lock){
  116. ensureOpen();
  117. implFlush();
  118. }
  119. }
  120. publicvoidclose()throwsIOException{
  121. synchronized(lock){
  122. if(!isOpen)
  123. return;
  124. implClose();
  125. isOpen=false;
  126. }
  127. }
  128. privatebooleanisOpen(){
  129. returnisOpen;
  130. }
  131. //--Charset-basedstreamencoderimpl--
  132. privateCharsetcs;
  133. privateCharsetEncoderencoder;
  134. privateByteBufferbb;
  135. //Exactlyoneoftheseisnon-null
  136. privatefinalOutputStreamout;
  137. privateWritableByteChannelch;
  138. //Leftoverfirstcharinasurrogatepair
  139. privatebooleanhaveLeftoverChar=false;
  140. privatecharleftoverChar;
  141. privateCharBufferlcb=null;
  142. privateStreamEncoder(OutputStreamout,Objectlock,Charsetcs){
  143. this(out,lock,
  144. cs.newEncoder()
  145. .onMalformedInput(CodingErrorAction.REPLACE)
  146. .onUnmappableCharacter(CodingErrorAction.REPLACE));
  147. }
  148. privateStreamEncoder(OutputStreamout,Objectlock,CharsetEncoderenc){
  149. super(lock);
  150. this.out=out;
  151. this.ch=null;
  152. this.cs=enc.charset();
  153. this.encoder=enc;
  154. //Thispathdisableduntildirectbuffersarefaster
  155. if(false&&outinstanceofFileOutputStream){
  156. ch=((FileOutputStream)out).getChannel();
  157. if(ch!=null)
  158. bb=ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
  159. }
  160. if(ch==null){
  161. bb=ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
  162. }
  163. }
  164. privateStreamEncoder(WritableByteChannelch,CharsetEncoderenc,intmbc){
  165. this.out=null;
  166. this.ch=ch;
  167. this.cs=enc.charset();
  168. this.encoder=enc;
  169. this.bb=ByteBuffer.allocate(mbc<0
  170. ?DEFAULT_BYTE_BUFFER_SIZE
  171. :mbc);
  172. }
  173. privatevoidwriteBytes()throwsIOException{
  174. bb.flip();
  175. intlim=bb.limit();
  176. intpos=bb.position();
  177. assert(pos<=lim);
  178. intrem=(pos<=lim?lim-pos:0);
  179. if(rem>0){
  180. if(ch!=null){
  181. if(ch.write(bb)!=rem)
  182. assertfalse:rem;
  183. }else{
  184. out.write(bb.array(),bb.arrayOffset()+pos,rem);
  185. }
  186. }
  187. bb.clear();
  188. }
  189. privatevoidflushLeftoverChar(CharBuffercb,booleanendOfInput)
  190. throwsIOException
  191. {
  192. if(!haveLeftoverChar&&!endOfInput)
  193. return;
  194. if(lcb==null)
  195. lcb=CharBuffer.allocate(2);
  196. else
  197. lcb.clear();
  198. if(haveLeftoverChar)
  199. lcb.put(leftoverChar);
  200. if((cb!=null)&&cb.hasRemaining())
  201. lcb.put(cb.get());
  202. lcb.flip();
  203. while(lcb.hasRemaining()||endOfInput){
  204. CoderResultcr=encoder.encode(lcb,bb,endOfInput);
  205. if(cr.isUnderflow()){
  206. if(lcb.hasRemaining()){
  207. leftoverChar=lcb.get();
  208. if(cb!=null&&cb.hasRemaining())
  209. flushLeftoverChar(cb,endOfInput);
  210. return;
  211. }
  212. break;
  213. }
  214. if(cr.isOverflow()){
  215. assertbb.position()>0;
  216. writeBytes();
  217. continue;
  218. }
  219. cr.throwException();
  220. }
  221. haveLeftoverChar=false;
  222. }
  223. voidimplWrite(charcbuf[],intoff,intlen)
  224. throwsIOException
  225. {
  226. CharBuffercb=CharBuffer.wrap(cbuf,off,len);
  227. if(haveLeftoverChar)
  228. flushLeftoverChar(cb,false);
  229. while(cb.hasRemaining()){
  230. CoderResultcr=encoder.encode(cb,bb,false);
  231. if(cr.isUnderflow()){
  232. assert(cb.remaining()<=1):cb.remaining();
  233. if(cb.remaining()==1){
  234. haveLeftoverChar=true;
  235. leftoverChar=cb.get();
  236. }
  237. break;
  238. }
  239. if(cr.isOverflow()){
  240. assertbb.position()>0;
  241. writeBytes();
  242. continue;
  243. }
  244. cr.throwException();
  245. }
  246. }
  247. voidimplFlushBuffer()throwsIOException{
  248. if(bb.position()>0)
  249. writeBytes();
  250. }
  251. voidimplFlush()throwsIOException{
  252. implFlushBuffer();
  253. if(out!=null)
  254. out.flush();
  255. }
  256. voidimplClose()throwsIOException{
  257. flushLeftoverChar(null,true);
  258. try{
  259. for(;;){
  260. CoderResultcr=encoder.flush(bb);
  261. if(cr.isUnderflow())
  262. break;
  263. if(cr.isOverflow()){
  264. assertbb.position()>0;
  265. writeBytes();
  266. continue;
  267. }
  268. cr.throwException();
  269. }
  270. if(bb.position()>0)
  271. writeBytes();
  272. if(ch!=null)
  273. ch.close();
  274. else
  275. out.close();
  276. }catch(IOExceptionx){
  277. encoder.reset();
  278. throwx;
  279. }
  280. }
  281. StringencodingName(){
  282. return((csinstanceofHistoricallyNamedCharset)
  283. ?((HistoricallyNamedCharset)cs).historicalName()
  284. :cs.name());
  285. }

更多源码查看http://www.docjar.com/projects/Open-JDK-6.b17-src-code.html

2. Writer类的flush方法

该类是一个抽象类,声明如下:

  1. publicabstractclassWriterimplementsAppendable,Closeable,Flushable

Writer类的flush()方法是一个抽象方法,其子类一般都实现了该方法。所以,一般使用字符流之后,调用一下flush()或者close()方法。

  1. abstractpublicvoidflush()throwsIOException;

细节请看jdk的api,或者Java的源码以及上面的StreamEncoder类源码。

ok,说到这里吧,这里主要借助Java的IO中字节流与字符流的flush()方法,来说明学Java看源码和思考是很重要的。

总之,不管你使用哪种流(字符、字节、具有缓冲的流)技术,不妨调用一下flush()/close()方法,防止数据无法写到输出流中。

资料太好 就收集过来 方便自己查阅 原作者:verycool个人空间: http://my.csdn.net/AndroidBluetooth

你可能感兴趣的:(java基础)