Main Page

Previous Next

Buffers

All the classes that define buffers have the abstract class Buffer as a base. The Buffer class therefore defines the fundamental characteristics common to all buffers. A particular buffer can store a sequence of elements of a given type, and an element can be of any primitive data type other than boolean. Thus, you can create buffers to store byte values, char values, short values, int values, long values, float values, or double values. The following classes in the java.nio package define these buffers:

Class

Description

ByteBuffer

A buffer that stores elements of type byte. You can also store the binary values of any of the other primitive types in this buffer, except for type boolean. Each binary value that you store will occupy a number of bytes in the buffer determined by the type – values of type char or short will occupy two bytes, int values will occupy four bytes, and so on. Only buffers of this type can be used in a file I/O operation.

CharBuffer

A buffer that only stores elements of type char.

ShortBuffer

A buffer that only stores elements of type short.

IntBuffer

A buffer that only stores elements of type int.

LongBuffer

A buffer that only stores elements of type long.

FloatBuffer

A buffer that only stores elements of type float.

DoubleBuffer

A buffer that only stores elements of type double.

We keep repeating except for type boolean every so often, so we had better address that. The various types of buffers only provide for the numerical data types and type boolean does not fit into this category. Of course, you may actually want to record some boolean values in a file. In this case, you have to devise a suitable alternative representation. You could use integer values 0 and 1, or perhaps strings "true" and "false", or even characters 't' and 'f'. You could even represent a boolean value as a single bit and pack eight of them at a time into a single byte, but this is only likely to be worthwhile if you have a lot of them. Which approach you choose will depend on what is most convenient in the context in which you are using them.

While we have seven different classes defining buffers, a channel only uses buffers of type ByteBuffer to read or write data. The other types of buffers in the table above are called view buffers, because they are usually created as views of an existing buffer of type ByteBuffer. We will see how and why a little later in this chapter.

Buffer Capacity

Each type of buffer stores elements of a specific kind – a ByteBuffer object holds bytes, a LongBuffer object holds integers of type long, and so on for the other buffer types. The capacity of a buffer is the maximum number of elements it can contain, not the number of bytes – unless, of course, it stores elements of type byte. The capacity of a buffer is fixed when you create it and cannot be changed subsequently. You can obtain the capacity for a buffer object as a value of type int by calling the capacity() method that it inherits from the Buffer class.

Click To expand

Of course, for a buffer that stores bytes, the capacity will be the maximum number of bytes it can hold, but for a buffer of type DoubleBuffer for instance that stores double values, the capacity will be the maximum number of double values you can put in it. Elements in a buffer are indexed from zero so the index position for referencing elements in a buffer runs from 0 to capacity-1.

Buffer Position and Limit

A buffer also has a limit and a position, both of which affect read/write operations executed by a channel using the buffer.

The position is the index position of the next buffer element to be read or written. This sounds a little strange, but keep in mind that a buffer can be for file input or output. For example, with a buffer used for output, the position identifies the next element to be written to the file. For a buffer used for file input, the position identifies where the next element read from the file will be stored.

The limit is the index position of the first element that should not be read or written. Thus, elements can be read or written starting with the element at position, and up to and including the element at limit-1. Thus if you want to fill all the elements in a buffer, the position must be at zero since this is where the first data item will go, and the limit must be equal to the capacity since the last data item has to be stored at the last element in the buffer, which is capacity-1.

A buffer's position and limit are used for determining what elements are involved in a read or write operation executed by a channel. How they affect I/O operations is easier to understand if we take a specific example. Let's first consider an operation that writes data from the buffer to a file.

Click To expand

When a file write operation is executed by a channel using a given buffer, elements from the buffer will be written to the file starting at the index specified by the position. Successive elements will be written to the file up to, and including, the element at index position limit-1. For a read operation, data that is read from the file is stored in a buffer starting at the element index given by the buffer position. Elements will continue to be read, assuming they are available from the file, up to the index position limit-1. Thus when you want to write all the data from a buffer, the limit will have to be equal to the capacity. In this case the limit will be an index value that is one beyond the index value for the last element in the buffer, so limit-1 will refer to the last element.

The position and limit are involved when you load data into a buffer or retrieve data from it. The position specifies where the next element should be inserted in a buffer or retrieved from it. As we shall see, you will usually have the position automatically incremented to point to the next available position when you insert or extract elements in a buffer. The limit acts as a constraint to indicate where the data in a buffer ends, a bit like an end-of-file marker. You cannot insert or extract elements beyond the position specified by the limit.

