What do slice(), duplicate(), and asReadOnlyBuffer() create, and how do their markers and backing storage relate to the original buffer?
answer
- all three share backing storage, have independent pos/limit/mark
- duplicate = full mirror (same capacity, copied markers)
- slice = window over [position, limit); index 0 = old position
- asReadOnlyBuffer = duplicate but put/compact -> ReadOnlyBufferException
- views are for zero-copy hand-off without cursor interference
basics
~20 sAll three make a new buffer that shares the same underlying data as the original (a window onto it) but has its own independent position, limit, and mark. duplicate() mirrors the whole buffer, slice() exposes only the part from position to limit, and asReadOnlyBuffer() makes a view you can read but not write.
solid answer
~50 sslice(), duplicate(), and asReadOnlyBuffer() all return a new Buffer that shares the SAME backing storage as the original — writes through one are visible through the others — but each carries its own independent position, limit, mark, and (for slice) a shifted origin. duplicate() gives a full mirror: same capacity and current position/limit values copied at creation, sharing content. slice() gives a window over just the original's remaining region [position, limit): the new buffer's position is 0, its capacity and limit equal the original's remaining count, and index 0 maps to the original's current position. asReadOnlyBuffer() is like duplicate() but rejects put()/compact() with ReadOnlyBufferException. Because the content is shared but the markers are not, these are ideal for handing a sub-region to another component without copying or disturbing your own cursor — e.g. parsing framed protocols.
go deeper
Knows these methods make another buffer that looks at the same data.
States that content is shared while position/limit/mark are independent, and that asReadOnlyBuffer prevents writes.
Explains slice's offset/remaining semantics vs duplicate's full mirror, the ReadOnlyBufferException behavior, and uses views for zero-copy framing.
Reasons about view-based zero-copy designs (protocol codecs, slab allocators), the lack of thread-safety/immutability guarantees, and trade-offs versus copying or absolute slice(index,length).
## The shared-content, independent-markers idea Sometimes you want a *second view* of a buffer's data without copying the bytes and without clobbering your own position/limit. NIO gives three view-creating methods. All of them obey the same two rules: 1. **Backing storage is SHARED.** The new buffer and the original point at the same underlying memory (the same `byte[]` for heap buffers, or the same native block for direct buffers). A `put` through one view is visible when you `get` through the other. Capacity changes are impossible (still fixed). 2. **Markers are INDEPENDENT.** Each view has its own `position`, `limit`, and `mark`. Moving the cursor on the view does not move it on the original, and vice versa. The three differ in *what region* the view covers and *whether it is writable*. ## duplicate() Returns a buffer with **the same capacity** and the **same position, limit, and mark values copied at the moment of the call**. After that the two cursors are independent. It is a full mirror of the original — same content, same byte ordering, same direct/read-only nature. Use it when you want to read the entire buffer from one place while another piece of code keeps its own cursor over the same data. ## slice() Returns a buffer that is a **window over only the original's currently accessible region** `[position, limit)`. Specifically, at the moment of the call: - the slice's **position is 0**, - its **capacity and limit equal `remaining()`** of the original (`limit - position`), - its **index 0 maps to the original's current position** (there is an internal offset). So if the original has position=8, limit=20, `slice()` produces a buffer of capacity 12 whose index 0 is the original's byte 8. This is the tool for carving out a sub-buffer (e.g. one frame of a framed protocol, or a header vs body split) and handing it off, while your own buffer's cursor stays where it was. (Java 13+ added `slice(index, length)` for an absolute sub-region independent of position/limit.) ## asReadOnlyBuffer() Like `duplicate()` (full mirror, shared content, copied markers) **except the view is read-only**: any `put`, `compact`, or `array()` call throws `ReadOnlyBufferException`. Reads still work and still see writes made through writable views of the same content. Use it to safely expose a buffer to untrusted/consuming code that should observe but never modify the data. You cannot go back: there is no `asWritableBuffer()`. ## Why "shared content, separate cursors" matters The whole point is **zero-copy hand-off without cursor interference**. Copying a sub-region would cost memory and time; sharing the position/limit would mean a consumer reading the data would corrupt your producer cursor. Views resolve both: the bytes are shared (cheap), the cursors are private (safe). A subtle consequence: because content is shared, a slice/duplicate **does not protect you from concurrent mutation** — if another thread writes the shared storage, your view sees it. Buffers are not thread-safe; views do not add safety, only separate cursors. ## Summary | method | region covered | writable? | markers | |---|---|---|---| | duplicate() | whole buffer (same capacity) | same as original | own copy of pos/limit/mark | | slice() | only [position, limit), index 0 = original position | same as original | position=0, capacity=limit=remaining | | asReadOnlyBuffer() | whole buffer | read-only (put -> ReadOnlyBufferException) | own copy of pos/limit/mark | All three share storage; none copies the bytes.
- If you write a byte through a duplicate(), is the change visible in the original?Yes. duplicate() shares the same backing storage, so a put through either view is visible through the other; only the position/limit/mark cursors are independent.
- How does slice() differ from duplicate() in the region it exposes?duplicate() mirrors the whole buffer with the same capacity; slice() exposes only the original's remaining region [position, limit), with the slice's index 0 mapped to the original's current position and capacity equal to remaining().
saying these in an interview costs you the question
- Thinking slice()/duplicate() copy the bytes (they share storage)
- Believing the views share position/limit with the original (markers are independent)
- Expecting slice() to cover the whole buffer (it covers only the remaining region)
- Trying to write through asReadOnlyBuffer() (throws ReadOnlyBufferException)
- Assuming a read-only view makes the data thread-safe or immutable (the shared storage can still be mutated via a writable view)