Java source code analysis: the Java NIO Buffer
In the previous post, we introduced the basic use of channel and buffer in Java NIO, which will look at the internal implementation of buffer from the source point of VIEW.
In the Java API documentation, the description of buffer is summarized as Follows:
Buffer: is a container for a specific base data type. The specific basic data type here refers to the other basically data types except the Boolean Type.
A buffer is a linear finite sequence of elements of a particular base data type. In addition to the content, the buffer Hungry Basic attribute also includes three important attributes, as Follows:
1, capacity (capacity): indicates the size of the buffer Capacity.
2. Limit (restriction): is the first index of an element that should not be read or Written. The limit for a buffer cannot be negative and cannot be greater than its Capacity.
3, position (position): indicates the next index of the element to read or Write. The position of the buffer cannot be negative and cannot be greater than its limit.
When you look at a document, you may not be able to understand the meaning of limit and position. After we have read the source, we will understand the ha, not Urgent.
however, before looking at the source code, you can advance to explain the next limit and position by drawing the Way.
There are two modes in buffer, one is write mode and one is read Mode.
There should be a better understanding of the meaning of position and limit.
Let's look at the source of the buffer below. Because buffer has a lot of subclasses, here is a intbuffer example.
Several important properties of buffer
// Invariants: mark <= position <= limit <= capacity privateint mark = -1; privateint0;//位置 privateint limit;//限制 privateint capacity;//容量
These attributes are the position, limit, capacity, and finally a mark that we have mentioned Above.
How to construct buffer
//constructor to initialize the buffer-specific properties according to the specified parameters //this Constructor When the package is privateBuffer (intMarkintPosintLimintCap) {//package-private if(cap <0)Throw NewIllegalArgumentException ("negative capacity:"+ cap); this. Capacity = cap; Limit (lim); Position (pos);if(mark >=0) {if(mark > POS)Throw NewIllegalArgumentException ("mark > Position: ("+ Mark +">"+ pos +")"); this. Mark = mark; } }
The logic in the constructor is fairly simple, which is an initialization, but includes the relevant validity checks Inside.
要注意的是此构造函数是包私有的。
Maybe someone like me, there is a question: the buffer class is an abstract class, why is there a constructor?
Since there is a constructor in the abstract class, it is the first time that I have seen it. The relevant information is therefore consulted. Found as Follows:
1, the abstract class can have a constructor function. But many people think that constructors are used to instantiate an object (or to create an instance of an object), and abstract classes cannot be instantiated, so abstract classes should not have public constructors. But there should be no "public" constructors, and there should be no constructors, which is two different concepts, so if the abstract class needs constructors, then it should be declared as "protected".
2, since the abstract class is can, and sometimes should have a constructor, that abstract class of the function of the function is what? I think there are at least two of them:
(1)初始化抽象类的成员;(2)为继承自它的子类使用。在Buffer这个抽象类中我们可以明显的感受到以上两点的作用。
3. Even if we declare an abstract class without constructors, the compiler will generate a default protection level constructor for Us. When a subclass is instantiated (whether or not it is constructed with a parameter) only the parameterless constructor of all the parent classes is called, and the parameter construct must be called Explicitly. the call order is the parameterless constructor that calls the abstract class First. If the subclass is instantiated with a meal-taking constructor, then the parameter constructor for the abstract class is called, and the constructor of the subclass itself is called Finally.
In the constructor, two functions are involved: limit (int newlimit) and position (int newposition), as Follows:
//function: Set the limit of buffer and if position is greater than newlimit, set the position to the new limit. //if mark is defined and is greater than the new limit, it will be discarded. public FinalBufferLimit(intNewlimit) {//validity check, that is, limit must satisfy such a relationship: 0<=limit<=position. if((newlimit > Capacity) | | (newlimit <0))Throw NewIllegalArgumentException (); Limit = newlimit;//if position is greater than newlimit, the position is set to the new Limit. if(position > Limit) position = limit;//if mark is defined and is greater than the new limit, it will be discarded (that is, set To-1) if(mark > Limit) Mark =-1;return this; }
Limit (int Newlimit) function: simply set limit.
//function function: Set the position of Buffer. if Mark is defined and is greater than new position, it will be abandoned. public final Buffer position (int newposition) {// Validity check, i.e. 0<=newposition<=limit. if ((newposition > Limit) | | (newposition < 0 )) throw new IllegalArgumentException (); Position = newposition; //if mark is defined and greater than the new position, it will be discarded. if (mark > Position) Mark =-1 ; return this ; }
Position (int Newposition) The function is simple: set Position.
These two functions are relatively simple ha, without any Difficulty.
Introduction to the common methods in buffer
In the demo code of the previous blog post, we will see that the following functions are used in buffer:
1. Allocate: Buffer Object allocated for a space
2. Put: used to add elements to buffer
3, Flip (): for converting write mode to read mode
4. Hasremaining: Determine if there are elements readable in buffer
5. Get (): reads the elements of the position position in buffer
6. Clear () or compact () method: clear the element
Below we mainly look at the source code of the above several methods
1, Allocate (int Cap) method Introduction
Because the buffer class is an abstract class and cannot instantiate an object, the allocate (int Cap) method is not present in buffer, and the allocate (int Cap) method is implemented in its Subclasses. Take Intbuffer as an example and look at the allocate (int Cap) method of the buffer subclass Intbuffer.
publicstaticallocate(int capacity) { if0) thrownew IllegalArgumentException(); returnnew HeapIntBuffer(capacity, capacity); }
function Function: allocates a new instance of the buffer object that is used to load data of type int. The position of this new buffer is 0,limit for Capacity,mark (i.e.-1). The elements in buffer are all initialized to Zero.
The object of a Intbuffer subclass is instantiated directly in the allocate method.
Since this involves the Heapintbuffer class, let's take a look at this class
publicabstractclass IntBuffer extends Buffer implements Comparable<IntBuffer> class HeapIntBuffer extends IntBuffer
From the inheritance relationship we know: Heapintbuffer This class is a subclass of Intbuffer
The constructor for this class is
HeapIntBuffer(intcapint lim) { // package-private super(-1, 0capnewint[cap], 0); }
In this constructor, the corresponding constructor in the parent class Intbuffer is called Directly. Look at this constructor in the Intbuffer class.
Before looking at the constructor, here are a few properties in the Intbuffer class.
finalint[] hb; // Non-null only for heap buffers finalint offset; boolean isReadOnly; // Valid only for heap buffers
The Intbuffer class consists of an array of type int, which we know from here that the underlying data structure of the buffer class is done using Arrays.
Continue to see the construction method of Intbuffer class
// Creates a new buffer with the given mark, position, limit, capacity, // backing array, and array offset // IntBuffer(intintintint cap, // package-private intint offset) { super(mark, pos, lim, cap);//调用父类Buffer中对应有参的构造函数 this.hb = hb; this.offset = offset; }
The above, is when we use the following code to get the Intbuffer instance of the entire process.
Intbuffer buffer = Intbuffer.allocate (cap);
2, put (int I) method Introduction
Let's look at the put method in the Intbuffer class
publicabstractput(int i); publicabstractintget(int index);
The put, get methods in the Intbuffer class are Abstract.
With the process analysis of the Allocate method above, we know that intbuffer buffer = Intbuffer.allocate (cap)
The buffer in the parent class is actually the object of the child class that the reference refers to. When we use Buffer.put (value)/buffer.get ().
is actually called the Put/get method in the subclass Heapintbuffer class, which is Polymorphic. A feature that is quite important in Java.
The Put method in the Heapintbuffer class is implemented as Follows:
publicput(int x) { hb[ix(nextPutIndex())] = x; returnthis; }
The idea of the put method is to store the value in the position Location.
Here are two new functions, respectively:
1, nextputindex (), function function: in a nutshell, return the next index position to write the element (that is, the current position value), and add the position to the Action.
2, IX (int i): offsets offset positions
The implementations of these two functions are as follows:
//函数功能:首先检查当前的position是否小于limit,如果小于则存储,否则抛异常。 finalint nextPutIndex() { // package-private if (position >= limit) thrownew BufferOverflowException(); return position++; } //函数功能:偏移offset个位置 protectedintix(int i) { return i + offset; }
3, Get () Method Introduction
After reading the put method, let's take a look at the Get method
Function: gets the element pointed to by the position position in Buffer.
//函数功能:获取buffer中position所指向的元素。 publicintget() { return hb[ix(nextGetIndex())]; } //函数功能:获取buffer中索引为i位置的元素 publicintget(int i) { return hb[ix(checkIndex(i))]; }
Two additional functions are also involved in the Get () method:
1, nextgetindex (), function function: returns the index position of the next read element (that is, the position Value)
2, IX (int I)
Final intNextgetindex () {//package-private if(position >= Limit)Throw NewBufferunderflowexception ();returnposition++; }//move position to the right NB position, This function has not yet seen where to get the application Final intNextgetindex (intNb) {//package-private if(limit-position < Nb)Throw NewBufferunderflowexception ();intp = position; Position + = nb;returnP }
The above is the internal implementation of the get method, which is quite simple, huh.
4. Flip () Method Introduction
In fact, at the beginning of the first look at other People's blog, is the most unable to understand the function of functions, the question is: why to read the buffer in the content, the first call this Method.
In fact, after the buffer read mode and write mode to clear, This function I also understand the Function.
/** * Flips this buffer. The limit is set toThe current position and then* The position is set toZero. If the mark is defined thenIt is * discarded. * * <p> after a sequence ofChannel-reador<i>put</i> operations, Invoke * This method toPrepare forA sequence ofChannel-writeorRelative * <i>get</i> Operations. For example: * * <blockquote><pre> * buf.put (magic);//prepend Header*inch. Read (buf);//Read data into rest of buffer* Buf.flip ();//Flip Buffer* Out.write (buf);//Write header + data to channel</pre></blockquote>* *///function: converts buffer from write mode to read Mode. //specific: set limit to position at this time and set position to Zero. If Mark is defined, it is discarded (that is, set To-1) the scenario for this method is to call this method to prepare for a pipe write or get operation after the pipeline has read or called the put Method. publicFinal Buffer flip () {limit = position; Position =0; Mark =-1;returnThis }
The above is the flip () method of introduction, is not also relatively simple ha.
5, hasremaining () Introduction
function function: Determine if there are any readable elements in the Buffer.
/** * Tells whether there are any elements between the current position and * the limit. */ publicfinalbooleanhasRemaining() { return position < limit; }
The internal implementation of this method directly determines whether the position is less than limit.
so, logic is pretty simple, too.
6. Introduction to the Clear () compact () method
Clear () function: empties all the elements in the Buffer.
Its internal implementation is to reset several properties in buffer directly.
publicfinalclear() { 0; limit = capacity; mark = -1; returnthis; }
Finally look at the compact method
The compact () method Functions: clears the read elements, copies the unread elements, and copies them to the beginning of the Buffer. This is also different from the clear () method.
The specific implementation is as Follows:
Public IntbufferCompact (){//copy first, Copy the remaining non-accessible elements to the zero-based position of bufferSystem. Arraycopy(hb,IX (position ())HbIX (0),remaining ());the//remaining () function returns the number of remaining elements //set position to the position of the next write element position (remaining ());//set limit to buffer capacity limit (capacity ());Discardmark ();//discard mark, set Mark To-1Return this; } Public Final intremaining (){return limit-position; }
Summary
The above will be the internal implementation of the buffer to an article, after an article of the feeling is that its internal implementation logic is quite simple ha.
We only need to understand several important properties in Buffer: position, limit, capacity in the reading mode and the meaning of the write mode can be a good understanding of the internal implementation of the Buffer.
The next step is to look at the internal implementation of selector in Java nio.
Finish.
Java source code analysis: the Java NIO Buffer