The String class is one of the most frequently used classes in Java, and it’s one of the most popular classes to be asked about in job interviews. Here’s a look at the String, StringBuilder, and StringBuffer classes. Here is an outline of the table of contents for this article:

Do you know the String class?

Understand String, StringBuffer, and StringBuilder

3. Performance tests of three classes in different scenarios

StringBuffer (StringBuffer, StringBuffer)

If there is anything wrong, your understanding and correction will be greatly appreciated.

Please respect the fruits of the author’s labor.

www.cnblogs.com/dolphin0520…

Do you know the String class?

The best way to learn about a class is to look at its implementation source code

\jdk1.6.0_14\ SRC \ Java \lang\ string. Java

If you open the class file, you’ll see that the String class is final:

123456789101112131415161718192021 public  final  class  String``     implements  java.io.Serializable, Comparable<String>, CharSequence``{``     /** The value is used for character storage. */``     private  final  char  value[];      /** The offset is the first index of the storage that is used. */``     private  final  int  offset;      /** The count is the number of characters in the String. */``     private  final  int  count;      /** Cache the hash code for the string */``     private  int  hash;  // Default to 0      /** Use serialVersionUID from JDK 1.0.2 for interoperability */ ' private  static  final  long  serialVersionUID = -6849794470754667710L;      . }

A few points can be seen from the above:

1) The String class is final, which means that the String class cannot be inherited and its member methods are all final by default. In Java, classes decorated with final are not allowed to be inherited, and all member methods in that class default to final methods. In earlier JVM implementations, methods modified by final were converted to inline calls to improve execution efficiency. Since Java SE5/6, this approach has been gradually abandoned. Therefore, in the current version of Java SE, there is no need to consider using final to improve method invocation efficiency. Make the method final only if you are sure that you do not want the method to be overridden.

The String class uses an array of char attributes to hold a String.

