Java设计Io流可谓是煞费苦心,如果你是初学者我敢保证第一次接触Java的IO类,一定会“狂晕!!”,晕,倒不是因为它有多么难学,而是太多,而且及其让人容易迷惑。在编程日子中,尤其是在网络编程中,几乎离不开Java的IO,关于Java的IO流的分类,可以到网上soso,今天跟大家分享一下flush方法。
1. OutputStream类的flush方法
该类实现了Flushable接口,所以重写了flush方法,看看flush()源码,会更加的让你明白:
sorry,该实现为空。就是一个空方法,什么也不做。看清楚啊,该方法不是抽象方法,是一个实实在在的方法。除了方法体中一无所有,其它还好!!!汗!!!看JDK的api如何解释!
开始,我安慰自己,该类是一个抽象类,它的子类肯定重写了该方法。好吧,OutputStream的直接子类有:
注意:这里的子类OutputStream是包 org.omg.CORBA.portable 的。
对于FileOutputStream、ByteArrayOutputStream、org.omg.CORBA.portable.OutputStream类它们的flush()方法均是从父类继承的flush方法。
FilterOutputStream类重写了flush()方法,但是实质还是调用父类的flush方法。
ObjectOutputStream、PipedOutputStream类重写了flush()方法。
好吧,来两个个小例子,很简单,第一个例子主要是向文本中写入字符串,第二个例子向文本中写入一定字节的数据,如下代码:
这两段代执行后,分别会在当前目录下产生7字节的文件(内容为java io)和1KB字节的文件。说到这里,有些人会说,这有什么稀奇,至于吗???呵呵,别急,淡定!!现在修改第二个代码,主要是注释掉调用flush()方法,如下:
ok,再次运行代码,额的神啊???文件大小居然是o字节。why????flush()方法有那么神奇,汗??!!!
仔细的你会发现,第一个代码并没有调用flush()方法,居然可以。为什么第二个就不可以呢?还是看源码,有说服力。
DataOutputStream继承FilterOutputStream,实现了DataOutput接口。我们知道FilterOutputStream类重写了flush()方法,但是实质还是调用父类的flush方法。DataOutputStream类的flush()方法效仿其父类FilterOutputStream的做法,如下:
那么,即使你在代码后面加上dos.flush();与不加是一样的效果,因为它们的父类flush()方法均为空,这就是为什么第一个代码的神奇所在。再看看第二个代码的病因在哪里?先看看BufferedOutputStream类的结构:
再看看,它的flush()方法:
不错,该类重写了flush()方法,不像前面几个那样不是继承就是山寨父类的flush()方法。BufferedOutputStream 类是一个使用了缓冲技术的类。这种类一把都会自己实现flush()方法。
那么,有人会问使用这种类的时候,难道必须使用flush()方法吗,当然不是喽??!!不过有个前提,你的字节数据必须不能小于8KB。实例代码,注意没有flush()方法。
执行代码,会产生8KB的文本文件。当然,怎么可能你每时每刻都知道你的数据一定会不小于8KB呢,所以还是调用flush()方法比较安全。不过,话又说回来,一般用完IO流之后(如果你有一个好的习惯)我们都会去调用close()方法,看源码可以知道该方法也是调用相对应的flush()方法。所以,大多数情况下你不必要担心。这里提醒一下,如果你的文件读写没有达到预期目的,十之八九是因为你没有调用flush()或者close()方法。
另外,字符流类大多数都实现了flush()或者close()方法,只不过,它们调用的是StreamEncoder类的该方法。该类位于sun.nio.cs包下面,其源码在我们jdk中是没有的。在此,ctrl+v其源码,如下:
1. /*
2. * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4. *
5. * This code is free software; you can redistribute it and/or modify it
6. * under the terms of the GNU General Public License version 2 only, as
7. * published by the Free Software Foundation. Sun designates this
8. * particular file as subject to the "Classpath" exception as provided
9. * by Sun in the LICENSE file that accompanied this code.
10. *
11. * This code is distributed in the hope that it will be useful, but WITHOUT
12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14. * version 2 for more details (a copy is included in the LICENSE file that
15. * accompanied this code).
16. *
17. * You should have received a copy of the GNU General Public License version
18. * 2 along with this work; if not, write to the Free Software Foundation,
19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20. *
21. * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22. * CA 95054 USA or visit www.sun.com if you need additional information or
23. * have any questions.
24. */
25.
26.
27.
28. package sun.nio.cs;
29.
30. import java.io;
31. import java.nio;
32. import java.nio.channels;
33. import java.nio.charset;
34.
35. public class StreamEncoder extends Writer
36. {
37.
38. private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
39.
40. private volatile boolean isOpen = true;
41.
42. private void ensureOpen() throws IOException {
43. if (!isOpen)
44. throw new IOException("Stream closed");
45. }
46.
47. // Factories for java.io.OutputStreamWriter
48. public static StreamEncoder forOutputStreamWriter(OutputStream out,
49. Object lock,
50. String charsetName)
51. throws UnsupportedEncodingException
52. {
53. String csn = charsetName;
54. if (csn == null)
55. csn = Charset.defaultCharset().name();
56. try {
57. if (Charset.isSupported(csn))
58. return new StreamEncoder(out, lock, Charset.forName(csn));
59. catch (IllegalCharsetNameException x) { }
60. throw new UnsupportedEncodingException (csn);
61. }
62.
63. public static StreamEncoder forOutputStreamWriter(OutputStream out,
64. Object lock,
65. Charset cs)
66. {
67. return new StreamEncoder(out, lock, cs);
68. }
69.
70. public static StreamEncoder forOutputStreamWriter(OutputStream out,
71. Object lock,
72. CharsetEncoder enc)
73. {
74. return new StreamEncoder(out, lock, enc);
75. }
76.
77.
78. // Factory for java.nio.channels.Channels.newWriter
79.
80. public static StreamEncoder forEncoder(WritableByteChannel ch,
81. CharsetEncoder enc,
82. int minBufferCap)
83. {
84. return new StreamEncoder(ch, enc, minBufferCap);
85. }
86.
87.
88. // -- Public methods corresponding to those in OutputStreamWriter --
89.
90. // All synchronization and state/argument checking is done in these public
91. // methods; the concrete stream-encoder subclasses defined below need not
92. // do any such checking.
93.
94. public String getEncoding() {
95. if (isOpen())
96. return encodingName();
97. return null;
98. }
99.
100. public void flushBuffer() throws IOException {
101. synchronized (lock) {
102. if (isOpen())
103. implFlushBuffer();
104. else
105. throw new IOException("Stream closed");
106. }
107. }
108.
109. public void write(int c) throws IOException {
110. char cbuf[] = new char[1];
111. 0] = (char) c;
112. 0, 1);
113. }
114.
115. public void write(char cbuf[], int off, int len) throws IOException {
116. synchronized (lock) {
117. ensureOpen();
118. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
119. 0)) {
120. throw new IndexOutOfBoundsException();
121. else if (len == 0) {
122. return;
123. }
124. implWrite(cbuf, off, len);
125. }
126. }
127.
128. public void write(String str, int off, int len) throws IOException {
129. /* Check the len before creating a char buffer */
130. if (len < 0)
131. throw new IndexOutOfBoundsException();
132. char cbuf[] = new char[len];
133. 0);
134. 0, len);
135. }
136.
137. public void flush() throws IOException {
138. synchronized (lock) {
139. ensureOpen();
140. implFlush();
141. }
142. }
143.
144. public void close() throws IOException {
145. synchronized (lock) {
146. if (!isOpen)
147. return;
148. implClose();
149. false;
150. }
151. }
152.
153. private boolean isOpen() {
154. return isOpen;
155. }
156.
157.
158. // -- Charset-based stream encoder impl --
159.
160. private Charset cs;
161. private CharsetEncoder encoder;
162. private ByteBuffer bb;
163.
164. // Exactly one of these is non-null
165. private final OutputStream out;
166. private WritableByteChannel ch;
167.
168. // Leftover first char in a surrogate pair
169. private boolean haveLeftoverChar = false;
170. private char leftoverChar;
171. private CharBuffer lcb = null;
172.
173. private StreamEncoder(OutputStream out, Object lock, Charset cs) {
174. this(out, lock,
175. cs.newEncoder()
176. .onMalformedInput(CodingErrorAction.REPLACE)
177. .onUnmappableCharacter(CodingErrorAction.REPLACE));
178. }
179.
180. private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {
181. super(lock);
182. this.out = out;
183. this.ch = null;
184. this.cs = enc.charset();
185. this.encoder = enc;
186.
187. // This path disabled until direct buffers are faster
188. if (false && out instanceof FileOutputStream) {
189. ch = ((FileOutputStream)out).getChannel();
190. if (ch != null)
191. bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
192. }
193. if (ch == null) {
194. bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
195. }
196. }
197.
198. private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {
199. this.out = null;
200. this.ch = ch;
201. this.cs = enc.charset();
202. this.encoder = enc;
203. this.bb = ByteBuffer.allocate(mbc < 0
204. ? DEFAULT_BYTE_BUFFER_SIZE
205. : mbc);
206. }
207.
208. private void writeBytes() throws IOException {
209. bb.flip();
210. int lim = bb.limit();
211. int pos = bb.position();
212. assert (pos <= lim);
213. int rem = (pos <= lim ? lim - pos : 0);
214.
215. if (rem > 0) {
216. if (ch != null) {
217. if (ch.write(bb) != rem)
218. assert false : rem;
219. else {
220. out.write(bb.array(), bb.arrayOffset() + pos, rem);
221. }
222. }
223. bb.clear();
224. }
225.
226. private void flushLeftoverChar(CharBuffer cb, boolean endOfInput)
227. throws IOException
228. {
229. if (!haveLeftoverChar && !endOfInput)
230. return;
231. if (lcb == null)
232. 2);
233. else
234. lcb.clear();
235. if (haveLeftoverChar)
236. lcb.put(leftoverChar);
237. if ((cb != null) && cb.hasRemaining())
238. lcb.put(cb.get());
239. lcb.flip();
240. while (lcb.hasRemaining() || endOfInput) {
241. CoderResult cr = encoder.encode(lcb, bb, endOfInput);
242. if (cr.isUnderflow()) {
243. if (lcb.hasRemaining()) {
244. leftoverChar = lcb.get();
245. if (cb != null && cb.hasRemaining())
246. flushLeftoverChar(cb, endOfInput);
247. return;
248. }
249. break;
250. }
251. if (cr.isOverflow()) {
252. assert bb.position() > 0;
253. writeBytes();
254. continue;
255. }
256. cr.throwException();
257. }
258. false;
259. }
260.
261. void implWrite(char cbuf[], int off, int len)
262. throws IOException
263. {
264. CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
265.
266. if (haveLeftoverChar)
267. false);
268.
269. while (cb.hasRemaining()) {
270. false);
271. if (cr.isUnderflow()) {
272. assert (cb.remaining() <= 1) : cb.remaining();
273. if (cb.remaining() == 1) {
274. true;
275. leftoverChar = cb.get();
276. }
277. break;
278. }
279. if (cr.isOverflow()) {
280. assert bb.position() > 0;
281. writeBytes();
282. continue;
283. }
284. cr.throwException();
285. }
286. }
287.
288. void implFlushBuffer() throws IOException {
289. if (bb.position() > 0)
290. writeBytes();
291. }
292.
293. void implFlush() throws IOException {
294. implFlushBuffer();
295. if (out != null)
296. out.flush();
297. }
298.
299. void implClose() throws IOException {
300. null, true);
301. try {
302. for (;;) {
303. CoderResult cr = encoder.flush(bb);
304. if (cr.isUnderflow())
305. break;
306. if (cr.isOverflow()) {
307. assert bb.position() > 0;
308. writeBytes();
309. continue;
310. }
311. cr.throwException();
312. }
313.
314. if (bb.position() > 0)
315. writeBytes();
316. if (ch != null)
317. ch.close();
318. else
319. out.close();
320. catch (IOException x) {
321. encoder.reset();
322. throw x;
323. }
324. }
325.
326. String encodingName() {
327. return ((cs instanceof HistoricallyNamedCharset)
328. ? ((HistoricallyNamedCharset)cs).historicalName()
329. : cs.name());
330. }
2. Writer类的flush方法
该类是一个抽象类,声明如下:
Writer类的flush()方法是一个抽象方法,其子类一般都实现了该方法。所以,一般使用字符流之后,调用一下flush()或者close()方法。
细节请看jdk的api,或者Java的源码以及上面的StreamEncoder类源码。
ok,说到这里吧,这里主要借助Java的IO中字节流与字符流的flush()方法,来说明学Java看源码和思考是很重要的。
总之,不管你使用哪种流(字符、字节、具有缓冲的流)技术,不妨调用一下flush()/close()方法,防止数据无法写到输出流中。