Java Language Specification — Strings and Methods
Source: https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.10.5
1. Spec Reference
- JLS §3.10.5: String Literals — https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.10.5
- JLS §3.10.6: Text Blocks — https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.10.6
- JLS §4.3.3: The Class String — https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.3.3
- JLS §8.4: Method Declarations — https://docs.oracle.com/javase/specs/jls/se21/html/jls-8.html#jls-8.4
- JLS §8.4.1: Formal Parameters
- JLS §8.4.2: Method Signature
- JLS §8.4.3: Method Modifiers
- JLS §8.4.4: Generic Methods
- JLS §8.4.5: Method Return Type
- JLS §8.4.6: Method Throws
- JLS §8.4.7: Method Body
- JLS §8.4.8: Inheritance, Overriding, Hiding
- JLS §15.18.1: String Concatenation Operator
+ - JLS §15.28: String Switch Expressions
-- JLS §3.10.5: String Literal --
StringLiteral:
" {StringCharacter} "
StringCharacter:
InputCharacter but not " or \
EscapeSequence
EscapeSequence:
\ b (backspace U+0008)
\ t (tab U+0009)
\ n (newline U+000A)
\ f (form feed U+000C)
\ r (carriage return U+000D)
\ " (double quote U+0022)
\ ' (single quote U+0027)
\ \ (backslash U+005C)
\ s (space U+0020) [Java 15+]
\ LineTerminator [line continuation, Java 15+]
OctalEscape
OctalEscape:
\ OctalDigit
\ OctalDigit OctalDigit
\ ZeroToThree OctalDigit OctalDigit
-- JLS §3.10.6: Text Block --
TextBlock:
""" {WhiteSpace} LineTerminator {TextBlockCharacter} """
TextBlockCharacter:
InputCharacter but not \
EscapeSequence
LineTerminator
-- JLS §8.4: Method Declaration --
MethodDeclaration:
{MethodModifier} MethodHeader MethodBody
MethodHeader:
Result MethodDeclarator [Throws]
TypeParameters {Annotation} Result MethodDeclarator [Throws]
MethodDeclarator:
Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
Result:
UnannType
void
FormalParameterList:
FormalParameter { , FormalParameter }
FormalParameter:
{VariableModifier} UnannType VariableDeclaratorId
VariableArityParameter
VariableArityParameter:
{VariableModifier} UnannType {Annotation} ... Identifier
MethodModifier:
Annotation
public | protected | private
abstract | static | final
synchronized | native | strictfp
Throws:
throws ExceptionTypeList
ExceptionTypeList:
ExceptionType { , ExceptionType }
ExceptionType:
ClassType
TypeVariable
MethodBody:
Block
;
-- JLS §15.18.1: String Concatenation --
-- When + is applied to a String operand, it becomes string concatenation.
-- Non-string operands are converted via String.valueOf().
-- The JLS permits the compiler to use StringBuilder or StringConcatFactory.
3. Core Rules & Constraints
3.1 String Immutability (JLS §4.3.3)
String objects are immutable — once created, their content cannot be changed. - All
String methods that appear to "modify" return new String instances. - The
String class is declared final — cannot be subclassed. String implements CharSequence, Comparable<String>, and Serializable.
3.2 String Interning (JLS §3.10.5)
- String literals are automatically interned (JLS §3.10.5, §4.3.3).
- All references to the same string literal refer to the same
String object. "hello" == "hello" is true (both interned). new String("hello") == "hello" is false (new object not interned by default). String.intern() returns the canonical interned instance.
3.3 String Concatenation (JLS §15.18.1)
- If either operand is a
String, + becomes string concatenation. null operands are converted to the string "null". - The expression is evaluated left to right.
javac may optimize using StringBuilder or invokedynamic with StringConcatFactory (Java 9+).
3.4 Text Blocks (JLS §3.10.6, Java 15+)
- Opening
""" must be followed by a line terminator. - The closing
""" determines the re-indentation anchor. - Incidental whitespace (common indentation) is stripped.
- Trailing whitespace on each line is stripped unless escaped with
\s. \ at end of line is a line-continuation escape — no newline inserted.
3.5 Method Declaration Rules (JLS §8.4)
abstract methods have no body (semicolon only); non-abstract methods must have a body. native methods have no Java body; implemented in native code. synchronized methods acquire the object's (or class's, if static) monitor before execution. - Varargs (
...) must be the last parameter; only one varargs per method. - Method overloading: same name, different parameter types (not just return type).
3.6 Method Overriding (JLS §8.4.8)
- Override requires: same name, same parameter types, return type is covariant (same or subtype).
@Override annotation is not required but triggers a compile error if the method does NOT override. - Overriding method's access modifier cannot be more restrictive than overridden method.
- Overriding method cannot declare new checked exceptions not declared by the overridden method.
4. Type Rules
4.1 Return Type Covariance (JLS §8.4.5)
- An overriding method may declare a return type that is a subtype of the overridden method's return type.
- Example:
Object clone() in Object can be overridden as Foo clone() in Foo. - This is called covariant return types (added in Java 5).
4.2 Method Signature (JLS §8.4.2)
- Signature consists of: method name + number and types of formal parameters (NOT return type, NOT thrown exceptions).
- Two methods with same name and parameter types are the same signature.
- Same signature methods cannot coexist in the same class (compile error) — unless one is bridge method (synthetic).
4.3 Varargs Type Rules (JLS §8.4.1)
void f(int... args) is equivalent to void f(int[] args) in the bytecode. f(1, 2, 3) compiles to f(new int[]{1, 2, 3}). f() compiles to f(new int[]{}). - Cannot overload
f(int[]) and f(int...) — same erasure.
4.4 Generic Methods (JLS §8.4.4)
- Type parameters declared before return type:
<T> List<T> singletonList(T t). - Type inference resolves
T from arguments at call site. - Bounds:
<T extends Comparable<T>> T max(T a, T b).
5. Behavioral Specification
5.1 String Comparison Behavior
== compares object references (identity). .equals() compares content (character-by-character). .equalsIgnoreCase() compares content ignoring case differences. .compareTo() returns lexicographic order (negative, 0, positive). .compareToIgnoreCase() — case-insensitive lexicographic order.
5.2 Core String Methods (from java.lang.String API)
| Method | Description | Returns |
length() | Number of UTF-16 code units | int |
charAt(int) | Code unit at index | char |
codePointAt(int) | Unicode code point at index | int |
substring(int, int) | Subsequence | String |
indexOf(String) | First occurrence | int (-1 if not found) |
contains(CharSequence) | Membership check | boolean |
replace(CharSequence, CharSequence) | Literal replacement (all occurrences) | String |
replaceAll(String, String) | Regex replacement | String |
split(String) | Splits by regex | String[] |
strip() | Removes leading/trailing whitespace (Unicode-aware, Java 11+) | String |
trim() | Removes ASCII whitespace (legacy) | String |
toUpperCase() | Uppercase (locale-sensitive) | String |
toLowerCase() | Lowercase (locale-sensitive) | String |
isEmpty() | Length == 0 | boolean |
isBlank() | All whitespace or empty (Java 11+) | boolean |
chars() | Stream of code units (Java 8+) | IntStream |
codePoints() | Stream of code points (Java 8+) | IntStream |
formatted(Object...) | Like String.format (Java 15+) | String |
repeat(int) | Repeat n times (Java 11+) | String |
indent(int) | Adjust indentation (Java 12+) | String |
translateEscapes() | Interpret \n, \t etc. in string (Java 15+) | String |
5.3 Method Invocation (JLS §15.12)
- Identify potentially applicable methods (by name and arity).
- Identify applicable methods (by type compatibility with actual args).
- Choose most specific applicable method.
- If two methods are equally specific and neither overrides the other → ambiguous compile error.
6. Defined vs Undefined Behavior
| Situation | Behavior per JLS |
"hello" + null | "hellonull" |
null + "hello" | "nullhello" |
"hello" == "hello" | true (interned literals) |
new String("hello") == "hello" | false |
s.charAt(s.length()) | StringIndexOutOfBoundsException |
s.substring(-1, 3) | StringIndexOutOfBoundsException |
s.substring(3, 1) | StringIndexOutOfBoundsException |
Concatenation with null reference | "null" string representation |
String.format(null, args) | NullPointerException |
Method overriding with throws | Can only reduce checked exceptions, not add new ones |
7. Edge Cases from Spec
7.1 String Concatenation with null
String s = null;
System.out.println("value: " + s); // "value: null"
System.out.println(s + " there"); // "null there"
Object obj = null;
String result = "" + obj; // "null"
7.2 String Literal Interning
String a = "hello";
String b = "hello";
String c = new String("hello");
String d = c.intern();
System.out.println(a == b); // true (same interned literal)
System.out.println(a == c); // false (c is new object)
System.out.println(a == d); // true (d is interned)
System.out.println(a.equals(c)); // true (same content)
7.3 Text Block Indentation
// The closing """ at column 8 determines indentation removal
String json = """
{
"name": "Java"
}
""";
// Result: "{\n \"name\": \"Java\"\n}\n"
// 8 spaces of indentation stripped from each line
7.4 Varargs Ambiguity
static void print(Object... args) { System.out.println("varargs Object"); }
static void print(String s) { System.out.println("exact String"); }
print("hello"); // calls exact String version (more specific)
print((Object) "hello"); // calls varargs Object version
7.5 Covariant Return Type Override
class Animal {
Animal create() { return new Animal(); }
}
class Dog extends Animal {
@Override
Dog create() { return new Dog(); } // covariant return: Dog is subtype of Animal
}
7.6 Method Hiding vs Overriding
class Parent {
static String type() { return "Parent"; } // class method
String name() { return "Parent"; } // instance method
}
class Child extends Parent {
static String type() { return "Child"; } // HIDES (not overrides) static method
@Override String name() { return "Child"; } // OVERRIDES instance method
}
// Parent p = new Child();
// p.type() → "Parent" (hidden, resolved at compile time)
// p.name() → "Child" (overridden, resolved at runtime)
8. Version History
| Java Version | Change | JEP/Reference |
| Java 1.0 | String, string literals, concatenation with + | JLS 1st ed. |
| Java 5 | Covariant return types; generics methods; varargs | JSR 14 |
| Java 7 | switch on String | JLS §14.11 |
| Java 9 | StringConcatFactory (invokedynamic for +) | JEP 280 |
| Java 11 | strip(), isBlank(), lines(), repeat() | JDK-11 API |
| Java 12 | indent(), transform() | JDK-12 API |
| Java 13 | Text blocks (preview) | JEP 355 |
| Java 14 | Text blocks (2nd preview) | JEP 368 |
| Java 15 | Text blocks (standard); formatted(), translateEscapes() | JEP 378 |
| Java 15 | \s and \<line-terminator> escape sequences | JEP 378 |
| Java 21 | String.indexOf(String, int, int) new overload | JDK-21 API |
9. Implementation-Specific Behavior (JVM-Specific)
9.1 String Concatenation Optimization (JEP 280, Java 9+)
javac compiles s1 + s2 + s3 using invokedynamic calling StringConcatFactory.makeConcatWithConstants. - The JVM decides at runtime how to best concatenate strings.
- HotSpot uses
StringBuilder-like strategies internally. - Avoids creating intermediate
String objects for each +.
9.2 String Deduplication
- JVM G1GC can deduplicate
String objects (same content → share char array). - Enabled with
-XX:+UseStringDeduplication. - Does NOT make
== comparisons reliable — use .equals().
9.3 Compact Strings (JEP 254, Java 9+)
- Strings containing only Latin-1 characters are stored as
byte[] instead of char[]. - Reduces memory footprint by ~50% for typical ASCII-heavy workloads.
- Transparent to Java code; no behavior change.
9.4 Virtual Method Dispatch
- Instance methods are dispatched via
invokevirtual (dynamic dispatch through vtable). private, final, and static methods use invokespecial or invokestatic (no virtual dispatch). - Interface methods use
invokeinterface.
10. Spec Compliance Checklist
11. Official Examples (Compilable Java 21 Code)
// Example 1: String Fundamentals
// File: StringFundamentals.java
public class StringFundamentals {
public static void main(String[] args) {
// Immutability
String s1 = "Hello";
String s2 = s1.concat(", World"); // new String created; s1 unchanged
System.out.println("s1: " + s1); // Hello
System.out.println("s2: " + s2); // Hello, World
// Reference equality vs content equality
String a = "Java";
String b = "Java";
String c = new String("Java");
System.out.println(a == b); // true (interned literals)
System.out.println(a == c); // false (new object)
System.out.println(a.equals(c)); // true (same content)
System.out.println(a.equals(null)); // false (no NPE)
// Key methods
String str = " Hello, World! ";
System.out.println(str.strip()); // "Hello, World!"
System.out.println(str.trim()); // "Hello, World!"
System.out.println("hello".toUpperCase()); // "HELLO"
System.out.println("HELLO".toLowerCase()); // "hello"
System.out.println(" ".isBlank()); // true
System.out.println("hello".contains("ell")); // true
System.out.println("hello".startsWith("hel")); // true
System.out.println("hello".endsWith("llo")); // true
System.out.println("hello".indexOf('l')); // 2
System.out.println("hello".lastIndexOf('l')); // 3
System.out.println("hello".replace("l", "r")); // "herro"
System.out.println("a,b,c".split(",").length); // 3
System.out.println("ha".repeat(3)); // "hahaha"
}
}
// Example 2: Text Blocks (Java 15+)
// File: TextBlockDemo.java
public class TextBlockDemo {
public static void main(String[] args) {
// JSON text block
String json = """
{
"name": "Alice",
"age": 30
}
""";
System.out.println(json);
// HTML text block
String html = """
<html>
<body>
<p>Hello, World!</p>
</body>
</html>
""";
System.out.println(html);
// Escape sequences in text blocks
String withEscape = """
Line 1\s
Line 2\s
""";
// \s preserves trailing space on each line
// Line continuation
String singleLine = """
This is a \
single line\
""";
System.out.println(singleLine); // "This is a single line"
// formatted() (Java 15+) — equivalent to String.format()
String template = """
Name: %s
Score: %.2f
""".formatted("Bob", 95.5);
System.out.println(template);
}
}
// Example 3: Method Declarations
// File: MethodDeclarations.java
import java.util.Arrays;
public class MethodDeclarations {
// Static method
static int add(int a, int b) { return a + b; }
// Instance method
String greet(String name) { return "Hello, " + name; }
// Varargs method
static int sum(int... numbers) {
int total = 0;
for (int n : numbers) total += n;
return total;
}
// Generic method
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
// Method with throws clause
static int parseInt(String s) throws NumberFormatException {
return Integer.parseInt(s);
}
// Covariant return type
static class Builder {
String value = "";
Builder append(String s) { value += s; return this; } // returns Builder for chaining
}
// Method overloading
static String format(int n) { return "int: " + n; }
static String format(double d) { return "double: " + d; }
static String format(String s) { return "string: " + s; }
public static void main(String[] args) {
System.out.println(add(3, 4));
System.out.println(sum(1, 2, 3, 4, 5)); // varargs
System.out.println(sum()); // empty varargs → 0
System.out.println(max("apple", "banana")); // generic method
System.out.println(max(10, 20));
// Method chaining with builder
Builder b = new Builder();
b.append("Hello").append(", ").append("World!");
System.out.println(b.value);
// Overloaded method resolution
System.out.println(format(42));
System.out.println(format(3.14));
System.out.println(format("test"));
}
}
// Example 4: StringBuilder for Mutable Strings
// File: StringBuilderDemo.java
public class StringBuilderDemo {
public static void main(String[] args) {
// StringBuilder: mutable character sequence
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World");
sb.append("!");
System.out.println(sb.toString()); // Hello, World!
System.out.println(sb.length()); // 13
System.out.println(sb.charAt(0)); // H
sb.insert(5, " Beautiful"); // insert at index
System.out.println(sb); // Hello Beautiful, World!
sb.delete(5, 15); // delete range
System.out.println(sb); // Hello, World!
sb.reverse();
System.out.println(sb); // !dlroW ,olleH
// Building strings in a loop — use StringBuilder, not +
long start = System.nanoTime();
StringBuilder csv = new StringBuilder();
for (int i = 0; i < 10_000; i++) {
csv.append(i).append(',');
}
long end = System.nanoTime();
System.out.printf("Built %d chars in %d µs%n",
csv.length(), (end - start) / 1000);
}
}
// Example 5: Method Overriding and toString()
// File: MethodOverriding.java
import java.util.Objects;
public class MethodOverriding {
static class Point {
final double x, y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point(%s, %s)".formatted(x, y);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Point other)) return false;
return Double.compare(x, other.x) == 0
&& Double.compare(y, other.y) == 0;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
// Covariant return
Point translate(double dx, double dy) {
return new Point(x + dx, y + dy);
}
}
static class Point3D extends Point {
final double z;
Point3D(double x, double y, double z) {
super(x, y);
this.z = z;
}
@Override
public String toString() {
return "Point3D(%s, %s, %s)".formatted(x, y, z);
}
@Override
Point3D translate(double dx, double dy) { // covariant return
return new Point3D(x + dx, y + dy, z);
}
}
public static void main(String[] args) {
Point p = new Point(1.0, 2.0);
System.out.println(p); // Point(1.0, 2.0)
System.out.println(p.translate(1, 1)); // Point(2.0, 3.0)
Point3D p3 = new Point3D(1.0, 2.0, 3.0);
System.out.println(p3); // Point3D(1.0, 2.0, 3.0)
// Polymorphism via toString
Point ref = p3;
System.out.println(ref.toString()); // Point3D(1.0, 2.0, 3.0)
// equals
System.out.println(p.equals(new Point(1.0, 2.0))); // true
System.out.println(p.equals(p3)); // false (z differs)
}
}
| Section | Topic | URL |
| JLS §3.10.5 | String Literals | https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.10.5 |
| JLS §3.10.6 | Text Blocks | https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.10.6 |
| JLS §4.3.3 | Class String | https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.3.3 |
| JLS §8.4 | Method Declarations | https://docs.oracle.com/javase/specs/jls/se21/html/jls-8.html#jls-8.4 |
| JLS §8.4.8 | Overriding | https://docs.oracle.com/javase/specs/jls/se21/html/jls-8.html#jls-8.4.8 |
| JLS §15.12 | Method Invocation | https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.12 |
| JLS §15.18.1 | String Concatenation | https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.18.1 |
| JEP 280 | Indify String Concatenation | https://openjdk.org/jeps/280 |
| JEP 378 | Text Blocks | https://openjdk.org/jeps/378 |
| JEP 254 | Compact Strings | https://openjdk.org/jeps/254 |