Since a buffer's position is an index, it must be greater than or equal to zero. You can also deduce that it must also be less than or equal to the limit. Clearly, the limit cannot be greater than the capacity of a buffer. Otherwise, we could be trying to write elements to positions beyond the end of the buffer. However, as we have seen, it can be equal to it. These relationships can be expressed as:

0 • position • limit • capacity

As a general rule, if your code attempts to do things directly or indirectly that result in these relationships being violated, an exception will be thrown.

When you create a new independent buffer, its capacity will be fixed at a value that you specify. It will also have a position of zero and its limit will be set to its capacity. When you create a view buffer from an existing ByteBuffer, the contents of the view buffer starts at the current position for the ByteBuffer. The capacity and limit for the view buffer will be set to the limit for the original buffer, divided by the number of bytes in an element in the view buffer. The limit and position for the view buffer will subsequently be independent of the limit and position for the original buffer.

Setting the Position and Limit

You can set the position and limit for a buffer explicitly by using the following methods defined in the Buffer class:

Method

Description

position(int newPosition)

Sets the position to the index value specified by the argument. The new position value must be greater than or equal to zero, and not greater than the current limit, otherwise an exception of type IllegalArgumentException will be thrown. If the buffer's mark is defined (we will come to the mark in the next section) and greater than the new position, it will be discarded.

limit(int newLimit)

Sets the limit to the index value specified by the argument. If the buffer's position is greater than the new limit it will be set to the new limit. If the buffer's mark is defined and exceeds the new limit it will be discarded. If the new limit value is negative or greater than the buffer's capacity, an exception of type IllegalArgumentException will be thrown.

Both of these methods return a reference of type Buffer for the object for which they were called. This enables you to chain them together in a single statement. For instance, given a buffer reference, buf, you could set both the position and the limit with the statement:

buf.limit(512).position(256);

This assumes the capacity of the buffer is at least 512 elements. If you are explicitly setting both the limit and the position you should always choose the sequence in which you set them to avoid setting a position that is greater than the limit. If the buffer's limit starts out less than the new position you want to set, attempting to set the position first will result in an IllegalArgumentException being thrown. Setting the limit first to a value less than the current position will have a similar effect. If you want to avoid checking the current limit and position when you want to reset both, you can always do it safely like this:

buf.position(0).limit(newLimit).position(newPosition);

Of course, the new position and limit values must be legal; otherwise an exception will still be thrown. In other words newPosition must be non-negative, and less than newLimit. To be 100% certain setting a new position and limit is going to work, you could code it something like this:

if(newPosition >= 0 && newLimit > newPosition)
  buf.position(0).limit(newLimit).position(newPosition);
else
  System.out.printn("Illegal position:limit settings." 
                  + "Position: " + newPosition + " Limit: "+ newLimit);
  

You can determine whether there are any elements between the position and the limit in a buffer by calling the hasRemaining() method for the buffer:

if (buf.hasRemaining()) {   // If limit-position is >0
  System.out.println("We have space in the buffer!");
}

You can also find out how many elements can be accommodated using the remaining() method. For example:

System.out.println("The buffer can accommodate " + buf.remaining() +
                                                        " more elements.");

Of course, the value returned by the remaining() method will be the same as the expression buf.limit()-buf.position().

Creating Buffers

None of the classes that define buffers have constructors available. Instead, you use a static factory method to create a buffer. You will typically create a buffer object of type ByteBuffer by calling the static allocate() method for the class. You pass a value of type int as an argument to the method that defines the capacity of the buffer – the maximum number of elements the buffer must accommodate. For example:

// Buffer of 1024 bytes capacity
ByteBuffer buf =  ByteBuffer.allocate(1024);

When you create a new buffer using the allocate() method for the Buffer class, it will have a position of zero and its limit will be set to its capacity. This buffer will therefore have a position of 0, and a limit and capacity of 1024.

You can also create other types of buffers in the same way. For instance:

// Buffer stores 100 float values
FloatBuffer floatBuf =  FloatBuffer.allocate(100);

Since the elements are of type float, the data in this buffer will occupy 400 bytes. Its position will be 0, and its limit and capacity will be 100.

In practice, you are unlikely to want to create buffers other than ByteBuffer objects in this way, since you cannot use them directly for channel I/O operations. You will usually create a ByteBuffer first, and then create any view buffers that you need from this buffer.

View Buffers

