Java IO stream learning summary three: buffered stream - BufferedInputStream, BufferedOutputStream
Java IO stream learning summary 1: input and output streams
Java IO stream learning summary 2: File
Java IO stream learning summary three: buffered stream - BufferedInputStream, BufferedOutputStream
Java IO Stream Learning Summary IV: Buffered Streams - BufferedReader, BufferedWriter
Java IO stream learning summary five: conversion stream - InputStreamReader, OutputStreamWriter
Java IO stream learning summary six: ByteArrayInputStream, ByteArrayOutputStream
Java IO stream learning summary seven: Commons IO 2.5-FileUtils
2021 Java Okio - a more efficient and easy-to-use IO library
Inheritance Diagram
InputStream |__FilterInputStream |__BufferedInputStream
First of all, a question is thrown, why should there be BufferedInputStream with InputStream?
The two classes BufferedInputStream and BufferedOutputStream are subclasses of FilterInputStream and FilterOutputStream respectively. As decorator subclasses, using them can prevent the actual write operation every time data is read/sent, representing the use of buffers.
It is necessary to know that the operation without buffering requires writing a byte every time a byte is read. Since the IO operation involving the disk is much slower than the operation of the memory, the stream without buffering is very inefficient. With a buffered stream, many bytes can be read at a time, but not written to disk, just put into memory first. When the buffer size is enough, write to the disk at one time. This method can reduce the number of disk operations and improve the speed a lot!
At the same time, because they implement the buffering function, it should be noted that after using BufferedOutputStream to write data, the flush() method or the close() method should be called to force the data in the buffer to be written out. Otherwise the data may not be written out. There are two similar classes, BufferedReader and BufferedWriter.
The question posed at the beginning of this article can now be answered:
The BufferedInputStream and BufferedOutputStream classes are the input stream/output stream that implements the buffering function. Using buffered input and output streams is more efficient and faster.
Summarize:
BufferedInputStream is the buffered input stream. it inherits from FilterInputStream. BufferedInputStream The role of is to add some functionality to another input stream, for example, to provide "buffering capabilities" and support mark()mark and reset()reset method. BufferedInputStream Essentially implemented via an internal buffer array. For example, when creating a new input stream corresponding to BufferedInputStream After that, when we pass read()When reading data from the input stream, BufferedInputStream The data of the input stream will be filled into the buffer in batches. Each time the data in the buffer is read, the input stream fills the data buffer again; this repeats until we finish reading the input stream data position.
Introduction to the BufferedInputStream API
Source code key field analysis
private static int defaultBufferSize = 8192;//Built-in cache byte array size 8KB protected volatile byte buf[]; //Built-in cached byte array protected int count; //The total number of bytes in the current buf, note that it is not the total number of bytes in the source of the underlying byte input stream protected int pos; //The next byte index to be read in the current buf protected int markpos = -1; //The position of the next byte to be read in buf recorded by the last call to the mark(int readLimit) method protected int marklimit; //After calling mark, before the subsequent call to reset() method fails, the maximum amount of data that Yunxun reads from in, which is used to limit the maximum value of the buffer after being marked
Constructor
BufferedInputStream(InputStream in) //Build bis with default buf size, underlying byte input stream BufferedInputStream(InputStream in, int size) //Build bis with the specified buf size and the underlying byte input stream
General method introduction
int available(); //Returns the number of bytes available for reading from the source corresponding to the underlying stream void close(); //closes this stream, releasing all resources associated with this stream boolean markSupport(); //Check if this stream supports mark void mark(int readLimit); //Mark the subscript of the next byte read in the current buf int read(); //read next byte in buf int read(byte[] b, int off, int len); //read next byte in buf void reset(); //Reset the position in the buf marked by the last call to mark long skip(long n); //skip n bytes, not only valid bytes in buf, but also bytes in the source of in
Introduction to the BufferedOutputStream API
key field
protected byte[] buf; //Built-in cache byte array, used to store the bytes that the program wants to write to out protected int count; //The total number of bytes existing in the built-in cache byte array
Constructor
BufferedOutputStream(OutputStream out); //Construct bos with default size, underlying byte output stream. The default buffer size is 8192 bytes (8KB) BufferedOutputStream(OutputStream out, int size); //Constructs a bos using the specified size, underlying byte output stream
Constructor source code:
/** * Creates a new buffered output stream to write data to the * specified underlying output stream. * @param out the underlying output stream. */ public BufferedOutputStream(OutputStream out) { this(out, 8192); } /** * Creates a new buffered output stream to write data to the * specified underlying output stream with the specified buffer * size. * * @param out the underlying output stream. * @param size the buffer size. * @exception IllegalArgumentException if size <= 0. */ public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }
general method
//Mention here, BufferedOutputStream does not have its own close method. When it calls the method of the parent class FilterOutputStrem to close, it will indirectly call the flush method implemented by itself to flush the remaining bytes in buf to out, and then out.flush( ) into the destination, and so does the DataOutputStream.
void flush(); will write bos data in flus arrive out In the specified destination, note that this is not flush arrive out , because it internally calls out.flush() write(byte b); write a byte to buf middle write(byte[] b, int off, int len); Will b part of the write buf middle
So when does flush() work?
The answer is: when the OutputStream is a BufferedOutputStream.
When the effect of flush() is required to write a file, it is required
FileOutputStream fos = new FileOutputStream("c:\a.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
That is to say, you need to pass in FileOutputStream as a parameter of the BufferedOutputStream constructor, and then write to BufferedOutputStream to use buffering and flush().
Looking at the source code of BufferedOutputStream, it is found that the so-called buffer is actually a byte[].
Each write of BufferedOutputStream actually writes the content to byte[]. When the buffer capacity reaches the upper limit, a real disk write will be triggered.
Another way to trigger disk writes is to call flush().
1.BufferedOutputStream will automatically flush when close()
2.BufferedOutputStream needs to call flush only when the buffer is not full without calling close(), and the content of the buffer needs to be written to a file or sent to other machines through the network.
Practical exercise 1: Copy files.
Operation: Use the cache stream to copy the name in the root directory of the F drive: 123.png to abc.png
package com.app; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class A3 { public static void main(String[] args) throws IOException { String filePath = "F:/123.png" ; String filePath2 = "F:/abc.png" ; File file = new File( filePath ) ; File file2 = new File( filePath2 ) ; copyFile( file , file2 ); } /** * copy file * @param oldFile * @param newFile */ public static void copyFile( File oldFile , File newFile){ InputStream inputStream = null ; BufferedInputStream bufferedInputStream = null ; OutputStream outputStream = null ; BufferedOutputStream bufferedOutputStream = null ; try { inputStream = new FileInputStream( oldFile ) ; bufferedInputStream = new BufferedInputStream( inputStream ) ; outputStream = new FileOutputStream( newFile ) ; bufferedOutputStream = new BufferedOutputStream( outputStream ) ; byte[] b=new byte[1024]; //Represents a read of up to 1KB of content at a time int length = 0 ; //Represents the number of bytes actually read while( (length = bufferedInputStream.read( b ) )!= -1 ){ //length represents the number of bytes actually read bufferedOutputStream.write(b, 0, length ); } //Write the contents of the buffer to the file bufferedOutputStream.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally { if( bufferedOutputStream != null ){ try { bufferedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( bufferedInputStream != null){ try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( inputStream != null ){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if ( outputStream != null ) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
Effect picture:
How to properly close a stream
In the above code, our code to close the stream is written like this.
finally { if( bufferedOutputStream != null ){ try { bufferedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( bufferedInputStream != null){ try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( inputStream != null ){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if ( outputStream != null ) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
Thinking: Do we still need to close the node stream after the processing stream is closed?
Let's take a look at the source code with the question:
bufferedOutputStream.close();
/** * Closes this input stream and releases any system resources * associated with the stream. * Once the stream has been closed, further read(), available(), reset(), * or skip() invocations will throw an IOException. * Closing a previously closed stream has no effect. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } // Else retry in case a new buf was CASed in fill() } }
The role of the close() method
1. Close the input stream and release system resources
2. BufferedInputStream decorates an InputStream to have a buffering function. To close it, it only needs to call the close() method of the object that is finally decorated, because it will eventually call the close() method of the real data source object. Therefore, it is possible to just call the close method of the outer stream to close its decorated inner stream.
So if we want to close the streams one by one, how can we do it?
The answer is: turn off the outer flow first, then turn off the inner flow. In general, it is: open first and then close, and then open and close first; another case: look at the dependencies, if stream a depends on stream b, you should close stream a first, and then close stream b. For example, the processing flow a depends on the node flow b, and the processing flow a should be closed first, and then the node flow b should be closed.
After understanding how to close the stream correctly, then we can optimize the above code and only close the outer processing stream.
finally { if( bufferedOutputStream != null ){ try { bufferedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if( bufferedInputStream != null){ try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }