This is almost certainly performance reasons. For example, imagine a parser that goes through a 500k ByteBuffer containing strings.
There are 3 approaches to returning the string content:
Build a String[] at parse time, one character at a time. This will take a noticeable amount of time. We can use == instead of .equals to compare cached references.
Build an int[] with offsets at parse time, then dynamically build String when a get() happens. Each String will be a new object, so no caching returned values and using ==
Build a CharSequence[] at parse time. Since no new data is stored (other than offsets into the byte buffer), the parsing is much lower that #1. At get time, we don't need to build a String, so get performance is equal to #1 (much better than #2), as we're only returning a reference to an existing object.
In addition to the processing gains you get using CharSequence, you also reduce the memory footprint by not duplicating data. For example, if you have a buffer containing 3 paragraphs of text, and want to return either all 3 or a single paragraph, you need 4 Strings to represent this. Using CharSequence you only need 1 buffer with the data, and 4 instances of a CharSequence implementation that tracks the start and length.