Let’s look at some of the String method implementations:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 public  String substring( int  beginIndex,  int  endIndex) {``     if  (beginIndex <  0 ) {` ` throw  new  StringIndexOutOfBoundsException(beginIndex); ` ` } ` ` if  (endIndex > count) {``         throw  new  StringIndexOutOfBoundsException(endIndex); ` ` } ` ` if  (beginIndex > endIndex) {``         throw  new  StringIndexOutOfBoundsException(endIndex - beginIndex); ` ` } ` ` return  ((beginIndex ==  0 ) && (endIndex == count)) ?  this  : ` ` new  String(offset + beginIndex, endIndex - beginIndex, value); ` ` }   public  String concat(String str) {``     int  otherLen = str.length(); ` ` if  (otherLen ==  0 ) {` ` return  this ; ` ` } ` ` char  buf[] =  new  char [count + otherLen]; ` ` getChars( 0 , count, buf,  0 ); ` ` str.getChars( 0 , otherLen, buf, count); ` ` return  new  String( 0 , count + otherLen, buf); ` ` }   public  String replace( char  oldChar,  char  newChar) {``     if  (oldChar ! = newChar) {`` int  len = count; ` ` int  i = - 1 ; ` ` char [] val = value;  /* avoid getfield opcode */``         int  off = offset;    /* avoid getfield opcode */          while  (++i < len) {``         if  (val[off + i] == oldChar) {``             break ; ` ` } ` ` } ` ` if  (i < len) {``         char  buf[] =  new  char [len]; ` ` for  ( int  j =  0  ; j < i ; j++) {``             buf[j] = val[off+j]; ` ` } ` ` while  (i < len) {``             char  c = val[off + i]; ` ` buf[i] = (c == oldChar) ? newChar : c; ` ` i++; ` ` } ` ` return  new  String( 0 , len, buf); ` ` } ` ` } ` ` return  this ;

As you can see from the above three methods, neither the sub operation, concat operation, nor replace operation is performed on the original string, but instead regenerates a new string object. That is, the original string is not changed.

One thing to always remember here:

“Any changes to the String do not affect the original object, and any associated change operations generate new objects.”

Now that you know the basics of the String class, let’s look at some areas that are easily overlooked and confused in common use.

Understand String, StringBuffer, and StringBuilder

1.String STR =”hello world” and String STR =new String(“hello world”

Presumably you are familiar with the above two statements, in the usual process of writing code is often encountered, so what are the differences and connections between them? Here are a few examples:

12345678910111213 public  class  Main {``              public  static  void  main(String[] args) {``         String str1 =  "hello world" ; ` ` String str2 =  new  String( "hello world" ); ` ` String str3 =  "hello world" ; ` ` String str4 =  new  String( "hello world" ); ` `          System.out.println(str1==str2); ` ` System.out.println(str1==str3); ` ` System.out.println(str2==str4); ` ` } ` `}

The output of this code is

  

Why did this happen? Here’s why:

As mentioned in a previous post on JVM memory mechanisms, there is a section of the class file that stores literal constants and symbolic references generated during compilation. This section is called the class file constant pool, which corresponds to the runtime constant pool of the method area at runtime.

So in the code above, String str1 = “Hello world”; And String str3 = “Hello world”; Both literal constants and symbolic references are generated at compile time, and the runtime literal constant “Hello World” is stored in the runtime constant pool (only one copy, of course). To bind a String to a reference in this way, the JVM execution engine first looks for the same literal constant in the runtime constant pool, and if so, points the reference directly to the literal constant that already exists. Otherwise, create a space in the runtime constant pool to store the literal constant and refer to it.

As you know, object generation via the new keyword is done in the heap, and object generation in the heap does not detect whether the object already exists. So if you create an object with new, you’re going to create a different object, even if the contents of the string are the same.

2. The difference between String, StringBuffer, and StringBuilder

Why do we need StringBuilder and StringBuffer classes when we already have String classes in Java?

So look at this code:

123456789 public  class  Main {``              public  static  void  main(String[] args) {``         String string =  "" ; ` ` for ( int  i= 0 ; i< 10000 ; i++){`` string +=  "hello" ; ` ` } ` ` } ` `}

This string += “hello”; “Is equivalent to adding” Hello “to the string and storing it in a new string, and making the string refer to the newly generated object. Decompile its bytecode files if you have any questions:

  

From this decompilated bytecode file, it is clear that the entire loop is executed from line 8 to line 35, and that each loop creates a New StringBuilder object, append it, and returns a String via the toString method. That is to say, 10,000 new objects have been created after the loop is executed. Imagine how much memory would be wasted if these objects had not been collected. The string+=”hello” operation is actually automatically optimized by the JVM to:

StringBuilder str = new StringBuilder(string);

str.append(“hello”);

str.toString();

Take a look at the following code:

123456789 public  class  Main {``              public  static  void  main(String[] args) {``         StringBuilder stringBuilder =  new  StringBuilder(); ` ` for ( int  i= 0 ; i< 10000 ; i++){`` stringBuilder.append( "hello" ); ` ` } ` ` } ` `}

Decompiling the bytecode file yields:

  

It’s clear from here that the code’s for loop starts at line 13 and ends at line 27, and that the new operation is done only once, that is, only one object is generated, and the append operation is done on top of the original object. So after 10,000 loops, this code takes up a much smaller amount of resources.

So one might ask why we need a StringBuffer class when we have a StringBuilder class? If you look at the source code, you can see that StringBuilder and StringBuffer have essentially the same member attributes and methods. The difference is that the member methods of The StringBuffer class are preceded by a keyword: Synchronized, needless to say, is a keyword that is protected against multithreaded access, meaning that stringBuffers are thread-safe.

The following snippet of code is derived from StringBuffer and StringBuilder.

Insert method of StringBuilder

123456 public  StringBuilder insert( int  index,  char  str[],  int  offset,``                               int  len)``   {` ` super .insert(index, str, offset, len); ` ` return  this ; ` ` }

Insert method for StringBuffer:

123456 public  synchronized  StringBuffer insert( int  index,  char  str[],  int  offset,``                                             int  len)``     {` ` super .insert(index, str, offset, len); ` ` return  this ; ` ` }

3. Performance tests of three classes in different scenarios

From section 2 we have seen the difference between the three classes. In this section we will do a small test to test the performance difference of the three classes:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 public  class  Main {``     private  static  int  time =  50000 ; ` ` public  static  void  main(String[] args) {``         testString(); ` ` testStringBuffer(); ` ` testStringBuilder(); ` ` test1String(); ` ` test2String(); ` ` } ` `           public  static  void  testString () {``         String s= "" ; ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             s +=  "java" ; ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "Operation" +s.getClass().getName()+ "Type use time is:" +(over-begin)+ "毫秒" ); ` ` } ` `      public  static  void  testStringBuffer () {``         StringBuffer sb =  new  StringBuffer(); ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             sb.append( "java" ); ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "Operation" +sb.getClass().getName()+ "Type use time is:" +(over-begin)+ "毫秒" ); ` ` } ` `      public  static  void  testStringBuilder () {``         StringBuilder sb =  new  StringBuilder(); ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             sb.append( "java" ); ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "Operation" +sb.getClass().getName()+ "Type use time is:" +(over-begin)+ "毫秒" ); ` ` } ` `      public  static  void  test1String () {``         long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             String s =  "I" + "love" + "java" ; ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "String direct addition operation:" +(over-begin)+ "毫秒" ); ` ` } ` `      public  static  void  test2String () {``         String s1 = "I" ; ` ` String s2 =  "love" ; ` ` String s3 =  "java" ; ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             String s = s1+s2+s3; ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "String indirect addition operation:" +(over-begin)+ "毫秒" ); ` ` } ` ` }

