Table of Contents
1. String
Strings are the building blocks for manipulating textual information and representing sequences of characters. In Java, strings are instances of the String
class. Conventionally, strings can be created using the new
keyword, but Java is designed to make string creation seamless. You can create strings without explicitly using new
, making the code cleaner and more intuitive.
// Using new keyword
String strWithNew = new String("Hello, World!");
// Without new keyword (preferred approach)
String strWithoutNew = "Hello, Java!";
The second approach is more common and is known as string literal creation. This method is favored for its simplicity and readability.
Strings in Java implement the CharSequence
interface, providing a uniform way to work with character sequences. This interface defines methods for querying and extracting information from sequences of characters, reinforcing the versatility of strings in Java.
1.1. Concatenation
One of the most intriguing aspects of Java strings is their concatenation capabilities. Concatenation is the process of combining strings, and in Java, the +
operator is used for this purpose. However, the behavior of the +
operator depends on the types of the operands involved.
1.1.1. Concatenation Rules
- If both operands are numeric,
+
performs addition. - If either operand is a string,
+
performs concatenation. - The expression is evaluated from left to right.
// Numeric addition
String result1 = 1 + 2 + "C"; // Result: "3C"
// String concatenation
String result2 = "C" + 2 + 1; // Result: "C21"
In the first example, the addition is performed before concatenating with the string “C”. In the second example, the string “C” is concatenated with the subsequent numeric values.
The +=
operator provides a concise way to perform concatenation and assign the result back to a variable.
String text = "Hello, ";
text += "Java!";
In this example, the +=
operator appends “Java!” to the existing string, making it “Hello, Java!”.
1.2. Immutability
Strings hold a unique position due to their immutability. Once a String
object is created, it remains fixed and unchangeable. You can think of it as a sealed storage box with a predetermined content. While this immutability offers advantages in terms of consistency and optimal memory usage, it comes at the cost of zero flexibility.
Immutability in Java strings means that once a String
object is instantiated, its content cannot be modified. Any operation that seems to alter a string actually creates a new string object with the desired changes, leaving the original string intact.
public class ImmutabilityExample {
public static void main(String[] args) {
String originalString = "Java";
originalString.concat(" is amazing!");
System.out.println("Original String: " + originalString);
String modifiedString = originalString.concat(" is amazing!");
System.out.println("Modified String: " + modifiedString);
}
}
output:
Original String: Java
Modified String: Java is amazing!
In this example, the concat
method is used to append ” is amazing!” to the original string. However, notice that the original string remains unchanged. This is because the result of the concat
operation is not assigned back to the original string. Instead, a new string (modifiedString
) is created to hold the modified content.
1.3. String Methods
Java’s String
has a plethora of methods that empower developers to manipulate and analyze text efficiently. In this exploration, we’ll delve into some of the essential string methods
1. length()
The length()
method returns the length of a string, representing the number of characters in the sequence.
String message = "Hello, Java!";
int length = message.length(); // length is 12
The length()
method in Java, unlike indexing or position counting, adheres to conventional counting starting from 1. When you invoke the length()
method on a String
object, it returns the total number of characters in the string, with the count beginning at 1 for the first character and incrementing by 1 for each subsequent character.
2. charAt()
charAt(int index)
retrieves the character at the specified index in the string.
String word = "Java";
char firstChar = word.charAt(0); // firstChar is 'J'
char outOfBoundsChar = word.charAt(10); // StringIndexOutOfBoundsException
When using the charAt(int index)
method in Java, it’s essential to remember that the index is zero-based. Attempting to access an index that is out of the bounds of the string will result in a StringIndexOutOfBoundsException
.
3. indexOf()
indexOf(String str)
returns the index of the first occurrence of the specified substring.
indexOf(String str, int fromIndex)
returns the index of the first occurrence of the specified substring, starting from the given fromIndex
String sentence = "Java is amazing!";
int index = sentence.indexOf("is"); // index is 5
int index = sentence.indexOf("a", 5); // index is 8
4. substring()
substring(int beginIndex, int endIndex)
extracts a portion of a string, and it is zero-based. It returns a new string that consists of characters from the beginIndex
to endIndex - 1
. The second parameter, endIndex
, is exclusive. The endIndex
parameter is optional. If you omit it, the substring will include all characters from beginIndex
to the end of the string.
substring(int beginIndex)
extracts a portion of the string between the specified indices.
String text = "Programming is fun!";
String snippet = text.substring(15, 18); // "fun"
String substring = text.substring(5); // "amming is fun!"
String substring2 = text.substring(5, 19); // "amming is fun!"
String emptyString = text.substring(3, 3); // ""
String wrong = text.substring(3, 2); // StringIndexOutOfBoundsException
5. toLowerCase()
and toUpperCase()
These methods convert the string to lowercase and uppercase, respectively.
String phrase = "Hello, World!";
String lowercase = phrase.toLowerCase(); // "hello, world!"
String uppercase = phrase.toUpperCase(); // "HELLO, WORLD!"
6. equals()
and equalsIgnoreCase()
These methods compare strings for equality, considering case sensitivity.
String str1 = "Java";
String str2 = "java";
boolean isEqual = str1.equals(str2); // false
boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // true
7. startsWith()
and endsWith()
These methods check if a string starts or ends with a specified prefix or suffix. The check is case-sensitive
String filename = "example.txt";
boolean startsWith = filename.startsWith("ex"); // true
boolean startsWithCapitalCase = filename.startsWith("Ex"); // false
boolean endsWith = filename.endsWith(".txt"); // true
8. replace()
replace(char oldChar, char newChar)
replaces occurrences of a specified character.
replace(CharSequence target, CharSequence replacement)
replaces all occurrences of a specified target sequence with another sequence.
String phrase = "Java is cool!";
String updatedPhrase = phrase.replace('c', 'C'); // "Java is Cool!"
String updatedPhrase2 = phrase.replace("cool", "fun"); // "Java is fun!"
9. contains()
contains(CharSequence sequence)
checks if the string contains a specified sequence of characters.
String sentence = "Programming is fascinating!";
boolean containsWord = sentence.contains("is"); // containsWord is true
10. trim()
, strip()
, stripLeading()
, and stripTrailing()
trim()
and strip()
remove leading and trailing whitespaces from a string. The stip() method is new since Java 11. It does everything that trim() does, but it supports Unicode
String spacedString = " Hello, Java! ";
String trimmed = spacedString.trim(); // "Hello, Java!"
String stripped = spacedString.strip(); // "Hello, Java!"
String leadingStripped = spacedString.stripLeading(); // "Hello, Java! "
String trailingStripped = spacedString.stripTrailing(); // " Hello, Java!"
1.4. Method Chaining
Method chaining in Java allows you to invoke multiple methods on an object in a single line, with each method returning an object that supports the subsequent method call. This approach leads to more concise and readable code.
public class MethodChainingExample {
public static void main(String[] args) {
String text = " Java is amazing! ";
String result = text.trim().toUpperCase().replace("AMAZING", "incredible").substring(5, 18);
System.out.println("Original Text: " + text);
System.out.println("Modified Result: " + result);
}
}
In this example, we start with the original string ” Java is amazing! “. We then chain several string methods together:
trim()
: Removes leading and trailing whitespaces.toUpperCase()
: Converts the entire string to uppercase.replace("AMAZING", "incredible")
: Replaces the specified substring “AMAZING” with “incredible”.substring(5, 18)
: Extracts a substring from index 5 to 17 (exclusive).
The output will be:
Original Text: Java is amazing!
Modified Result: IS incredible
2. StringBuilder
The StringBuilder
class in Java provides a mutable sequence of characters, allowing for efficient modifications without creating new objects for each change. This is particularly useful when you need to concatenate or modify strings in a loop, where the immutability of String
objects might lead to inefficiency.
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("Java is");
for (int i = 0; i < 5; i++) {
stringBuilder.append(" powerful"); // Append " powerful" in each iteration
}
String result = stringBuilder.toString();
System.out.println("Result: " + result);
}
}
output:
Result: Java is powerful powerful powerful powerful powerful
In this example, a StringBuilder
is initially created with the content “Java is”. Inside the loop, the append
method is used to concatenate ” powerful” to the existing content in each iteration. Unlike using String
concatenation in a loop, where new objects would be created at each step, StringBuilder
allows for efficient modifications in-place.
2.1. Mutability and Chaining
Unlike String
, which is immutable, StringBuilder
allows modifications in-place and returns a reference to itself after each method call. Here’s an example demonstrating this difference:
public class MutabilityChainingExample {
public static void main(String[] args) {
String stringResult = "Java"
.concat(" is")
.concat(" powerful");
stringResult.concat(" and versatile");
StringBuilder stringBuilder = new StringBuilder("Java");
stringBuilder
.append(" is")
.append(" powerful")
.append(" and versatile");
System.out.println("String Result: " + stringResult);
System.out.println("StringBuilder Result: " + stringBuilder);
}
}
output
String Result: Java is powerful
StringBuilder Result: Java is powerful and versatile
- Using
String
for concatenation (immutable):- The
concat
method is used to concatenate substrings in a chained manner. - However, the result of the last
concat
call (stringResult.concat(" and versatile");
) is not assigned back tostringResult
. String objects are immutable, and theconcat
method returns a newString
object, which is discarded in this case.
- The
- Using
StringBuilder
for modifications (mutable):- The
append
method is used to append substrings to theStringBuilder
in a chained manner. - The modifications made by the
append
calls directly affect the state of the existingStringBuilder
object.
- The
2.2. StringBuilder Methods
1. length()
, charAt()
, indexOf()
and substring()
These 4 methods work exactly the same as in the String class
StringBuilder stringBuilder = new StringBuilder("Hello, World!");
char charAtIndex = stringBuilder.charAt(2); // l
int indexOfJava = stringBuilder.indexOf("World"); // 7
int length = stringBuilder.length(); // 13
String subString = stringBuilder.substring(3, 8); // lo, W
2. append(
)
Appends the specified string to the end of the StringBuilder
object.
StringBuilder stringBuilder = new StringBuilder("Java");
stringBuilder.append(" is powerful."); // "Java is powerful."
3. insert()
Inserts the specified string into the StringBuilder
object at the specified offset. The insert method takes two parameters:
offset
: The index at which the specified string will be inserted.str
: The string to be inserted at the specified offset.
StringBuilder stringBuilder = new StringBuilder("Java is versatile.");
stringBuilder.insert(8, "powerful and "); // Java is powerful and versatile.
4. delete() and deleteCharAt()
delete(int start, int end)
removes characters from the StringBuilder
object from the specified start index to the end index.
deleteCharAt(int index)
: Removes the character at the specified index from the StringBuilder
object.
StringBuilder stringBuilder = new StringBuilder("Java is powerful.");
stringBuilder.delete(7, 16); // Java is.
stringBuilder.deleteCharAt(3); // Jav is.
5. replace()
replace(int start, int end, String str)
: Replaces the characters from the start index to the end index with the specified string.
StringBuilder stringBuilder = new StringBuilder("Java is powerful.");
stringBuilder.replace(8, 16, "versatile"); // Java is versatile.
6. reverse()
Reverses the characters in the StringBuilder
object.
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuilder.reverse(); // olleH
7. toString()
Converts a StringBuilder into a String
3. Understanding Equality
3.1. ==
Operator
- Compares references, not content.
- Checks if two objects refer to the same memory location.
3.2. equals
Method
- Compares the content of objects.
- Needs to be explicitly overridden in custom classes for meaningful content-based comparison.
3.3. Equality with StringBuilder
==
checks if two StringBuilder
references point to the same object.
StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = new StringBuilder("Hello");
StringBuilder sb3 = sb1.append(" World");
System.out.println(sb1 == sb2); // false
System.out.println(sb1 == sb3); // true
StringBuilder
does not override the equals
method for content-based comparison. To compare content, you can convert StringBuilder
to String
using toString
and then use equals
.
StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = new StringBuilder("Hello");
System.out.println(sb1.toString().equals(sb2.toString())); // true
System.out.println(sb1.equals(sb2)); // false
3.4. Equality with String
==
checks if two String
references point to the same object.
String str1 = "Hello";
String str2 = "Hello";
String str3 = str1;
String str4 = str3.concat(" World");
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str4 == str3); // false
The ==
comparison between str1
and str2
returns true
because both reference the same interned String object in the string pool (see next chapter). In this case, the compiler recognizes that the content of the two string literals is the same (“Hello”), so it uses the same String object for both. It’s important to note that this behavior is specific to string literals and the interning mechanism. When you create strings using the new
keyword or concatenate strings at runtime, a new object is created, and ==
would then check for reference equality.
The concat
method creates a new String object (due to the immutability of String), and str4
ends up referring to a different object than str3
. Even though the content may be the same, ==
checks for reference equality, and in this case, the references are different.
equals
method is overridden in the String
class for content-based comparison.
String str1 = "Hello";
String str2 = "Hello World";
String str3 = str1;
String str4 = str3.concat(" World");
System.out.println(str1.equals(str2)); // false
System.out.println(str1.equals(str3)); // true
System.out.println(str4.equals(str2)); // true
str1.equals(str2)
isfalse
because the content is different.str1.equals(str3)
istrue
because they point to the same object with the same content.str4.equals(str2)
istrue
because their content is the same after the concatenation.
4. The String Pool
In Java, the String Pool is a special area in the Java Virtual Machine (JVM) memory where string literals and constants are stored to optimize memory usage. The String Pool helps conserve memory by reusing common strings instead of creating new objects for identical string literals.
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true
str1 is a literal that gets placed into the String Pool. str2 reuses the same literal from the String Pool. So str1 and str2 both reference the same literal from the String Pool
String original = "Trim me!";
String trimmed = original.trim(); // Creates a new string
System.out.println(original == trimmed); // false
The trim()
method returns a new string with leading and trailing whitespaces removed. Since strings are immutable, the trim()
method does not modify the original string but creates a new one.
String literal = "Hello";
String newString = new String("Hello");
System.out.println(literal == newString); // false
Creating a string with the new
keyword explicitly results in a new object and does not utilize the String Pool. Therefore, comparing it with a literal using ==
will yield false
.
String str3 = new String("Hello").intern();
String literal = "Hello";
System.out.println(literal == str3); // true
The intern()
method can be used to add a string to the String Pool explicitly, even if it is created with the new
keyword. After interning, the strings reference the same object in the String Pool.