Java NIO (2): Buffer internal details, javanio
Java NIO entry (2) Internal details of the buffer
Overview
This article describes two important buffer components in NIO:Status variableAndAccess Method(Accessor ).
State variables are the key to the "internal statistical mechanism" mentioned in the previous article. Each read/write operation changes the buffer state. By recording and tracking these changes, the buffer zone can internally manage its own resources.
When reading data from the Channel, the data is put into the buffer zone. In some cases, you can write the buffer directly to another channel, but in general, you also need to view the data. This is done using the access method get. Similarly, if you want to put the raw data into the buffer, you need to use the access method put ().
In this section, you will learn about the state variables and access methods in NIO. We will describe each component and give you a chance to see its practical application. Although NIO's internal statistical mechanism may seem complicated at the beginning, you will soon see that most of the actual work has been completed for you. You may be used to bookkeeping through manual encoding-that is, byte arrays and index variables, which are now processed internally in NIO.
Status variable
Three values can be used to specify the status of the buffer at any time:
Together, these three variables can trace the status of the buffer zone and the data it contains. We will analyze each variable in detail in the following sections and introduce how they adapt to typical read/write (input/output) processes. In this example, we assume that we want to copy data from an input channel to an output channel.
1. Position
You can recall that the buffer zone is actually a beautifying array. When reading data from a channel, you place the data you read into the underlying array. The position variable tracks how much data has been written. More accurately, it specifies which element of the array the next byte will be placed in. Therefore, if you read three bytes from the channel to the buffer, the position of the buffer is set to 3, pointing to the fourth element in the array.
Similarly, when writing data to a channel, you obtain data from the buffer zone. The position value tracks how much data is obtained from the buffer. More accurately, it specifies the element from which the next byte comes from the array. Therefore, if five bytes are written from the buffer zone to the channel, the position of the buffer zone is set to 5, pointing to the sixth element of the array.
2. Limit
The limit variable indicates how much data needs to be retrieved (when the buffer is written to the channel), or how much space can be put into the data (when the buffer is read from the channel ).
Position is always less than or equal to limit.
3. Capacity
The buffer capacity indicates the maximum data capacity that can be stored in the buffer. In fact, it specifies the size of the underlying array-or at least the capacity of the underlying array that is allowed.
Limit cannot be greater than capacity.
First, observe a newly created buffer. For the purpose of this example, we assume that the total buffer capacity is 8 bytes. The Buffer status is as follows:
Recall that the limit value cannot be greater than capacity. In this example, both values are set to 8. We can illustrate this by pointing them to the end of the array (if there are 8th slots, It is the location of the 8th slots.
Set position to 0. If we read some data into the buffer, the next read data enters the slot 0. If we write some data from the buffer, the next byte read from the buffer comes from slot 0. The position settings are as follows:
Since capacity will not change, we can ignore it in the following discussion.
First read
Now we can start read/write operations on the newly created buffer. First, read some data from the input channel to the buffer. The first read gets three bytes. They are placed in the array starting from position, and the position is set to 0. After reading, the position is increased to 3, as shown below:
Limit has not changed.
Second read
During the second read, we read the other two bytes from the input channel to the buffer zone. These two bytes are stored at the position specified by position, so the position is increased by 2:
Limit has not changed.
Flip
Now we need to write the data to the output channel. Before that, we must call the flip () method. This method is very important to do two things:
The figure in the previous section shows the buffer before flip. The following is the buffer zone after flip:
Now we can write data from the buffer into the channel. Position is set to 0, which means that the next byte we get is the first byte. Limit has been set to the original position, which means that it includes all the previously read bytes, and there are not many bytes.
First write
At the first write, we take four bytes from the buffer and write them into the output channel. This increases position to 4, while limit remains unchanged, as shown below:
Second write
We have only one byte left to write. Limit is set to 5 when we call flip (), and the position cannot exceed limit. Therefore, the last write operation extracts a byte from the buffer and writes it to the output channel. This increases position to 5 and keeps limit unchanged, as shown below:
Clear
The last step is to call the clear () method of the buffer zone. This method resets the buffer to receive more bytes. Clear does two very important tasks:
Shows the status of the buffer after clear () is called:
The buffer zone can now receive new data.
Access Method
So far, we have only transferred data from one channel to another using a buffer zone. However, programs often need to process data directly. For example, you may need to save user data to a disk. In this case, you must put the data directly into the buffer and then write the buffer to the disk using the channel.
Alternatively, you may want to read user data from the disk. In this case, you need to read the data from the channel to the buffer, and then check the data in the buffer.
At the end of this section, we will analyze in detail how to use the get () and put () Methods of the ByteBuffer class to directly access data in the buffer zone.
Get () method
The ByteBuffer class has four get () methods:
The first method gets a single byte. The second and third Methods read a group of bytes into an array. The fourth method retrieves bytes from a specific location in the buffer zone. The methods that return ByteBuffer only return the this value of the buffer that calls them.
In addition, we think that the first three get () methods are relative, and the last method is absolute.RelativeThis means that the get () operation is subject to the limit and position values-more specifically, the byte is read from the current position, and the position will increase after the get operation. On the other hand,AbsoluteMethod ignores the limit and position values, and does not affect them. In fact, it completely bypasses the statistical method of the buffer zone.
The methods listed above correspond to the ByteBuffer class. Other classes have equivalent get () methods. These methods are identical in other aspects except for processing bytes. They process types that are compatible with the buffer class.
Put () method
The ByteBuffer class has five put () methods:
The first method writes (put) A single byte. The second and third methods are written to a group of bytes from an array. The fourth method writes data from a given source ByteBuffer to this ByteBuffer. The fifth method writes bytes to a specific location in the buffer. The methods that return ByteBuffer only return the this value of the buffer that calls them.
Like the get () method, we divide the put () methodRelativeOrAbsolute. The first four methods are:RelativeAnd the fifth method isAbsolute.
The method shown above corresponds to the ByteBuffer class. Other classes have equivalent put () methods. These methods are identical except for processing bytes. They process the types that match the buffer class.
The following code provides a simple example:
1 // TypesInByteBuffer 2 3 import java.nio.*; 4 5 public class TypesInByteBuffer { 6 static public void main( String[] args) throws Exception { 7 ByteBuffer buffer=ByteBuffer.allocate(64); 8 ByteBuffer buffer2=ByteBuffer.allocate(1024); 9 10 buffer.putInt(30);11 buffer.putDouble(Math.PI);12 buffer.flip();13 // System.out.println(buffer.getInt());14 // System.out.println(buffer.getDouble());15 // buffer.clear();16 17 buffer2.put(buffer);18 buffer2.flip();19 System.out.println(buffer2.getInt());20 System.out.println(buffer2.getDouble());21 }22 }
Typed get () and put () Methods
In addition to the get () and put () methods described in the previous sections, ByteBuffer also has other methods for reading and writing different types of values, as shown below:
GetByte ()
GetChar ()
GetShort ()
GetInt ()
GetLong ()
GetFloat ()
GetDouble ()
PutByte ()
PutChar ()
PutShort ()
PutInt ()
PutLong ()
PutFloat ()
PutDouble ()
In fact, each of these methods has two types-one is relative and the other is absolute. They are useful for reading formatted binary data (like the file header.
1 // TypesInByteBuffer 2 3 import java.nio.*; 4 5 public class TypesInByteBuffer { 6 static public void main( String[] args) throws Exception { 7 ByteBuffer buffer=ByteBuffer.allocate(64); 8 9 buffer.putInt(30);10 buffer.putDouble(Math.PI);11 buffer.flip();12 System.out.println(buffer.getInt());13 System.out.println(buffer.getDouble());14 buffer.clear();15 }16 }
Buffer usage: an internal loop
The following internal cycle summarizes the process of using a buffer to copy data from the input channel to the output channel.
1 while (true) {2 buffer.clear();3 int r = fcin.read( buffer );4 if (r==-1) {5 break;6 }7 buffer.flip();8 fcout.write( buffer );9 }
Read () and write () calls are greatly simplified, because many work details are completed by the buffer zone. The clear () and flip () methods are used to switch between read and write buffer.