Test results (Win7, Eclipse, JDK6) :

  

As mentioned above, string+=”hello” is in fact automatically optimized by the JVM. Look at this code:

1234567891011121314151617181920212223242526272829303132 public  class  Main {``     private  static  int  time =  50000 ; ` ` public  static  void  main(String[] args) {``         testString(); ` ` testOptimalString(); ` ` } ` `           public  static  void  testString () {``         String s= "" ; ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             s +=  "java" ; ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "Operation" +s.getClass().getName()+ "Type use time is:" +(over-begin)+ "毫秒" ); ` ` } ` `      public  static  void  testOptimalString () {``         String s= "" ; ` ` long  begin = System.currentTimeMillis(); ` ` for ( int  i= 0 ; i<time; i++){``             StringBuilder sb =  new  StringBuilder(s); ` ` sb.append( "java" ); ` ` s=sb.toString(); ` ` } ` ` long  over = System.currentTimeMillis(); ` ` System.out.println( "The simulated JVM optimization operation time is:" +(over-begin)+ "毫秒" ); ` ` } ` ` } ` ` 

Execution Result:

  

Verified.

The following is a general explanation of the above results:

1) It is efficient to add strings directly because the compiler determines their value, i.e., “I”+”love”+” Java “; Is optimized to “Ilovejava” at compile time. This can be verified using the javap -c command to decompile the generated class file.

For indirect addition (that is, including string references), s1+s2+s3; This is less efficient than direct addition because reference variables are not optimized at the compiler.

2) The execution efficiency of String, StringBuilder and StringBuffer:

StringBuilder > StringBuffer > String

Of course this is relative, not necessarily in all cases.

For example String STR = “hello”+ “world” is more efficient than StringBuilder st = new StringBuilder().append(“hello”).append(“world”)

Therefore, these three categories have their own advantages and disadvantages, and should be selected and used according to different situations:

String STR =”hello” is recommended for String addition or small changes.

StringBuilder is recommended when adding strings frequently, and StringBuffer is recommended when using multiple threads.

 

StringBuffer, StringBuffer, StringBuffer

Here are some common questions about String and StringBuffer:

1. What is the output of this code?

String a = “hello2”; String b = “hello” + 2; System.out.println((a == b));

The output is true. The reason is simple: “hello”+2 is optimized to “hello2” at compile time, so variable A and variable B refer to the same object at run time.

2. What is the output of the following code?

String a = “hello2”; String b = “hello”; String c = b + 2; System.out.println((a == c));

The output is false. String c = b + 2; It is not optimized at compile time, and b+2 is not treated as a literal constant, so objects generated this way are actually stored on the heap. So a and C don’t refer to the same object. Javap-c results in:

  

3. What is the output of the following code?

String a = “hello2”; final String b = “hello”; String c = b + 2; System.out.println((a == c));

The output is true. For final modified variables, a copy is kept in the class file constant pool, which means that no access is made through a connection, and any access to a final variable is directly replaced by the real value at compile time. So String c = b + 2; This is optimized at compile time to: String c = “hello” + 2; Here’s what javap-c looks like:

  

4. The following code outputs:

123456789101112 public  class  Main {``     public  static  void  main(String[] args) {``         String a =  "hello2" ; ` ` final  String b = getHello(); ` ` String c = b +  2 ; ` ` System.out.println((a == c)); ` ` } ` `      public  static  String getHello() {``         return  "hello" ; ` ` } ` `}

The output is false. B is final, but its assignment is returned by the method call, so its value can only be determined at run time. Therefore, a and C refer to different objects.

5. What is the output of the following code?

12345678910111213 public  class  Main {``     public  static  void  main(String[] args) {``         String a =  "hello" ; ` ` String b =   new  String( "hello" ); ` ` String c =   new  String( "hello" ); ` ` String d = b.intern(); ` `          System.out.println(a==b); ` ` System.out.println(b==c); ` ` System.out.println(b==d); ` ` System.out.println(a==d); ` ` } ` `}

The output is (JDK version JDK6) :

  

What’s involved here is the use of the String. Intern method. In the String class, intern is a native method that, prior to JAVA SE6, looked for a String with the same content in the runtime constant pool and returned a reference to the String if it did, or pooled it and returned a reference to the String if it didn’t. So, a and D refer to the same object.

6.String STR = new String(” ABC “) How many objects are created?

This problem is said in many books, such as “Java programmer interview treasure book”, including many domestic big companies will encounter written interview questions, most of the online circulation and some interview books are said to be two objects, this statement is one-sided.

If you do not understand the place can refer to this post:

Rednaxelafx.iteye.com/blog/774673…

First you have to figure out what it means to create an object. When was the creation created? Does this code create 2 objects at run time? Of course not, decompilation with Javap-c yields the bytecode content executed by the JVM:

  

Obviously, new is called only once, that is, only one object is created.

And here’s where it gets confusing, because this code actually only creates one object at run time, which is an “ABC” object on the heap. And why is everyone saying 2 objects? Well, there’s a clarification of the notion that there’s a difference between how the code executes and how the class loads. It is true that an “ABC” object is created in the runtime constant pool during class loading, and only a String object is created during code execution.

So, the question is if you change it to String STR = new String(” ABC “) how many strings are involved? The logical explanation is two.

Personally, if you encounter this question during the interview, you can ask the interviewer whether “how many objects are created or how many objects are involved in the execution of this code” and then answer it according to the specific.

7. What is the difference between 1) and 2) in this code?

12345678 public  class  Main {``     public  static  void  main(String[] args) {``         String str1 =  "I" ; ` ` //str1 += "love"+"java"; 1) ` ` str1 = str1+ "love" + "java" ;       / / 2) ` `      } ` `}

1) is more efficient than 2), “love”+” Java “in 1) is optimized to” loveJava “at compile time, and 2) is not optimized. Here are two ways to get bytecode:

1) bytecode:

  

2) bytecode:

  

As you can see, there is only one append operation in 1) and two appends in 2).

Refer to the article: rednaxelafx.iteye.com/blog/774673…

www.blogjava.net/Jack2007/ar…

www.jb51.net/article/360…

Blog.csdn.net/yirentianra…

www.jb51.net/article/333…