The Java char type is a 16-bit primitive that is used to hold a single character. A char variable can hold any unicode character. Java supports internationalization through unicode characters.
A Java String is a sequence of characters. String type is represented by the java.lang.String class. Strings are primarily used used to hold text in a Java program. The String class provides the necessary methods to operate on String objects. A String object’s value is always represented with quotes around it.
Initializing a String
A String object is declared like every other object in Java but can be initialized in two ways.
Using new operator;
String s = new String(“Hello World”);
or directly
String s = “Hello World”;
Here, s is a String object reference and it points to a location in memory that contains the value “Hello World”. The value of s is itself a sequence of characters ‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘ ‘, ‘W’, ‘o’, ‘r’, ‘l’, ‘d’.
Java developers prefer to use the direct initialization way almost all the time. Both initialization ways achieve the same thing, creating a String object.
String objects are immutable. Once a string is initialized, its value cannot be changed.
String s = “Hello”;
s = “Hello World”;
In this example, it might seem like the value of String reference s is initialized with a value “Hello” and later the value is changed to “Hello World”. In reality, “Hello” and “Hello World” are two different String objects. On the first line, a String object with value “Hello” is created and s is set to point to it. On the second line, a new String object with value “Hello World” is created and String variable s is set to start pointing to the new object instead of the one that has the value “Hello”.
Empty Strings
A String objects holds text value. In Java, Strings are allowed to keep no text at all. Such Strings are called empty strings.
An empty String is initialized as follows:
String s = “”; or String s = new String(“”);
String Equality
Two strings are said to be equal if they hold the same value.
Now, let’s create two Strings.
String s1 = “Hello World”;
String s2 = “Hello World”;
Both s1 and s2 have the same value, “Hello World”. Therefore, these two strings are said to be equal.
Equality for two strings is checked using the equals method of java.lang.Object, which is inherited by java.lang.String. The method returns true if they have the same value, otherwise it returns false.
public class MyClass {
public static void main(String [] args) {
String s1 = “Hello World”;
String s2 = “Hello World”;
if (s1.equals(s2)) {
System.out.println(“The two strings are equal.”);
} else {
System.out.println(“The two strings are not equal.”);
}
}
}
Compiling and running this class would give the output
The two strings are equal.
In this example, even though s1 and s2 have the same value, they are different string instances and therefore, point to different locations in the memory. Two Java objects are the same only if they point to the same location in memory and this is done using the == operator.
Now, let’s change the example a little bit.
public class MyClass {
public static void main(String [] args) {
String s1 = “Hello World”;
String s2 = “Hello World”;
System.out.println(“The two strings have the same value: “ + s1.equals(s2));
System.out.println(“The two strings are the same: “ + s1 == s2);
System.out.println(“s2 reference is changed.”);
s2 = s1;
System.out.println(“The two strings are the same: “ + s1 == s2);
}
}
The output would be:
The two strings have the same value: true
The two strings are the same: false
s2 reference is changed.
The two strings are the same:true
Because the two strings are initialized separately, they are not the same even though they have the same value. Later, s2 is made to point to s1’s memory location and then the two strings become the same.
To understand the issue better, let’s see another example.
public class MyClass {
public static void main(String [] args) {
String s1 = “Hello World”;
String s2 = s1;
System.out.println(“The two strings are the same: “ + s1 == s2);
System.out.println(“s2 is reinitialized”);
s2 = “Hello World”;
System.out.println(“The two strings are the same: “ + s1 == s2);
}
}
This time the output would be:
The two strings are the same: true
s2 is reinitialized.
The two strings are the same: false
In this example, in the beginning, the two strings initially point to the same memory location and are therefore the same. Then, s2 is assigned the same value again. Even then, a new string initialization takes place and s2 begins to point to a new location in the memory.
String operations
java.lang.String class provides the methods needed to operate on strings. Here are some of the most commonly used methods from java.lang.String class.
String substring(int beginningIndex, int endingPosition)
Returns the part of the String starting from beginningIndex and ending in endingPosition. While calculating endingPosition, the first letter in the String has position 1.
“Hello World”.substring(0, 6) → returns “Hello”
char charAt(int index)
Returns the character at index.
“Hello World”.charAt(2) → returns ‘l’
String concat(String s)
Concatenates two strings together.
“Hello”.concat(“ World”) → returns “Hello World”
int length()
Returns the number of characters in the string.
“Hello”.length() → Returns 5
int indexOf(String s)
Returns the index of the first occurrence of s inside the String. If it does not exist inside the string that it called on, it returns -1.
“Hello World”.indexOf(“World”) → Returns 6
StringBuffer and StringBuilder Classes
Even though String concatenation, whether it is done using concat method or using + operator, can append a string to another, it ends up creating a new String object. For appending strings, Java provides two other classes: StringBuffer and StringBuilder. StringBuffer uses an internal character array for the string that is appended to. Therefore, using a StringBuffer is faster than String concatenation. Appending to a StringBuffer is done using StringBuffer class’ append() method.
Let’s see this in an example.
public class MyClass {
public static void main(String [] args) {
StringBuffer s = new StringBuffer(“”);
s.append(“Hello”);
s.append(“ “);
s.append(“World”);
System.out.println(s.toString());
}
}
The output would be
Hello World
Here in this example, a StringBuffer object is initialized with an empty String. Similarly, it could be initialized with “Hello” or any other String. Following the initialization, three strings are appended to the StringBuffer, with the order:”Hello”, “ “, “World”. In the end, StringBuffer’s toString() method is invoked to get the appended string and it is printed on the console.
StringBuffer and StringBuilder define the same methods and can be used for the same purpose. The difference between the two is that StringBuilder is synchronized, making it thread safe and therefore, slower. So, StringBuilder should be used only when thread safety is important for the String that is being formed by appending.