java常用类05_StringBuilder类

1.StringBuilder类概述

StringBuffer类和StringBuilder类非常类似,都是继承自抽象类AbstractStringBuilder类,均代表可变的Unicode字符序列。StringBuilder类和StringBuffer类方法几乎一模一样,不过StringBuilder不是线程安全的,这是和StringBuffer的主要区别:

  • StringBuffer线程安全,做线程同步检查,效率较低。

  • StringBuilder线程不安全,不做线程同步检查,因此效率较高(推荐使用)。

因为JDK1.5版本之前只有一个StringBuffer类(线程安全),由于发现利用多线程对同一字符串数据操作的情况是很少的,为了提高效率JDK1.5以后就有了StringBuilder类,StringBuilder类提供一个与StringBuffer兼容的API,所以StringBuilder类和StringBuffer类的方法和功能完全一致。

目前,我们还没有涉及到线程与同步,知道结论 StringBuilder 比 StringBuffer 快即可。

2.字符串拼接优化

对于常量做字符串拼接操作时,对于编译期就能确定的值,编译器会将值合并。

【示例】常量做字符串拼接

public class Test {
	public static void main(String[] args) {
		test01();
		test02();
	}
	/**
	 * 直接量拼接,对于编译期就能确定的值,编译器会将值合并
	 * String hw = "hello" + "world";反编译class
	 * 我们将看到 : String hw = "helloworld";
     *
	 * 所以hw == helloWorld 输出true
	 */
	public static void test01() {
		String hw = "hello" + "world"; // 等于:String hw = "helloworld";
		String helloWolrd = "helloworld";
		System.out.println(hw == helloWolrd); // 输出:true
	}
	/**
	 * String hw = h + "world";虽然包含变量 h的运算,但是编译器
	 * 对fianl变量在编译期能确定其值,会发生宏替换,即:h变量替换成其值"hello",
	 * 然后编译器会对直接量字串直接合并
	 * String hw = h + "world";在编译完后就变成了 String hw = "helloworld";
     *
	 * 所以hw == helloWorld 输出true
	 */
	public static void test02() {
		final String h = "hello"; // final修饰的是常量
		String hw = h + "world"; // 等于:String hw = "helloworld";
		String helloWolrd = "helloworld";
		System.out.println(hw == helloWolrd); // 输出:true
	}
}

对于变量做字符串拼接操作时,由于相加的变量中存放的是字符串的地址引用,因为在编译时无法确切地知道其他具体的值,也就没有办法对其进行优化处理,这时为了达到连接的效果,其内部采用了 StringBuilder 的机制进行处理(JDK 5 中新增的,我这里没有 JDK 1.4,估计在 JDK 1.4 下采用的是 StringBuffer),将他们都 append进去,最后用 toString 输出。
【示例】变量做字符串拼接操作

public class Test {
	/**
	 * hw在编译期并不能确定值,因为h是变量,JVM在运行期才能确定其值
	 * 会在运行期时,进行字串拼接生成新的字串对象,通过javap -c Test查看虚拟机指令
	 * 我们发现String hw = h + "world";其实等效于:
	 * StringBuilder sb = new StringBuilder(h);
	 * sb.append("world");
	 * String hw = sb.toString();
	 *
	 * 所以hw == helloWorld 输出false
	 */
	public static void main(String[] args) {
		String h = "hello";
		String hw = h + "world";
		String helloWolrd = "helloworld";
		System.out.println(hw == helloWolrd); // 输出:false 
	}
}

3.字符串的效率比较

分别使用String、StringBuffer、StringBuilder进行10000次的字符串拼接操作,通过计算运行时间来分析执行的效率。

【示例】字符串拼接效率比较

public class StringBuilderDemo {
	public static void main(String[] args) {
		stringBuilderMethod(); // 测试StringBuilder耗时
		stringBufferMethod(); // 测试StringBuffer耗时
		stringMethod(); // 测试String耗时 
	}
	// 测试String耗时
	public static void stringMethod() {
		String str = "";
		long now1 = System.currentTimeMillis(); // 开始时间
		for (int i = 0; i < 10000; i++) { // 进行10000次字符串拼接
			str += i; // 相当于产生了10000个StringBuffer对象
		}
		long now2 = System.currentTimeMillis(); // 结束时间
		System.out.println("String耗时:" + (now2 - now1) + "ms");
	}
	// 测试StringBuffer耗时
	public static void stringBufferMethod() {
		StringBuffer sb = new StringBuffer();
		long now1 = System.currentTimeMillis(); // 开始时间
		for (int i = 0; i < 10000; i++) { // 进行10000次字符串拼接
			sb.append(i);
		}
		long now2 = System.currentTimeMillis();	// 结束时间
		System.out.println("StringBuffer耗时:" + (now2 - now1) + "ms");
	}
	// 测试StringBuilder耗时
	public static void stringBuilderMethod() {
		StringBuilder sb = new StringBuilder();
		long now1 = System.currentTimeMillis(); // 开始时间
		for (int i = 0; i < 10000; i++) { // 进行10000次字符串拼接
			sb.append(i);
		}
		long now2 = System.currentTimeMillis(); // 结束时间
		System.out.println("StringBuilder耗时:" + (now2 - now1) + "ms");
	}
}

以上代码执行结果为:
在这里插入图片描述
StringBuffer和StringBuilder的运行效率几乎一致,这是因为二者都是对对象本身进行操作,不会生成新的字符串对象。二者的区别主要是StringBuffer是线程安全的,而StringBuilder是线程不安全的,所以StringBuilder的运行效率略高于StringBuffer。

String是不可变的对象,每次对String进行操作的时候,都会生成一个新的对象(StringBuilder),占用大量内存空间,这会对系统的性能造成影响,执行效率最低。

所以在平时编程的过程中,如果字符串需要经常改变,应该尽量避免使用String。

4.对象方法链式调用

在我们日常开发中,会遇到调用一个方法后,返回一个对象的情况,这种设计允许用户连续的链式调用方法,从而实现按人的惯性思维进行快速开发,我们称之为链式语法。在我们学过的常用工具类中,StringBuffer类和StringBuilder类中的方法都是使用链式语法的典范,通过在方法添加return this的形式来实现方法的链式调用,我们打开StringBuilder的源码:
在这里插入图片描述
从源码中我们可以看到,我们每次调用StringBuilder类中的insert方法,都会返回当前可变字符串对象,然后我们可以使用返回的对象继续调用方法,从而实现了方法链式调用。

【示例】对象的方法链式调用案例

// 创建一个可变字符容器,用来存放数据
StringBuilder sb = new StringBuilder("hello-world");
// 通过链式语法操作可变字符串数据
sb.append("-WHSXT").insert(4, "AAA").delete(6, 8);
// 输出:"hellAA-world-WHSXT"
System.out.println(sb);

ps:如需最新的免费文档资料和教学视频,请添加QQ群(627407545)领取。

发布了92 篇原创文章 · 获赞 0 · 访问量 2596

猜你喜欢

转载自blog.csdn.net/zhoujunfeng121/article/details/104784286