This month, I'm going to tackle String literals and how they're handled in Java. If you read last month's SCJP Tip Line article, you'll find that this article is a nice follow up all about Strings. If you didn't read it, no big deal - I plan to cover enough information here to get you up to speed. String literals are a little "special" in Java in the way that they are treated. For the most part, the information contained in this post is not on the SCJP exam, but I see it on mock exams all the time and it comes up in the SCJP Forum quite often. Besides, it's just darned interesting stuff.
Let's start off by asking a simple question. What is a String Literal? A String literal is a sequence of characters between quotation marks, such as "string" or "literal". You've probably used String literals hundreds of times in your own applications. You might not, however, have realized just how special String literals are in Java.
Strings are Immutable
So what makes String literals so special? Well, first of all, it's very important to remember that String objects are immutable. That means that, once created, a String object cannot be changed (short of using something like reflection to get at private data). Immutable, you say? Unchangable? What about this code?
Source Code |
public class ImmutableStrings { public static void main(String[] args) { String start = "Hello"; String end = start.concat(" World!"); System.out.println(end); } } // Output Hello World! |
Well look at that, the String changed...or did it? In that code, no String object was ever changed. We start by assigning "Hello" to our variable, start. That causes a new String object to be created on the heap and a reference to that object is stored in start. Next, we invoke the method concat(String) on that object. Well, here's the trick, if we look at the API Spec for String, you'll see this in the description of the concat(String) method:
Concatenates the specified string to the end of this string.
If the length of the argument string is 0, then this String object is returned. Otherwise, a new String object is created, representing a character sequence that is the concatenation of the character sequence represented by this String object and the character sequence represented by the argument string.
Examples:
"cares".concat("s") returns "caress"
"to".concat("get").concat("her") returns "together"
Parameters:
str - the String that is concatenated to the end of this String.
Returns:
a string that represents the concatenation of this object's characters followed by the string argument's characters.
Notice the part I've highlighted in bold. When you concatenate one String to another, it doesn't actually change the String object, it simply creates a new one that contains the contents of both of the original Strings, one after the other. That's exactly what we did above. The String object referenced by the local variablestart never changed. In fact, if you added the statement System.out.println(start); after you invoked the concat method, you would see that start still referenced a String object that contained just "Hello". And just in case you were wondering, the '+' operator does the exact same thing as the concat() method.
Strings really are immutable.
Storage of Strings - The String Literal Pool
If you've done any preparation for the SCJP exam (and quite possibly even if you haven't), you've probably heard of the "String Literal Pool." What is the String Literal Pool? Most often, I hear people say that it is a collection of String objects. Although that's close, it's not exactly correct. Really, it's a collection ofreferences to String objects. Strings, even though they are immutable, are still objects like any other in Java. Objects are created on the heap and Strings are no exception. So, Strings that are part of the "String Literal Pool" still live on the heap, but they have references to them from the String Literal Pool.
Yeah, so that doesn't really explain what the pool is, or what it's for, does it? Well, because String objects are immutable, it's safe for multiple references to "share" the same String object. Take a look at this example:
Source Code |
public class ImmutableStrings { public static void main(String[] args) { String one = "someString"; String two = "someString"; System.out.println(one.equals(two)); System.out.println(one == two); } } // Output true true |
In such a case, there is really no need to make two instances of an identical String object. If a String object could be changed, as a StringBuffer can be changed, we would be forced to create two separate objects. But, as we know that String objects cannot change, we can safely share a String object among the two String references, one and two. This is done through the String literal pool. Here's how it is accomplished:
When a .java file is compiled into a .class file, any String literals are noted in a special way, just as all constants are. When a class is loaded (note that loading happens prior to initialization), the JVM goes through the code for the class and looks for String literals. When it finds one, it checks to see if an equivalent String is already referenced from the heap. If not, it creates a String instance on the heap and stores a reference to that object in the constant table. Once a reference is made to that String object, any references to that String literal throughout your program are simply replaced with the reference to the object referenced from the String Literal Pool.
So, in the example shown above, there would be only one entry in the String Literal Pool, which would refer to a String object that contained the word "someString". Both of the local variables, one and two, would be assigned a reference to that single String object. You can see that this is true by looking at the output of the above program. While the equals() method checks to see if the String objects contain the same data ("someString"), the == operator, when used on objects, checks for referential equality - that means that it will return true if and only if the two reference variables refer to the exact same object. In such a case, the references are equal. From the above output, you can see that the local variables, one and two, not only refer to Strings that contain the same data, they refer to the same object.
Graphically, our objects and references would look something like this:
Note, however, that this is a special behavior for String Literals. Constructing Strings using the "new" keyword implies a different sort of behavior. Let's look at an example:
Source Code |
public class ImmutableStrings { public static void main(String[] args) { String one = "someString"; String two = new String("someString"); System.out.println(one.equals(two)); System.out.println(one == two); } } // Output true false |
In this case, we actually end up with a slightly different behavior because of the keyword "new." In such a case, references to the two String literals are still put into the constant table (the String Literal Pool), but, when you come to the keyword "new," the JVM is obliged to create a new String object at run-time, rather than using the one from the constant table.
In such a case, although the two String references refer to String objects that contain the same data, "someString", they do not refer to the same object. That can be seen from the output of the program. While the equals() method returns true, the == operator, which checks for referential equality, returns false, indicating that the two variables refer to distinct String objects.
Once again, if you'd like to see this graphically, it would look something like this. Note that the String object referenced from the String Literal Pool is created when the class is loaded while the other String object is created at runtime, when the "new String..." line is executed.
If you'd like to get both of these local variables to refer to the same object, you can use the intern() method defined in String. Invoking two.intern() will look for a String object referenced from the String Literal Pool that has the same value as the one you invoked the intern method upon. If one is found, a reference to that String is returned and can be assigned to your local variable. If you did so, you'd have a picture that looks just like the one above, with both local variables,one and two, referring to the same String object, which is also referenced from the String Literal Pool. At that point, the second String object, which was created at run-time, would be eligible for garbage collection.
Garbage Collection
What makes an object eligible for garbage collection? If you're preparing for the SCJP exam (or even if you're not), the answer to that question should roll right off your tongue. An object is eligible for garbage collection when it is no longer referenced from an active part of the application. Anyone see what is special about garbage collection for String literals? Let's look at an example and see if you can see where this is going.
Source Code |
public class ImmutableStrings { public static void main(String[] args) { String one = "someString"; String two = new String("someString"); one = two = null; } } |
Just before the main method ends, how many objects are available for garbage collection? 0? 1? 2?
The answer is 1. Unlike most objects, String literals always have a reference to them from the String Literal Pool. That means that they always have a reference to them and are, therefore, not eligible for garbage collection. This is the same example as I used above so you can see what our picture looked liked originally there. Once we assign our variables, one and two, to null, we end up with a picture that looks like this:
As you can see, even though neither of our local variables, one or two, refer to our String object, there is still a reference to it from the String Literal Pool. Therefore, the object is not elgible for garbage collection. The object is always reachable through use of the intern() method, as referred to earlier.
Conclusion
Like I said at the outset of this article, virtually none of this information is included on the SCJP exam. However, I constantly see this question coming up in the SCJP forum and on various mock exams. These are a few of the highlights you can keep in mind when it comes to String literals:
- Equivalent String Literals (even those stored in separate classes in separate packages) will refer to the same String object.
- In general, String Literals are not eligible for garbage collection. Ever.
- Strings created at run-time will always be distinct from those created from String Literals.
- You can reuse String Literals with run-time Strings by utilizing the intern() method.
- The best way to check for String equality is to use the equals() method.
Be sure to check out these resources, as well:
API Spec for String
JLS, §3.10.5 String Literals
Tune in to the SCJP TipLine for more updates.
Until next time,
Corey
From http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html
相关推荐
SCJP
SCJP典型试题1000例 SCJP典型试题1000例 SCJP典型试题1000例
SCJP,全称为Sun Certified Programmer for the Java 2 Platform,是Oracle公司(原Sun Microsystems)推出的针对Java程序员的认证考试。这个考试旨在测试考生对于Java SE平台基础编程的知识和技能。以下是一些SCJP...
《SCJP大全》是一款全面覆盖SCJP(Sun Certified Programmer for Java Platform, Standard Edition)考试的知识资源,旨在帮助学习者一次性通过这项重要的Java编程认证。SCJP是Java开发人员的基础认证,证明了持有者...
SCJP – Sun Certified Java Programmer (Sun Java 程序员认证).Sun 公司作为Java 语言的发明者,对全球的Java 开发人员进行技术水平认证。该认证在国际上获得了IT 公司的普遍认可,是目前国际上最热门的IT 认证之一...
SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)是Oracle公司先前为Java程序员提供的一个认证考试,旨在验证候选者对Java编程语言的基本理解与应用能力。这个"scjp模拟器"可能是用于...
The SCJP6 Sun Certified Programmer for Java 6 Study Guide (Exam 310-065) is a comprehensive resource that covers all the essential topics required for the certification exam. From basic concepts like ...
SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)是Oracle公司为Java程序员提供的一项权威认证,证明其在Java编程语言上的专业能力。这个认证涵盖了基础语法、类库、异常处理、多线程、...
SCJP(Sun Certified Programmer for the Java Platform, Standard Edition)是Oracle公司(原Sun Microsystems)推出的一项Java编程语言的认证考试,旨在验证候选者对Java SE平台的基础知识和编程能力。这个认证...
SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)是Oracle公司先前对Java程序员进行认证的一项考试,旨在测试开发者对于Java SE平台的基础知识和编程能力。这个压缩包文件包含了一个...
SCJP 可以说各种Java认证的基础,相对于SCJD来说,SCJP更侧重于测验您的Java程序设计概念及能力,内容偏重于Java语法及JDK的内容。其对应的最主要的学习课程是SL-275。SCJP 的学习/考试过程是相对相对比较明确的,...
SCJP(Sun Certified Programmer for the Java Platform)是Oracle公司(原SUN公司)推出的一门针对Java编程语言的初级认证考试。这个认证旨在验证开发者对Java语言基础的掌握程度,包括语法、数据类型、控制结构、...
SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)是Oracle公司为Java程序员提供的一项认证考试,旨在验证考生对Java编程语言的基础知识和理解。这个“SCJP考试模拟软件”显然是一款帮助...
《SCJP官方教材中文版》是一本专门为准备SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)考试的学员编写的指南。SCJP,现已被Oracle认证为Oracle Certified Associate, Java SE 8 ...
Java SCJP,全称为Sun Certified Programmer for the Java 2 Platform, Standard Edition,是Oracle公司针对Java初学者和专业开发者的一项认证考试。这个“Java SCJP中文学习指南”旨在帮助中文使用者深入理解Java...
《SCJP官方教材中文版》是一本针对Sun Certified Programmer for the Java 2 Platform, Standard Edition (SCJP)考试的专业指南。这本书旨在帮助读者深入理解Java编程语言的基础知识,为通过SCJP认证考试做好充分...
SCJP sun certificated java programmer (SCJP) 一种Java认证考试 对于Java程序设计员,Sun推出两项认证:Sun Certificated Java Programmer (SCJP)和Sun Certificated Java Developer(SCJD)。 SCJP 可以说...
SCJP(Sun Certified Programmer for the Java 2 Platform, Standard Edition)是Oracle公司(原Sun Microsystems)推出的Java程序员认证考试,旨在验证考生对Java SE平台基础知识的掌握程度。这个"SCJP考试模拟机...
SCJP(Sun Certified Programmer for the Java SE 6 Platform)是Oracle公司推出的Java程序员认证考试,其旧版编号为310-065。这个压缩包文件“SCJP6.rar”包含了备考SCJP 310-065的重要资源,帮助考生进行全真模拟...