Java provides three main classes to work with text:
- String → Immutable sequence of characters.
- StringBuilder → Mutable sequence of characters (fast).
- StringBuffer → Mutable and thread-safe version of
StringBuilder.
Although all three store characters, they are designed for different purposes.
- String → Use when text should never change.
- StringBuilder → Use when modifying strings frequently in a single thread.
- StringBuffer → Use when multiple threads modify the same string safely.
Strings in Java are immutable.
Once a String object is created, its content cannot be modified.
Whenever you perform operations like
str = str + "World";or
str = str.trim();Java does not modify the existing object.
Instead,
- Creates a new String object.
- Copies old content.
- Applies changes.
- Old object becomes garbage.
Example:
String s = "Java";
s = s + " Programming";Memory:
Old Object
"Java"
↓
New Object
"Java Programming"
The old "Java" object cannot be reused.
Imagine this loop:
String s = "";
for(int i=0;i<10000;i++)
{
s += i;
}Every iteration creates
- new String
- copy previous characters
- garbage collection
Thousands of temporary objects are created.
This becomes very slow.
Instead of creating new objects repeatedly,
StringBuilder
uses one resizable internal buffer.
It simply changes that buffer.
append()
↓
same object changes
↓
No new String every time
This makes it much faster.
Think of a printed book.
If you want to change one sentence,
you print an entirely new book.
Think of a whiteboard.
You erase,
write,
append,
delete,
without buying a new board.
The lecture shows two ways to create Strings.
String s1 = "Hello";Stored inside the String Pool if possible.
String s2 = new String("Hello");Always creates a new object in Heap memory.
Even if "Hello" already exists in the String Pool.
The notes list several constructors.
new String()
new String(String)
new String(char[])
new String(byte[])
new String(StringBuilder)
new String(StringBuffer)The lecture groups methods into categories.
length()
isEmpty()
isBlank()Example
String s = "Hello";
System.out.println(s.length()); //5charAt()
toCharArray()Example
String s = "Java";
System.out.println(s.charAt(2));Output
v
contains()
indexOf()
lastIndexOf()
startsWith()
endsWith()
Example
String s = "Programming";
System.out.println(s.contains("gram"));Output
true
equals()
equalsIgnoreCase()
compareTo()
Example
String a = "java";
String b = "JAVA";
System.out.println(a.equals(b));Output
false
substring()
toUpperCase()
toLowerCase()
trim()
strip()
repeat()
replace()
replaceAll()
split()
join()
Example
String s = " hello ";
System.out.println(s.trim());Output
hello
valueOf()
getBytes()
intern()
format()
The lecture diagram shows this concept visually.
Example
String s = "Hello";
s.toUpperCase();What happens?
NOT
HELLO
Actually
Hello
↓
HELLO (new object)
Original object remains unchanged.
Correct usage:
s = s.toUpperCase();StringBuilder belongs to
java.langIt extends
AbstractStringBuilder
The notes also show that StringBuffer extends the same parent class.
Internally,
StringBuilder stores
byte[] value
(or char[] conceptually in older explanations)
along with
count
which tracks how many characters are actually used.
Example
StringBuilder sb = new StringBuilder("Java");Internally
Capacity = 20
Count = 4
+----+----+----+----+----+----+----+
| J | a | v | a | | | ... |
+----+----+----+----+----+----+----+
Why capacity 20?
Constructor with a String allocates:
16 + string length
16 + 4 = 20
This is illustrated in the lecture notes.
StringBuilder sb = new StringBuilder();Default capacity
16
Custom capacity
StringBuilder sb = new StringBuilder(50);Capacity
50
Suppose
Capacity =16
You append more characters.
When capacity is full,
Java creates a bigger array.
Formula
newCapacity = oldCapacity × 2 + 2
Example
Old
16
New
16 × 2 + 2
=34
Again full
34 ×2 +2
=70
This resizing sequence is shown in the notes.
Suppose
Capacity
16
You append
20 characters
Old array
16 cells
↓
Create
34 cells
↓
Copy old data
↓
Continue appending
This happens automatically.
The lecture lists these common methods.
append()
insert()
delete()
replace()
reverse()
charAt()
setCharAt()
length()
capacity()
ensureCapacity()
trimToSize()
toString()StringBuilder sb = new StringBuilder();
sb.append("Java");
sb.append(" Programming");Output
Java Programming
sb.insert(4," Language");Result
Java Language Programming
sb.delete(4,13);Removes characters between indices.
StringBuilder sb = new StringBuilder("Java");
sb.reverse();Output
avaJ
sb.replace(0,4,"Python");Returns
Current characters
Returns
Internal array size
Ensures the internal buffer has at least the requested capacity.
sb.ensureCapacity(100);Reduces unused capacity.
Useful when a large builder is no longer expected to grow.
Converts
StringBuilder
↓
String
Example
String str = sb.toString();A common mistake highlighted by the notes is comparing two StringBuilder objects with equals().
StringBuilder sb1 = new StringBuilder("Aditya");
StringBuilder sb2 = new StringBuilder("Aditya");
System.out.println(sb1.equals(sb2));Output
false
Why?
StringBuilder does not override Object.equals().
So equals() checks reference equality, not character content.
To compare content:
sb1.toString().equals(sb2.toString());StringBuffer is
- Mutable
- Thread-safe
It extends the same AbstractStringBuilder class as StringBuilder.
Imagine
Thread 1
append("A")
and
Thread 2
append("B")
Both operate on the same object.
Without synchronization,
operations may overlap.
Possible outputs
HelloA
HelloAB
or inconsistent results due to a race condition, as illustrated in the lecture.
StringBuffer synchronizes its methods to prevent this.
| Feature | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutable | ❌ No | ✅ Yes | ✅ Yes |
| Thread-safe | ✅ Yes (immutable) | ❌ No | ✅ Yes |
| Performance | Slow for repeated modifications | Fast | Slower than StringBuilder |
| Best Use | Constant text | Heavy string manipulation | Multi-threaded shared modification |
String s = "Java";Uses the String Pool.
new String("Java");Always creates a Heap object.
StringBuilder objects are never stored in the String Pool.
Bad
String s="";
for(...)
s += i;Good
StringBuilder sb = new StringBuilder();
for(...)
sb.append(i);For simple concatenation:
String s = "A" + "B";The compiler optimizes this efficiently.
No need to manually use StringBuilder.
Modern web applications usually handle each request on its own thread.
Most string manipulation happens within that single thread.
StringBuilder is usually sufficient.
StringBuilder sb = new StringBuilder("Java");length() → 4
capacity() → 20
Stringis immutable, so every modification creates a new object.- Repeated String concatenation creates many temporary objects and increases garbage collection.
StringBuildermodifies the same internal buffer, making it much faster for repeated updates.- Default
StringBuildercapacity is 16. new StringBuilder("Java")creates a capacity of 20 (16 + 4).- When the buffer fills, capacity grows using
oldCapacity × 2 + 2. StringBuilderandStringBufferextendAbstractStringBuilder.StringBuilderis not thread-safe.StringBufferis synchronized and thread-safe but slower due to synchronization overhead.StringBuilder.equals()compares object references, not content.
- Why is
Stringcalled immutable? - Why is
StringBuilderfaster inside loops? - What is the default capacity of
StringBuilder()? - What is the capacity of
new StringBuilder("Java")? - If capacity is 16, what will it become after resizing?
- What is the difference between
length()andcapacity()? - Why does
sb1.equals(sb2)returnfalsefor twoStringBuilderobjects with the same text? - When should you choose
StringBufferoverStringBuilder? - Why are
Stringliterals stored differently fromnew String("...")? - What does
trimToSize()do?
- String Pool and
intern()in depth - Wrapper Classes and Autoboxing
StringTokenizervssplit()StringJoinerString.format()andFormatter- Regular Expressions (
PatternandMatcher)