String StringBuffer StringBuilder

    相信在面试过程中很多面试官都会问到String,StringBuffer,StringBuilder的区别。但是对于它的机制,原理会头晕。这就要拿出JVM的工作原理了。

     大家都知道String是由“字符”组成的串,在程序中使用的频率很高,String是java中的一个类,但又是一个特殊的类。具体特殊在哪?

     1、 String类对象的创建方式有2种:

          方式一:String str = new String("hello world"); //利用构造器创建。这是java类中很普遍的

          方式二:String str = "hello world"; //这个类似于基本类型赋值,但是它又不是基本类型。

    2、java class文件结构和常量池

         Java程序要运行,首先需要编译器把源文件编译成字节码文件(即.class文件),然后由JVM来解释执行。

      class文件是8bit的二进制流。文件中最开始的4个字节组成的叫magic(魔数),其作用在于分辨是否为class文件。紧接着是version,contant pool(常量池),Access rights ,Implemented interfaces,fields等等。

      

package com.wireless.xuwei;

public class StringDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("hello world!");
	}

}

    此时上述代码中的"hello world!"字符串字面值被编译之后,可以清楚的看到存放在了class常量池中的字符串常量表中。如图所示:


3、 JVM运行class文件的原理

      源码被编译成class文件之后,JVM就要运行这个class文件。它首先会先用类装载器ClassLoader来加载class文件。(我们在开发过程中导入的jar包通常都是把class打成jar包,如果该包中的class的方法@hide,需要用到反射机制,此时也是通过类加载器加载这个class文件作为Class类的一个实例,再通过这个实例去获取它的属性,构造器,类方法或者是成员方法等)。当JVM把class文件加载完成后,会创建许多内存数据结构来存放class文件中的字节数据。如class文件中的属性,常量,类方法,方法中的二进制指令序列等信息。当然在运行的时候,需要为方法创建栈帧等。这些数据都会被JVM放到“方法区”,“”,“”。

4、拘留字符串对象

      在java源码中的每一个字面值字符串(如String str = "java"),其中"java"就是字面值字符串,它会在编译成class文件时,形成标志号为8(CONSTANT_String_info)的常量表。当JVM加载class 文件时,会为对应的常量池建立一个内存数据结构,并存放在方法区中。同时JVM又会为字符串字面值"java"在堆中创建一个新的String对象,该对象叫拘留字符串对象。同时然后把常量表中的入口地址转为堆中挽留字符串对象的地址。

package com.wireless.xuwei;

public class StringDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("hello world!");
		
		String s1 = "a"; //s1指向的是拘留对象地址
		String s2 = "a";
		System.out.println(s1 == s2); //true 由此可见,源码中所有相同字面值的字符串
		//都只能建立一个唯一的拘留字符串对象。
		
		String str = new String("a");//在堆中分配一个String类对象的空间。
		//str指向的是该对象的地址
		String str2 = new String("a");
		System.out.println(str == str2);//false 因为二者都是指对String类对象的地址值。但地址值不一样
		
		String s3 = s1 + s2;
		String aa = "aa";
		System.out.println(aa == s3);//false 因为s1是指向拘留对象的址,在进行"+"
		//运行时,JVM会在堆中创建一个StringBuilder的类,内容为挽留对象的内容。然后再
		//调用append()把s2指向的挽留对象值追加进来,然后StringBuilder会调用toString()
		//会在堆中创建一个String类的对象,并将该String类的对象地址值给了s3
		
		System.out.println(aa == "a" + "a");//true 因为在java中字面值的运行都是在编译期间就执行了。
		//即aa指向的是一个拘留对象引用值,内容为"aa",而"a"+"a"在编译期间就成为了"aa"
		//此时JVM发现堆中已经存在内容一样的挽留对象,则不再创建新的对象,即二者即为同一个。
	}

}

 5、 StringBuffer StringBuilder

        StringBuffer它是一个线程安全的,可调用内部的一些方法(如appen())来达到改变字符串。

           StringBuilder是一个非线程安全的。但是它的性能要超过StringBuffer,二者的使用几乎一样。

6、 总结

 

一) 在编译阶段就能确定的,即只有字面量字符串进行“+”时,选用String性能最好;如果字符串是从其他来          的,如String str = "a"; String str2 = str + "bc";它实质上就执行上述的JVM的顺序了。即先创建一个                StringBuilder类的对象。内容为拘留对象的内容"a",再执行append(),把另外一个挽留对象的内                   容“bc”也加进来,再调用toString()方法再在堆中创建一个新的String类的对象。 

   

二) 如果在进行“+”运算时,字符串内容是来自其他的String,"+"操作过多时会造成内存溢出的。因为                   每”+“一次就创建一个新的对象,过多了JVM会来不及回收而造成内存溢出。

 

三)在没有线程安全要求的情况下,应选择StringBuilder,性能远强于StringBuffer

猜你喜欢

转载自ilovejoe.iteye.com/blog/2144867