JB Header

Java String Basics and Why Strings Are Immutable

Introduction Text is everywhere in programs - names, messages, file contents, web pages - and in Java, text is handled by the String class. This tutorial covers Java String basics and why strings are immutable. We will start with what a String is, and the ways to create one. We will then join strings together, and look at strings as sequences of characters. Next we will understand what it means that strings are immutable, and see why "changing" a string really creates a new one. Lastly, we will look at why Java's designers made strings immutable, and the main consequences for your code.

(Note - String literals, including escape sequences such as \n, are covered in Java LiteralsClick to read tutorial: Java Literals: Integer, Floating-Point, char and boolean.) What is a String? A String is a sequence of characters, such as "Hello". In Java, String is a class in the java.lang package, not a primitive type - so a String variable holds a reference to a String object, and can be null. But strings are so important that the language gives them special support -
Creating strings
Ways to create a String
//CreatingStrings.java
public class CreatingStrings {
  public static void main(String[] args) {
    String literal = "Java";                       // from a literal - the usual way
    String withNew = new String("Java");           // with new - rarely needed
    char[] letters = {'J', 'a', 'v', 'a'};
    String fromChars = new String(letters);        // from a char array
    String fromNumber = String.valueOf(3.14);      // from a number
    String repeated = "ab".repeat(3);              // from another string's method
    String empty = "";                             // the empty string
    String nothing = null;                         // no String object at all

    System.out.println(literal + " " + withNew + " " + fromChars + " " + fromNumber + " " + repeated);
    System.out.println("empty has length " + empty.length() + ", isEmpty: " + empty.isEmpty());
    System.out.println("nothing = " + nothing);
  }
}
 OUTPUT of the above code
Java Java Java 3.14 ababab
empty has length 0, isEmpty: true
nothing = null
Explanation of the code
  • Create strings from literals whenever you can. new String("Java") creates an extra, unneeded object.
  • The empty string "" is a real String object with length 0. null means there is no object at all - calling nothing.length() would throw a NullPointerException.
  • Joining null to a string with + does not fail - it adds the text null.
Joining strings The + operator joins (concatenates) strings. If either side of + is a String, the other side is converted to text - numbers, booleans and objects alike. concat() and String.join() are alternatives.
Joining strings
//JoiningStrings.java
public class JoiningStrings {
  public static void main(String[] args) {
    String first = "Tom";
    String last = "Jones";
    String full = first + " " + last;
    System.out.println(full);

    int age = 32;
    System.out.println(full + " is " + age + " years old");
    System.out.println("Total: " + 10 + 20);
    System.out.println("Total: " + (10 + 20));

    System.out.println(first.concat(last));
    System.out.println(String.join(", ", "Tom", "Harry", "Ethan"));
  }
}
 OUTPUT of the above code
Tom Jones
Tom Jones is 32 years old
Total: 1020
Total: 30
TomJones
Tom, Harry, Ethan
"Total: " + 10 + 20 printed Total: 1020, because + works from left to right, and once the left side is a string, every + joins text. Parentheses make the addition happen first. See operator precedenceClick to read tutorial: Java Operator Precedence and Associativity Explained. A String is a sequence of characters Each character of a string has an index, starting at 0, just like an array. length() gives the number of characters, and charAt(i) gives the character at index i.
Characters and indexes in a String
//StringChars.java
public class StringChars {
  public static void main(String[] args) {
    String word = "Brahman";
    System.out.println("Length: " + word.length());
    System.out.println("First: " + word.charAt(0) + ", last: " + word.charAt(word.length() - 1));

    int vowels = 0;
    for (int i = 0; i < word.length(); i++) {
      char c = Character.toLowerCase(word.charAt(i));
      if ("aeiou".indexOf(c) >= 0) {
        vowels++;
      }
    }
    System.out.println("Vowels: " + vowels);

    try {
      System.out.println(word.charAt(7));
    } catch (StringIndexOutOfBoundsException e) {
      System.out.println("charAt(7) failed: " + e.getMessage());
    }
  }
}
 OUTPUT of the above code
Length: 7
First: B, last: n
Vowels: 2
charAt(7) failed: Index 7 out of bounds for length 7
Note that length() is a method for strings - with parentheses - while arrays use the field length. As with arrays, an index outside 0 to length() - 1 throws an exception, here a StringIndexOutOfBoundsException. (The try and catch blocks catch the exception so that we can print its message; exception handling is covered later in this series.)
Strings are immutable A String object can never be changed after it is created. It has no method which modifies its characters. Methods which seem to change a string - toUpperCase(), replace(), trim(), concat() - all return a new String, and leave the original untouched.
Strings cannot be changed
//Immutability.java
public class Immutability {
  public static void main(String[] args) {
    String name = "java";
    name.toUpperCase();                    // result thrown away
    System.out.println("After toUpperCase(): " + name);

    String upper = name.toUpperCase();     // keep the new String
    System.out.println("upper = " + upper + ", name = " + name);

    String greeting = "Hello";
    String alias = greeting;               // both refer to the same object
    greeting = greeting + ", World";       // greeting now refers to a NEW object
    System.out.println("greeting = " + greeting);
    System.out.println("alias    = " + alias);
  }
}
 OUTPUT of the above code
After toUpperCase(): java
upper = JAVA, name = java
greeting = Hello, World
alias    = Hello
Explanation of the code
  • name.toUpperCase() on its own did nothing useful - it created "JAVA", but the result was not stored. This is a very common beginner bug. Always use the returned value.
  • greeting = greeting + ", World" did not change the "Hello" object. It created a new "Hello, World" object and pointed greeting at it. alias still refers to the unchanged "Hello".
  • The variable greeting can be reassigned - it is not final. It is the object which is immutable.
Why are strings immutable?
  • Sharing is safe: because no one can change a string, the same object can be shared freely - between variables, between methods, and in the string pool. A method which receives a string can be sure it will not change under it.
  • Security: strings hold file names, URLs, user names and class names. If they could be changed after being checked, a value could be altered between a security check and its use.
  • Thread safety: immutable objects can be used by many threads at once without any locking.
  • Hashing: a string's hash code can be calculated once and cached, which makes strings fast and reliable as keys in a HashMap or elements of a HashSet.
The cost is that every "change" creates a new object. Joining strings in a loop thousands of times creates thousands of throwaway objects - for that, use StringBuilderClick to read tutorial: String vs StringBuilder vs StringBuffer in Java, which is designed to be changed. Summary In this tutorial we first saw that String is a class with special language support - literals, the + operator and the string pool. We then created strings in several ways, and saw the difference between the empty string and null. Next we joined strings with +, concat() and String.join(), and treated a string as a sequence of characters with length() and charAt(). We then saw that strings are immutable - methods return new strings, and reassigning a variable does not change the original object. Lastly, we went through the reasons for immutability, and its cost. In the next tutorial, we will look at the String poolClick to read tutorial: Java String Pool: String Literals vs new String().