- Category: Programming Guides & Tutorials
- Published on Saturday, 02 February 2013 10:50
- Written by Vinayaga Moorthy
In this section we'll discuss how Java handles String objects in memory, and some of the reasons behind these behaviors.
One of the key goals of any good programming language is to make efficient use of memory. As applications grow, it's very common for String literals to occupy large amounts of a program's memory, and there is often a lot of redundancy within the universe of String literals for a program. To make Java more memory efficient, the JVM sets aside a special area of memory called the "String constant pool." When the compiler encounters a String literal, it checks the pool to see if an identical String already exists. If a match is found, the reference to the new literal is directed to the existing String, and no new String literal object is created. (The existing String simply has an additional reference.) Now we can start to see why making String objects immutable is such a good idea. If several reference variables refer to the same String without even knowing it, it would be very bad if any of them could change the String's value.
You might say, "Well that's all well and good, but what if someone overrides the String class functionality; couldn't that cause problems in the pool?" That's one of the main reasons that the String class is marked final. Nobody can override the behaviors of any of the String methods, so you can rest assured that the String objects you are counting on to be immutable will, in fact, be immutable.
Creating New Strings
Earlier we promised to talk more about the subtle differences between the various methods of creating a String. Let's look at a couple of examples of how a String might be created, and let's further assume that no other String objects exist in the pool:
String s = "abc"; // creates one String object and one reference variable
In this simple case, "abc" will go in the pool and s will refer to it.
String s = new String("abc"); // creates two objects, and one reference variable
In this case, because we used the new keyword, Java will create a new String object in normal (non-pool) memory, and s will refer to it. In addition, the literal "abc" will be placed in the pool.
Important Methods in the String Class
The following methods are some of the more commonly used methods in the String class.
1. charAt() Returns the character located at the specified index
2. concat() Appends one String to the end of another ( "+" also works)
3. equalsIgnoreCase() Determines the equality of two Strings, ignoring case
4. length() Returns the number of characters in a String
5. replace() Replaces occurrences of a character with a new character
6. substring() Returns a part of a String
7. toLowerCase() Returns a String with uppercase characters converted
8. toString() Returns the value of a String
9. toUpperCase() Returns a String with lowercase characters converted
10. trim() Removes whitespace from the ends of a String
Let's look at these methods in more detail.
public char charAt(int index) This method returns the character located at the String's specified index. Remember, String indexes are zero-based—for example,
String x = "airplane";
System.out.println( x.charAt(2) ); // output is 'r'
public String concat(String s) This method returns a String with the value of the String passed in to the method appended to the end of the String used to invoke the method—for example,
String x = "taxi";
System.out.println( x.concat(" cab") ); // output is "taxi cab"
The overloaded + and += operators perform functions similar to the concat() method—for example,
String x = "library";
System.out.println( x + " card"); // output is "library card"
String x = "Atlantic";
x += " ocean"
System.out.println( x ); // output is "Atlantic ocean"
In the preceding "Atlantic ocean" example, notice that the value of x really did change! Remember that the += operator is an assignment operator, so line 2 is really creating a new String, "Atlantic ocean", and assigning it to the x variable. After line 2 executes, the original String x was referring to, "Atlantic", is abandoned.
public boolean equalsIgnoreCase(String s) This method returns a boolean value (true or false) depending on whether the value of the String in the argument is the same as the value of the String used to invoke the method. This method will return true even when characters in the String objects being compared have differing cases—for example,
String x = "Exit";
System.out.println( x.equalsIgnoreCase("EXIT")); // is "true"
System.out.println( x.equalsIgnoreCase("tixe")); // is "false"
public int length() This method returns the length of the String used to invoke the method—for example,
String x = "01234567";
System.out.println( x.length() ); // returns "8"
public String replace(char old, char new) This method returns a String whose value is that of the String used to invoke the method, updated so that any occurrence of the char in the first argument is replaced by the char in the second argument—for example,
String x = "oxoxoxox";
System.out.println( x.replace('x', 'X') ); // output is "oXoXoXoX"
public String substring(int begin)
public String substring(int begin, int end) The substring() method is used to return a part (or substring) of the String used to invoke the method. The first argument represents the starting location (zero-based) of the substring. If the call has only one argument, the substring returned will include the characters to the end of the original String. If the call has two arguments, the substring returned will end with the character located in the nth position of the original String where n is the second argument. Unfortunately, the ending argument is not zero-based, so if the second argument is 7, the last character in the returned String will be in the original String's 7 position, which is index 6 (ouch). Let's look at some examples:
String x = "0123456789"; // as if by magic, the value of each char is the same as its index!
System.out.println( x.substring(5) ); // output is "56789"
System.out.println( x.substring(5, 8)); // output is "567"
The first example should be easy: start at index 5 and return the rest of the String. The second example should be read as follows: start at index 5 and return the characters up to and including the 8th position (index 7).
Arrays have an attribute (not a method), called length. You may encounter questions in the exam that attempt to use the length() method on an array, or that attempt to use the length attribute on a String. Both cause compiler errors—for example,
String x = "test";
System.out.println( x.length ); // compiler error
String x = new String;
System.out.println( x.length() ); // compiler error
public String toLowerCase() This method returns a String whose value is the String used to invoke the method, but with any uppercase characters converted to lowercase—for example,
String x = "A New Moon";
System.out.println( x.toLowerCase() ); // output is "a new moon"
public String toString() This method returns the value of the String used to invoke the method. What? Why would you need such a seemingly "do nothing" method? All objects in Java must have a toString() method, which typically returns a String that in some meaningful way describes the object in question. In the case of a String object, what more meaningful way than the String's value? For the sake of consistency, here's an example:
String x = "big surprise";
System.out.println( x.toString() ); // output – reader's exercise
public String toUpperCase() This method returns a String whose value is the String used to invoke the method, but with any lowercase characters converted to uppercase—for example,
String x = "A New Moon";
System.out.println( x.toUpperCase() ); // output is "A NEW MOON"
public String trim() This method returns a String whose value is the String used to invoke the method, but with any leading or trailing blank spaces removed— for example,
String x = " hi ";
System.out.println( x + "x" ); // result is " hi x"
System.out.println( x.trim() + "x"); // result is "hix"
blog comments powered by Disqus