Java program idiom 之 String

1.1.  String API

String对象是不可变的,那些看似改变了对象的方法其实是返回新的 String 对象,有一些方法使用时是要注意的。

 

A、 substring、 subSequence 方法。

String底层是使用字符数组来存储的, substring subSequence 方法返回新的StringCharSequence ,共享底层的字节数组,因为它是不可变的,这能提升性能,但也能导致问题。

如果原字符串是一个很大的临时字符串,但只需保留一小块子字符串时,使用sub* 就会导致内存的浪费。这篇博客描述了问题的情况:

http://www.ibm.com/developerworks/cn/java/j-lo-optmizestring/

 

 

B、 实现对象的toString 方法

这点是任何类实现toString 方法时都必须注意的。

public class InfinitRecursion {
	public String toString() {
		return "address:" + this;
	}

	public static void main(String[] args) {
		new InfinitRecursion().toString();
	}
}

  InfinitRecursion  类的 toString方法是希望输出对象的内存地址,但调用这个方法将导致 StackOverflowError  ,因为使用"+"  连接时会,编译器看到 this 不是一个 String 对象,会尝试将 this 转换成一个 String ,而这个转换就是通过调用 toString 方法实现的,这就无意发生了递归调用,导致栈溢出异常。要打印对象的内存地址,应该使用 Object.toString()

 

 

1.2.  字符串拼接

字符串拼接一般有:+StringBufferStringBuilder  三种形式。

+拼接示例:

 

	public static void main(String[] args) {
		String a = "123" + "456" + "a";
		String ab = a + "b" + "+-*/";
		String abc = ab + "c";
	}
 

使用 “ javap -c  类名 ” 查看编译后的字节码大致是这样的:

  public static void main(java.lang.String[]);

    Code:

       0: ldc           #2                  // String 123456a

       2: astore_1

       3: new           #3                  // class java/lang/StringBuilder

       6: dup

       7: invokespecial #4                  // Method java/lang/StringBuilder."<

init>":()V

      10: aload_1

      11: invokevirtual #5                  // Method java/lang/StringBuilder.ap

pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      14: ldc           #6                  // String b

      16: invokevirtual #5                  // Method java/lang/StringBuilder.ap

pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      19: ldc           #7                  // String +-*/

      21: invokevirtual #5                  // Method java/lang/StringBuilder.ap

pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      24: invokevirtual #8                  // Method java/lang/StringBuilder.to

String:()Ljava/lang/String;

      27: astore_2

      28: new           #3                  // class java/lang/StringBuilder

      31: dup

      32: invokespecial #4                  // Method java/lang/StringBuilder."<

init>":()V

      35: aload_2

      36: invokevirtual #5                  // Method java/lang/StringBuilder.ap

pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      39: ldc           #9                  // String c

      41: invokevirtual #5                  // Method java/lang/StringBuilder.ap

pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      44: invokevirtual #8                  // Method java/lang/StringBuilder.to

String:()Ljava/lang/String;

      47: astore_3

      48: return

 

从字节码可以看到:

1、 0: ldc #2  // String 123456a ” 说明如果 + 只涉及常量,编译器会把常量合并了;

2、 从第14161921 字节码指令说明,如果 + 操作中涉及变量,编译器不会合并常量;

3、 324 条字节码指令说明,编译器将 + 操作转换为 StringBuidler.append 操作,在执行赋值语句时会调用 StringBuilder.toString 方法来生成完整的字符串。

4、 324 条指令 与  2844 条指令说明,对于每条使用 + 拼接进行赋值的语句都会生成一个 StringBuilder

 

 

根据上面的结果:

1、 尽量不要使用+ 拼接,就算要用,也应该写成一条语句。

2、 绝对不应该在循环语句里使用+ 拼接,这会导致生成大量的 StringBuilder

 

 

StringBuilder和 StringBuffer 都使用数组作为底层存储结构,通过预分配数组空间,避免在每次 append 时扩容,有助于提升性能。

 

 

StringBuffer的所有方法都是线程安全的,在不需要线程安全的情况下,这会带来额外的性能开销。

StringBuilder是非线程安全版的 StringBuffer

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值