You can use a ByteBuffer object to create a buffer of any of the other types we have introduced, that shares all or part of the memory that the original ByteBuffer uses to store data. Such a buffer is referred to as a view buffer, because it allows you to view the contents of the byte buffer as elements of another data type. Data is always transferred to or from a file as a series of bytes, but it will typically consist of data elements of a mix of types other than type byte. A view buffer therefore has two primary uses, for loading data items that are not of type byte into a byte buffer prior to writing it to a file, and accessing data that has been read from a file as elements that are other than type byte.

We could create a view buffer of type IntBuffer from a ByteBuffer object like this:

ByteBuffer buf = ByteBuffer.allocate(1024); // Buffer of 1024 bytes capacity
IntBuffer intBuf = buf.asIntBuffer();       // Now create a view buffer

The content of the view buffer, intBuf, that we create here will start at the byte buffer's current position, which in this case is zero since it is newly created. The remaining bytes in buf will effectively be shared with the view buffer. At least, the maximum number of them that is a multiple of 4 will be, since intBuf stores elements of type int that require 4 bytes each. The view buffer will have an initial position of 0, and a capacity and limit of 256. This is because 256 elements of type int completely fill the 1024 bytes remaining in buf. If we had allocated buf with 1023 bytes, then intBuf would have mapped to 1020 bytes of buf, and would have a capacity and limit of 255.

You could now use this view buffer to load the original buffer with values of type int. You could then use the original byte buffer to write the int values to a file. As we said at the outset, view buffers have a similar role when you are reading a file. You would have a primary buffer of type ByteBuffer, into which you read bytes from a file, and then you might access the contents of the ByteBuffer through a view buffer of type DoubleBuffer so you can retrieve the data read from the file as type double.

The ByteBuffer class defines the following methods for creating view buffers:

Method

Description

asCharBuffer()

Returns a reference to a view buffer of type CharBuffer.

asShortBuffer()

Returns a reference to a view buffer of type ShortBuffer.

asIntBuffer()

Returns a reference to a view buffer of type IntBuffer.

asLongBuffer()

Returns a reference to a view buffer of type LongBuffer.

asFloatBuffer()

Returns a reference to a view buffer of type FloatBuffer.

asDoubleBuffer()

Returns a reference to a view buffer of type DoubleBuffer.

asReadOnlyBuffer()

Returns a reference to a read-only view buffer of type ByteBuffer.

In each case, the view buffer's contents start at the current position of the original byte buffer. The position of the view buffer itself is initially set to zero, and its capacity and limit are set to the number of bytes remaining in the original byte buffer divided by the number of bytes in the type of element that the view buffer holds. The diagram below illustrates a view buffer of type IntBuffer that is created after the initial position of the byte buffer has been incremented by 2, possibly after inserting a char value into the byte buffer:

Click To expand

You can create as many view buffers from a buffer of type ByteBuffer as you want, and they can overlap or not as you require. A view buffer always maps to bytes in the byte buffer starting at the current position. You will frequently want to map several different view buffers to a single byte buffer each providing a view of a different segment of the byte buffer:

Click To expand

The diagram illustrates a byte buffer with a view buffer of type IntBuffer mapped to the first 8 bytes, and a view buffer of type CharBuffer mapped to the last 12 bytes. All you need to do to achieve this is to ensure the position of the byte buffer is set appropriately before you create each view buffer.

Duplicating and Slicing Buffers

You can duplicate any of the buffers we have discussed by calling the duplicate() method for a buffer. The method returns a reference to a buffer with the same type as the original, which shares the contents. The duplicate buffer initially has the same capacity, position, and limit as the original. However, although changes to the contents of the duplicate will be reflected in the original – and vice versa, each buffer's position and limit are independent of the other. One use for a duplicate buffer is when you want to access different parts of the buffer's contents concurrently. You can retrieve data from a duplicate buffer without affecting the original buffer in any way.

Click To expand

Thus a duplicate buffer is not really a new buffer in memory. It is just a new object that provides an alternative route to accessing the same block of memory that is being used to buffer the data. The duplicate() method returns a reference of a new object of the same type as the original, but has no independent data storage. It merely shares the memory that belongs to the original buffer object but with independent position and limit values.

You can also slice any of the buffers we have seen. Calling the slice() method for a buffer will return a reference to a new buffer object of the same type as the original that shares the elements remaining in the original buffer. In other words, it maps to a part of the original buffer starting at the element at its current position, up to and including the element at limit-1. Of course, if the position of the original buffer object is zero and the limit is equal to the capacity, the slice() method effectively produces the same result as the duplicate() method, that is the entire buffer will be shared. Slicing a buffer gives you access to the data in a given part of a buffer through two or more separate routes, each with its own independent position and limit.

Creating Buffers by Wrapping Arrays

You can also create a buffer by wrapping an existing array of the same type as the buffer elements by calling one of the static wrap() method in the Buffer class. This creates a buffer that already contains the data in the array. For example, you could create a ByteBuffer object by wrapping an array of type byte[], like this:

String saying = "Handsome is as handsome does.";
byte[] array = saying.getBytes();   // Get string as byte array
ByteBuffer buf = ByteBuffer.wrap(array);

Of course, you could convert the string to a byte array and create the buffer in a single statement:

ByteBuffer buf = ByteBuffer.wrap(saying.getBytes());

In any event, the buffer object will not have memory of its own to store the data. The buffer will be backed by the byte array that we have used to define it so modifications to the elements in the buffer will alter the array, and vice versa. The capacity and limit for the buffer will be set to the length of the array and its position will be zero.

You can also wrap an array to create a buffer so that the position and limit correspond to a particular sequence of elements in the array. For example:

String saying = "Handsome is as handsome does.";
byte[] array = saying.getBytes();   // Get string as byte array
ByteBuffer buf = ByteBuffer.wrap(array, 9, 14);

This creates a buffer by wrapping the whole array as before, but the position and limit are set using the second and third argument, in effect specifying the subsection of the array that can be read or written.

Click To expand

The buffer's capacity will be array.length and the position is set to the value of the second argument, 9. The third argument specifies the number of buffer elements that can be read or written so this value will be added to the position to define the limit. If either the second argument value or the sums of the second and third argument values do not represent legal index values for the array, then an exception of type IndexOutOfBoundsException will be thrown.

You can also wrap arrays of other basic types to create a buffer of the corresponding type. For example:

long[] numbers = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
LongBuffer numBuf = LongBuffer.wrap(numbers);

The buffer of type LongBuffer that we create will have a capacity of array.length, which will be 11. The buffer position will be set to 0 and the limit will be set to the capacity. In a similar manner we can create buffers from arrays of any of the other basic types with the exception of type boolean.

If a buffer object has been created from an array, you can obtain a reference to the backing array that is storing the data in the buffer by calling array() for the buffer. For example, for the buffer created by the previous code fragment, we could obtain a reference to the original array like this:

long[] data = numBuf.array();

The variable data will now contain a reference to the original array, numbers, which we used to create numBuf. If the buffer had not been created from an array, the array() method will throw an exception of type UnsupportedOperationException.

If a buffer object is passed to a method as an argument, you might need to determine whether or not it has a backing array – before you can call its array() method for example, if you plan to alter the buffer's contents by changing the elements in the array. The hasArray() method for a buffer object will return true if the buffer was created from an array, and false otherwise. Typical usage of this method is something like this:

if(numBuf.hasArray()) {
  long[] data = numBuf.array();
  // Modify the data array directly to alter the buffer...

} else {
  // Modify the buffer using put() methods for the buffer object...
}

Obviously you would only take the trouble to do this if modifying the backing array was a whole lot more convenient or faster than using put() methods for the buffer. We will see how we use put() methods to modify a buffer's contents very soon.

You can create buffers of type CharBuffer by wrapping an object of type String, as well as an array of type char[]. For example:

String wisdom = "Many a mickle makes a muckle.";
CharBuffer charBuf = CharBuffer.wrap(wisdom);

Since a String object is immutable, the buffer that results from this is read-only. Attempting to transfer data into the buffer will result in an exception of type ReadOnlyBufferException being thrown.

Marking a Buffer

You use the mark property for a buffer to record a particular index position in the buffer, which you want to be able to return to later. You can set the mark to the current position by calling the buffer object's mark() method that is inherited from the Buffer class. For example:

buf.mark();

This method also returns a reference of type Buffer so you could chain it with the methods for setting the limit and position:

buf.limit(512).position(256).mark();

This will set the mark to 256, the same as the position.

After a series of operations that alter the position, you can reset the buffer's position to the mark that you have set by calling the reset() method that is inherited from the Buffer class:

buf.reset();

If you have not set the mark, or it has been discarded by an operation to set the limit or the position, the reset() method will throw an exception of type InvalidMarkException. The mark for a view buffer operates independently of the mark for the buffer from which it was created.

You probably won't need to mark a buffer most of the time. You would typically use it in a situation where you are scanning some part of a buffer to determine what kind of data it contains, after reading a file for instance. You could mark the point where you started the analysis, and then return to that point by calling reset() for the buffer when you have figured out how to deal with the data.

Buffer Data Transfers

Of course, before you can use a channel to write the contents of a buffer to a file, you need to load the buffer with the data. Methods for loading data into a buffer are referred to as put methods. Similarly, when a channel has read data from a file into a buffer, you are likely to want to retrieve the data from the buffer. In this case you use the buffer's get methods.

There are two kinds of operations that transfer data elements to or from a buffer. A relative put or get operation transfers one or more elements starting at the buffer's current position. In this case the position is automatically incremented by the number of elements transferred. In an absolute put or get operation, you explicitly specify an index for the position in the buffer where the data transfer is to begin. In this case the buffer's position will not be updated so it will remain at the index value it was before the operation was executed.

Transferring Data Into a Buffer

The ByteBuffer class and all the view buffer classes have two put() methods for transferring a single element of the buffer's type to the buffer. One is a relative put() method that transfers an element to a given index position in the buffer and the other is an absolute put method that places the element at an index position that you specify as an argument. They also have three relative put methods for bulk transfer of elements of the given type. Let's consider the put() methods for a ByteBuffer object as an example.

Method

Description

put(byte b)

Transfers the byte specified by the argument to the buffer at the current position, and increments the position by 1. An exception of type BufferOverflowException will be thrown if the buffer's position is not less than its limit.

put(int index,

byte b)

Transfers the byte specified by the second argument to the buffer at the index position specified by the first argument. The buffer position is unchanged. An exception of type IndexOutOfBoundsException will be thrown if the index value is negative or greater than or equal to the buffer's limit.

put(byte[]array)

Transfers all the elements of array to this buffer starting at the current position. The position will be incremented by the length of the array. An exception of type BufferOverflowException will be thrown if there is insufficient space in the buffer to accommodate the contents of array.

put(byte[]array,

int offset,

int length)

Transfers elements array[offset] to array[offset+length-1] inclusive to the buffer. If there is insufficient space for them an exception of type BufferOverflowException will be thrown.

put(ByteBuffer src)

Transfers the bytes remaining in src to the buffer. This will be src.remaining() elements from the buffer src from its position index to limit-1. If there is insufficient space to accommodate these then an exception of type BufferOverflowException will be thrown. If src is identical to the current buffer – you are trying to transfer a buffer to itself in other words – an exception of type IllegalArgumentException will be thrown.

Each of these methods returns a reference to the buffer for which they were called. If the buffer is read-only, any of these methods will throw an exception of type ReadOnlyBufferException. We will see how a buffer can be read-only when we discuss Using View Buffers in more detail. Each Buffer class that stores elements of a given basic type – CharBuffer, DoubleBuffer, or whatever – will have put() methods analogous to these, but with arguments of a type appropriate to the type of element in the buffer.

The ByteBuffer class has some extra methods that enable you to transfer binary data of other primitive types to the buffer. For instance, you can transfer a value of type double to the buffer with either of the following methods:

Method

Description

putDouble(double value)

Transfers the double value specified by the argument to the buffer at the current position and increments the position by 8. If there are less than 8 bytes remaining in the buffer, an exception of type BufferOverflowException will be thrown.

putDouble(int index,

double value)

Transfers the double value specified by the second argument, to the buffer starting at the index position specified by the first argument. The buffer's position will be unchanged. If there are less than 8 bytes remaining in the buffer, an exception of type BufferOverflowException will be thrown. If index is negative or the buffer's limit is less than or equal to index+7, an exception of type IndexOutOfBoundsException will be thrown.

There are similar pairs of methods defined in the ByteBuffer class to transfer elements of other basic types. These are putChar(), putShort(), putInt(), putLong(), and putFloat(), each of which transfers a value of the corresponding type. Like the other put() methods we have seen, these all return a reference to the buffer for which they are called. This is to enable you to chain the calls for these methods together in a single statement if you wish. For instance:

ByteBuffer buf = ByteBuffer.allocate(50);
String text = "Value of e";
buf.put(text.getBytes()).putDouble(Math.E);

Here, we write the string to the buffer by converting it to bytes by calling its getBytes() method, and passing the result to the put() method for the buffer. The put() method returns a reference to the buffer, buf, so we use that to call the putDouble() method to write the 8 bytes for the double value, Math.E, to the buffer. The buffer will then contain a total of 18 bytes. Of course, putDouble() also returns a reference to buf, so we could chain further calls together in the same statement if we so wished.

Note that we are transferring the string characters to the buffer as bytes in the local character encoding, not as Unicode characters. To transfer them as the original Unicode characters, we could code the operations like this:

char[] array = text.toCharArray();    // Create char[] array from the string
// Now use a loop to transfer array elements one at a time
for (int i = 0 ; i< array.length ; i++) {
  buf.put(array[i]);
}
buf.putDouble(Math.E);                // Transfer the binary double value

Using View Buffers

View buffers are intended to make it easier to transfer data elements of various basic types to or from a ByteBuffer. The only slightly tricky part is that you have to keep track of the position for the original ByteBuffer object yourself when you use a view buffer, since operations with the view buffer will not update the position for the backing byte buffer. We could do what the previous code fragment does using view buffers:

ByteBuffer buf = ByteBuffer.allocate(50);      // The original byte buffer
String text = "Value of e";

// Create view buffer
CharBuffer charBuf = buf.asCharBuffer();
// Transfer string via view buffer
charBuf.put(text);
// Update byte buffer position by the number of bytes we have transferred
buf.position(buf.position() + 2*charBuf.position());
// Transfer binary double value
buf.putDouble(Math.E);

Putting data into a view buffer with a relative put operation only updates the position of the view buffer. The position for the backing ByteBuffer is unchanged, so we must increment it to account for the number of bytes occupied by the Unicode characters we have written. Since we transfer the eight bytes for the constant Math.E directly using buf, the position will be incremented by 8 automatically.

Preparing a Buffer for Output to a File

We have seen that a buffer starts out with a position set to 0 – the first element position – and with its limit set to the capacity. Suppose we create a buffer with the statement:

ByteBuffer buf = ByteBuffer.allocate(80);

We can now create a view buffer that can store values of type double with the statement:

DoubleBuffer doubleBuf = buf.asCharBuffer();

The view buffer's initial state will be as shown below:

Click To expand

The limit is automatically set to the capacity, 10, so it points to one element beyond the last element. We could load six values of type double into this buffer with the following statements:

double[] data = { 1.0, 1.414, 1.732, 2.0, 2.236, 2.449 };
doubleBuf.put(data);            // Transfer the array elements to the buffer

This operation automatically increments the position for the buffer. Now the buffer will be as shown below:

Click To expand

The position and limit values are now set to values ready for more data to be added to the buffer. The value of position points to the first empty element and limit points to one beyond the last empty element. Of course, the position for the backing ByteBuffer is still in its original state, but we can update that to correspond with the data we have loaded into the view buffer with the statement:

buf.setPosition(8*doubleBuf.getPosition());

If we now want to write the data we have in the buffer to a file, we must change the values for position and limit in the byte buffer to identify the elements that are to be written. A file write operation will write data elements starting from the element in the buffer at the index specified by position, and up to and including the element at the index limit-1. To write our data to the file, the limit for the byte buffer needs to be set to the current position, and the position needs to be set back to zero. We could do this explicitly using the methods we have seen. For instance:

// Limit to current position and position to 0
buf.limit(buf.position()).position(0);

This will first set the limit to the byte referenced by the current position, and then reset the position back to the first byte, byte 0. However, we don't need to specify the operation in such detail. The Buffer class conveniently defines a method, flip(), that does exactly this, so you would normally set up the buffer to be written to a file like this:

// Limit to current position and position to 0
buf.flip();

The flip() method returns the buffer reference as type Buffer, so you can chain this operation on the buffer with others in a single statement. So, after you have loaded your byte buffer with data, don't forget to flip it before you write it to a file. If you don't, your data will not be written to the file, but garbage may well be. If you loaded the data using a view buffer, you also have to remember to update the byte buffer's position before performing the flip.

While we are here, let's cover two other methods that modify the limit and/or the position for a buffer. The clear() method sets the limit to the capacity and the position to zero, so it restores these values to the state they had when the buffer was created. This does not reset the data in the buffer though. The contents are left unchanged. You call the clear() method when you want to reuse a buffer, either to load new data into it, or to read data into it from a channel. The rewind() method simply resets the position to zero, leaving the limit unchanged. This enables you to reread the data that is in the buffer. Both of these methods are defined in the base class, Buffer, and both return a reference to the buffer of type Buffer so you can chain these operations with others that are defined in the Buffer class.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor