The performance optimization of the method depends in part on the JVM in use. For example, in OpenJDK substring is implemented as:
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
That string constructor is a protected form that is implemented as:
// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
Note that this doesn't need to create a new value (the char[] that backs the String).
On the other hand, as described in Java 7 Performance Tuning Guide this was removed because of a memory leak (the single character substring of a 1000 character long string that gets garbage collected still retains the 1000 character string backing it).
And so, the choice of which jvm you are using could have a significant effect upon the creation of strings from substrings.
Depending on your application, and how critical that performance is, you might consider implementing your own limited version of the String that reimplements the offset and length implementation for substring.