This is the 10th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

An overview

The key difference between streams and channels is that streams are byte based, while channels are block based. Streams are designed to transmit data sequentially byte by byte. The difference is that the channel passes blocks of data in the buffer. Before the bytes of a channel can be read or written, they must already be stored in a buffer and read/write one buffer at a time.

A buffer can be thought of as a list of fixed-size elements, such as arrays, of a particular type, usually a primitive data type. In addition to Boolean, all of Java’s basic data types have specific Buffer subclasses: ByteBuffer, CharBuffer, ShortBuffer, IntBuffer.longBuffer, FloatBuffer, and DoubleBuffer. Methods in each subclass have a return value and argument list of the corresponding type. For example, the DoubleBuffer class has methods to set and get double. The IntBuffer class has methods for setting and getting ints. The common Buffer superclass only provides “generic” methods that don’t need to know what type of data the Buffer contains (the lack of generics for simple types does have a negative effect here). Network programs use ByteBuffers almost exclusively, but programs occasionally use other types in place of byteBuffers.

The core attributes

The byte Buffer’s parent Buffer has several core attributes, as follows:

  • Capacity: indicates the capacity of the buffer. Given by the constructor, once set, it cannot be changed
  • Limit: indicates the limit of the buffer. Data after limit cannot be read or written. The buffer limit cannot be negative and cannot be larger than its capacity
  • Position: Index of the next read/write position (similar to a PC). The buffer position cannot be negative and cannot be greater than limit
  • Mark: Records the value of current position. After position has been changed, you can restore mark to its position by calling the reset() method.

The preceding four attributes must meet the following requirements: Mark <= position <= Limit <= Capacity for example, you can use the following figure to learn more about these four attributes:

When creating a buffer with capacity 10, the state is as follows:

We then add three elements a, B, and C to the buffer, and since we are still in read mode, position will change to 3

Now switch to read mode (using the flip() method, described below), position changes to 0 (the start of the read) and limit to 3 (the limit of the read).

Creating a buffer

allocate

The basic allocate() method only returns a new buffer with a specified fixed capacity, which is an empty buffer. For example, to create a ByteBuffer of capacity 100:

ByteBuffer buffer1 = ByteBuffer.allocate(100);
Copy the code

Position is at the beginning of the buffer (that is, position 0). In addition, buffers created with allocate() can be accessed by array(). For example, you can get data from a buffer using the following method:

byte[] data1 = buffer1.array();
Copy the code

Array () actually exposes the buffer’s private data, and the resulting byte array is reflected in the buffer, and vice versa. So you need to be careful not to modify the byte array and forget that the buffer also changes, and vice versa.

allocateDirect

The ByteBuffer class also has an allocateDirect() method, which does not create a backup array for the buffer. In terms of usage, allocateDirect() is used in exactly the same way as allocate() :

ByteBuffer buffer = ByteBuffer.allocateDirect(100);
Copy the code

Can deeply understand the allocateDirect () with the allocate (), the difference between using getClass () method to print their own object type, return allocateDirect () returns the Java nio. DirectByteBuffer, Allocate () returns java.nio.heapbyteBuffer. For allocateDirect(), it uses direct memory, so it is efficient to read and write, but inefficient to allocate memory. Meanwhile, allocate() uses Java heap memory, so it is inefficient to write and write, so direct buffers should not be considered unless performance is measured and found to be a real problem.

Common methods of ByteBuffer

put()

The put() method puts a piece of data or an array of bytes into a buffer. After doing so, position in the buffer is incremented by one, pointing to the next available position.

flip()

The flip() method switches the mode of operation on the buffer from write -> read or read -> write. After this operation, two things happen:

  • In write mode -> read mode, position = 0, limit refers to the next position of the last element, and capacity does not change
  • If it is read -> write, the value in the put() method is restored

get()

The get() method reads a value in the buffer, position +1, and an exception is thrown if limit is exceeded, but it is important to note that the get(I) method does not change the value of position.

rewind()

This method can only be used in read mode. Using rewind() restores position, limit, and capacity to the values before get()

clean()

The clean() method restores each property in the buffer to its original state, position = 0, Capacity = limit. The buffer data will still exist, but will be overwritten by the next write.

compact()

This method is a ByteBuffer method, not a Buffer method. Compact compresses the unfinished data forward and then switches to write mode. When the data is moved forward, the original value is not cleared and the previous value is overwritten.

Mark () and reset ()

The mark() method saves the postion value to the mark property. Mark makes a mark to record the position. Reset () method changes the position value to the value saved in mark.

Buffer and string conversion to and from

Methods a

Encoding: The string calls the getByte method to get the byte array, which is put into the ByteBuffer

Decoder: Call the flip method of ByteBuffer (because it is in write mode and cannot read data), then decode ByteBufferUtil. DebugAll (buffer1) with the decoder method of StandardCharsets. Is a statement that visualizes the buffer

public class Translate {
    public static void main(String[] args) {
        // String -> buffer
        String str1 = "hello";
        ByteBuffer buffer1 = ByteBuffer.allocate(16);
        // Get the array of bytes through the string getByte method and put it in the buffer
        buffer1.put(str1.getBytes());
        ByteBufferUtil.debugAll(buffer1);// Buffer visualization

        // Buffer -> string
        buffer1.flip(); // Switch mode
        
        // Decode StandardCharsets to get CharBuffer and toString to get string
        String str2 = StandardCharsets.UTF_8.decode(buffer1).toString();
        System.out.println(str2);
        ByteBufferUtil.debugAll(buffer1);// Buffer visualization}}Copy the code

Results presentation (using the ByteBuffer visualization tool) :

Method 2

Encoding: ByteBuffer is obtained through the encode method of StandardCharsets. The ByteBuffer obtained at this time is in read mode without switching mode through flip

Decoder: decoder method by StandardCharsets

public class Translate {
    public static void main(String[] args) {
        // String -> buffer
        String str1 = "hello";
        // ByteBuffer is obtained through the encode method of StandardCharsets. The obtained ByteBuffer is in read mode
        ByteBuffer buffer1 = StandardCharsets.UTF_8.encode(str1);
        ByteBufferUtil.debugAll(buffer1);// Buffer visualization

        // Buffer -> string
        // Decode StandardCharsets to get CharBuffer and toString to get string
        String str2 = StandardCharsets.UTF_8.decode(buffer1).toString();
        System.out.println(str2);
        ByteBufferUtil.debugAll(buffer1);// Buffer visualization}}Copy the code

The results show

Methods three

Encoding: The string calls the getByte() method to get the byte array, which is passed to the wrap() method of ByteBuffer, which gets the ByteBuffer. There is also no need to call the flip method to switch to read mode.

Decoder: decoder method by StandardCharsets

public class Translate {
    public static void main(String[] args) {
        // String -> buffer
        String str1 = "hello";
        

        // ByteBuffer is obtained through the encode method of StandardCharsets. The obtained ByteBuffer is in read mode
        ByteBuffer buffer1 = ByteBuffer.wrap(str1.getBytes());
        ByteBufferUtil.debugAll(buffer1);

        // Buffer -> string
        // Decode StandardCharsets to get CharBuffer and toString to get stringString str2 = StandardCharsets.UTF_8.decode(buffer1).toString(); System.out.println(str2); ByteBufferUtil.debugAll(buffer1); }}Copy the code