`
bingooh
  • 浏览: 52973 次
  • 性别: Icon_minigender_1
  • 来自: 佛山
社区版块
存档分类
最新评论

String, StringBuffer, StringBuilder的区别

 
阅读更多

先来看以下问题,以下代码创建了几个对象?

 

String s1=new String("abc");

 

 

网上比较流行的说法是创建了两个对象:字符串对象abc和引用对象s1。这种说法十分模糊,什么是引用对象?s1究竟是引用还是对象?

 

正确的说法应该是s1是引用了一个字符串对象的变量,通过s1可以操作其所引用的字符串对象。因此从某种意义上,可以把s1直接视为对象(可以把s1理解为一个指向字符串对象的指针,但建议不要这样理解,因为Java里没有指针的概念)

 

查看String的帮助文档可以知道,执行以上代码将创建一个字符串对象,此对象的内容将“复制”所传入的参数值,这里为字符串abc。很显然,必须先创建abc然后才能进行复制。那么是谁创建的abc呢?

 

在Java里,字符串值如“abc”被实现为常量。在使用abc时,首先JVM会查找内存看是否已存在字符串abc,如果不存在则创建字符串abc对象,在随后的使用中,字符串abc对象都不会被改变。因此abc可称为字符串常量

 

因此执行以上代码可以肯定的是将创建变量s1指向的字符串对象。而在执行此代码前,如果abc对象不存在,那么将先创建abc字符串常量对象,否则不会创建。

 

因为字符串实现为常量,如果所使用的字符串在内存里不存在,那么JVM需要先创建他们(即为其分配内存),这可能会比较影响性能,以下代码将会创建4个字符串常量对象,即包含4次分配内存的操作:

String s1="a";
String s2="b";
String s3="c";
String s4=s1+s2+s3;

 

为了解决以上问题,Java提供了StringBuffer类。在创建StringBuffer对象时,会为其分配一块内存缓冲区,用于存放需要拼接的字符串值。在缓冲区存满前都不会再次进行分配,进而提高性能。

 

StringBuilder与StringBuffer基本相同,不同点在于StringBuilder是非线程安全的,而StringBuffer是线程安全的。因此StringBuilder性能高于StringBuffer

 

以下使用TestNG对以上部分观点进行了测试

 

以下测试说明,如果字符串常量对象已存在,那么后续变量将直接引用此对象:

package com.bingo.practice;

import org.testng.Assert;
import org.testng.annotations.Test;

public class StringEqualTest {
	
	@Test
	public void test(){
		String s1=new String("abc");
		String s2=new String("abc");
		
		//s1,s2引用不同的字符串对象,但是其字符串值相同
		Assert.assertEquals(s1, s2);
		Assert.assertEquals(s1, "abc");
		Assert.assertNotSame(s1, s2);
		
		String s3="abc";
		String s4="abc";
		
		//s3, s4都引用字符串常量对象abc
		Assert.assertEquals(s3, s4);
		Assert.assertSame(s3, s4);
	}
}

 

以下测试拼接字符串的性能比较:StringBuilder>StringBuffer>String

package com.bingo.practice;

import org.testng.annotations.Test;

public class StringPerformanceTest {
	private static final int count=100000;
	
	@Test
	public void testStringBuilder(){
		StringBuilder b=new StringBuilder();
		for(int i=0;i<count;i++)
			b.append("a");
		System.out.println(b.toString());
	}
	
	@Test
	public void testStringBuffer(){
		StringBuffer s=new StringBuffer();
		for(int i=0;i<count;i++)
			s.append("a");
		System.out.println(s.toString());
	}	
	
	@Test
	public void testString(){
		String s="";
		for(int i=0;i<count;i++)
			s+="a";
		System.out.println(s);
	}	
}

 

测试结果如下:

 

以下测试线程安全,String,StringBuilder非线程安全(测试失败),StringBuffer线程安全(测试通过)

package com.bingo.practice;

import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class StringTest {
	private String actualString;
	private String expectedString;
	
	@BeforeClass
	public void init(){
		this.actualString="";
		this.expectedString="";
		
		for(int i=0;i<10000;i++)
			expectedString+="a";
	}
	
	@Test(invocationCount=10000,threadPoolSize=10)
	public void test(){
		actualString+="a";
	}
	
	@AfterClass
	public void finish(){
		Assert.assertEquals(actualString, expectedString);
	}
}

 

 

package com.bingo.practice;

import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class StringBuilderTest {
	private StringBuilder actualBuilder;
	private StringBuilder expectedBuilder;
	
	@BeforeClass
	public void init(){
		this.actualBuilder=new StringBuilder();
		this.expectedBuilder=new StringBuilder();
		
		for(int i=0;i<10000;i++)
			expectedBuilder.append("a");
	}
	
	@Test(invocationCount=10000,threadPoolSize=10)
	public void test(){
		actualBuilder.append("a");
	}
	
	@AfterClass
	public void finish(){
		Assert.assertEquals(actualBuilder.toString(), expectedBuilder.toString());
	}
}

 

 

package com.bingo.practice;

import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class StringBufferTest {
	private StringBuffer actualBuffer;
	private StringBuffer expectedBuffer;
	
	@BeforeClass
	public void init(){
		this.actualBuffer=new StringBuffer();
		this.expectedBuffer=new StringBuffer();
		
		for(int i=0;i<10000;i++)
			expectedBuffer.append("a");
	}
	
	@Test(invocationCount=10000,threadPoolSize=10)
	public void test(){
		actualBuffer.append("a");
	}
	
	@AfterClass
	public void finish(){
		Assert.assertEquals(actualBuffer.toString(), expectedBuffer.toString());
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 7.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics