`
wen866595
  • 浏览: 264191 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java program idiom 之 String

    博客分类:
  • java
 
阅读更多

1.1.  String API

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

 

A、 substring subSequence 方法。

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

如果原字符串是一个很大的临时字符串,但只需保留一小块子字符串时,使用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.  字符串拼接

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

+拼接示例:

 

	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、 从第14 16 19 21 字节码指令说明,如果 + 操作中涉及变量,编译器不会合并常量;

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

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

 

 

根据上面的结果:

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

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

 

 

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

 

 

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

StringBuilder是非线程安全版的 StringBuffer

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics