String 字符串常量
StringBuffer字符串变量(线程安全)
StringBuilder字符串变量(非线程安全)
简要的说, String 类型和StringBuffer类型的主要性能区别其实在于 String 是不可变的对象, 所以在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,而后将指针指向新的 String 对象,因此常常改变内容的字符串最好不要用 String ,由于每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了之后, JVM 的 GC 就会开始工做,那速度是必定会至关慢的。
而若是是使用StringBuffer类则结果就不同了,每次结果都会对StringBuffer对象自己进行操做,而不是生成新的对象,再改变对象引用。因此在通常状况下咱们推荐使用StringBuffer,特别是字符串对象常常改变的状况下。而在某些特别状况下, String 对象的字符串拼接实际上是被 JVM 解释成了StringBuffer对象的拼接,因此这些时候 String 对象的速度并不会比StringBuffer对象慢,而特别是如下的字符串对象生成中, String 效率是远要比StringBuffer快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候StringBuffer竟然速度上根本一点都不占优点。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 因此固然不须要太多的时间了。但你们这里要注意的是,若是你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去作html
在大部分状况下StringBuffer > String
StringBufferjava
Java.lang.StringBuffer线程安全的可变字符序列。一个相似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但经过某些方法调用能够改变该序列的长度和内容。程序员
每一个字符串缓冲区都有必定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。若是内部缓冲区溢出,则此容量自动增大。从 JDK 5.0 开始,为该类增添了一个单个线程使用的等价类,即 StringBuilder 。与该类相比,一般应该优先使用 StringBuilder 类,由于它支持全部相同的操做,但因为它不执行同步,因此速度更快。编程
可将字符串缓冲区安全地用于多个线程。能够在必要时对这些方法进行同步,所以任意特定实例上的全部操做就好像是以串行顺序发生的,该顺序与所涉及的每一个线程进行的方法调用顺序一致。
StringBuffer上的主要操做是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每一个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,若是 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
在大部分状况下StringBuilder > StringBuffer数组
java.lang.StringBuilder缓存
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与StringBuffer兼容的 API,但不保证同步。该类被设计用做StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种状况很广泛)。若是可能,建议优先采用该类,由于在大多数实现中,它比StringBuffer要快。二者的方法基本相同。安全
可是若是将 StringBuilder 的实例用于多个线程是不安全的。须要这样的同步,则建议使用 StringBuffer 。性能优化
ok,Talk is cheap,show you the code:app
package com.test; public class Testssb { /** Creates a new instance of testssb */ final static int ttime = 70000;// 测试循环次数 public Testssb() { } public void test(String s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s += "add"; } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); } public void test(StringBuffer s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s.append("add"); } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); } public void test(StringBuilder s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s.append("add"); } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); } // 对 String 直接进行字符串拼接的测试 public void test2(){ String s2 = "abadf"; long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ String s = s2 + s2 + s2 ; } long over = System.currentTimeMillis(); System.out.println(" 操做字符串对象引用相加类型使用的时间为: " + (over - begin) + " 毫秒 " ); } public void test3(){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ String s = "abadf" + "abadf" + "abadf" ; } long over = System.currentTimeMillis(); System.out.println(" 操做字符串相加使用的时间为: " + (over - begin) + " 毫秒 " ); } public static void main(String[] args){ String s1 ="abc"; StringBuffer sb1 = new StringBuffer("abc"); StringBuilder sb2 = new StringBuilder("abc"); Testssb t = new Testssb(); t.test(s1); t.test(sb1); t.test(sb2); t.test2(); t.test3(); } }
测试结果:jvm
50000 操做 java.lang.String 类型使用的时间为: 10456 毫秒 操做 java.lang.StringBuffer 类型使用的时间为: 4 毫秒 操做 java.lang.StringBuilder 类型使用的时间为: 3 毫秒 操做字符串对象引用相加类型使用的时间为: 23 毫秒 操做字符串相加使用的时间为: 1 毫秒 60000 操做 java.lang.String 类型使用的时间为: 17455 毫秒 操做 java.lang.StringBuffer 类型使用的时间为: 4 毫秒 操做 java.lang.StringBuilder 类型使用的时间为: 3 毫秒 操做字符串对象引用相加类型使用的时间为: 13 毫秒 操做字符串相加使用的时间为: 1 毫秒 70000 操做 java.lang.String 类型使用的时间为: 25882 毫秒 操做 java.lang.StringBuffer 类型使用的时间为: 5 毫秒 操做 java.lang.StringBuilder 类型使用的时间为: 3 毫秒 操做字符串对象引用相加类型使用的时间为: 14 毫秒 操做字符串相加使用的时间为: 1 毫秒
你还能够往下继续加大测试数据,只是个人本子扛不住了。。。就不测了。。。
其实我这里测试并非很公平,由于都放在了一块儿以前后顺序进行,测试方法中间没有考虑到JVM的GC收集前面产生的无引用对象垃圾而对执行过程的中断时间。若是你们有更好的想法或者思路欢迎跟我讨论:decli AT qq DOT com
关于上文所述:在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”;
这里的表述不恰当
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 这么说是对的,可是若是是jvm的把戏,那么String S1 = “This is only a” + “ simple” + “test”; 是否是会和
String S3=“This is only a”;
S3+=“ simple”;
S3+=“test”;
System.out.println(S1==S3);的结果是同样呢?很显然结果是false;
其实这并非JVM的把戏,而是java编译器的把戏。
给出以下源代码
package com.test; public class T { public static void main(String[] args) { String s1 = "This is only a" + " simple " + "test"; String s2 = "This is only a"; s2+= " simple "; s2+= "test"; System.out.println(s1==s2); } }
那么它编译(JDK5.0)后的class会是什么样的呢:
H:\tmp_download>jad -p T.class // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: T.java package com.test; import java.io.PrintStream; public class T { public T() { } public static void main(String args[]) { String s1 = "This is only a simple test"; String s2 = "This is only a"; s2 = (new StringBuilder(String.valueOf(s2))).append(" simple ").toString(); s2 = (new StringBuilder(String.valueOf(s2))).append("test").toString(); System.out.println(s1 == s2); } } H:\tmp_download>
能够看到 + 变成了 StringBuilder 的方式,这是Javac的问题,也是Javac优化class的一个技巧。
http://stackoverflow.com/questions/8725739/stringbuilder-usage
注:不要用 Java decompiler 去编译,这个工具会作一些美化,而要用 JAD:
JAVA高级:反编译工具jad的简单用法
http://tech.sina.com.cn/s/2007-09-28/09061768599.shtml
http://www.varaneckas.com/jad/
最后,咱们再来看个常见的问题:
new String() vs literal string(字面量)
Java运行环境有一个字符串常量池(String constant pool),由String类维护。执行语句String s="abc"时,首先查看字符串池中是否存在字符串"abc",若是存在则直接将"abc"赋给s,若是不存在则先在字符串池中新建一个字符串"abc",而后再将其赋给s。
执行语句String s=new String("abc")时,在运行时涉及 2 个String实例,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另外一个是经过new String(String)建立并初始化的、内容与"xyz"相同的实例。
前一语句的效率高,后一语句的效率低,由于新建字符串须要占用内存空间和时间。
注意如下区别:
Also, a little test you can do to drive the point home: String a = new String("xpto"); String b = new String("xpto"); String c = "xpto"; String d = "xpto"; String e = a.intern(); System.out.println(a == b); System.out.println(a == c); System.out.println(c == d); System.out.println(c == e); With all this, you can probably figure out the results of these Sysouts: false false true true Since c and d are the same object, the == comparison holds true.
intern()
对于上面使用new建立的字符串对象,若是想将这个对象的引用加入到字符串常量池,可使用intern方法。
调用intern后,首先检查字符串常量池中是否有该对象的引用,若是存在,则将这个引用返回给变量,不然将引用加入并返回给变量。
http://stackoverflow.com/questions/14757978/new-string-vs-literal-string-performance
http://rednaxelafx.iteye.com/blog/774673
http://zhidao.baidu.com/question/26614057.html
http://developer.51cto.com/art/201106/266454.htm Java中的String与常量池
http://www.iteye.com/topic/634530 Java堆.栈和常量池 笔记
http://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/?comefrom=http://blogread.cn/news/ Java中的字符串常量池
REF:
是 String , StringBuffer 仍是 StringBuilder ?
http://www.blogjava.net/chenpengyi/archive/2006/05/04/44492.html
http://baike.baidu.com/view/3645996.htm
深刻理解Java中的String
http://g21121.iteye.com/blog/1873262
为何String类是不可变的?
http://www.importnew.com/7440.html
JDK6 和 JDK7 中的 substring() 实现差别(JDK6和JDK7中的substring()方法)
http://www.importnew.com/7418.html
Java中的String对象是不可变的吗
http://www.importnew.com/9468.html
Oracle优化Java字符串内部表示
http://www.infoq.com/cn/news/2013/12/Oracle-Tunes-Java-String
如何写一个不可变类?
http://www.importnew.com/7535.html
深刻理解Java中的final关键字
http://www.importnew.com/7553.html
Java 性能优化之 String 篇
http://www.ibm.com/developerworks/cn/java/j-lo-optmizestring/
Integer.valueOf(String) 方法之惑
http://www.importnew.com/9162.html
public static void main(String[] args) throws IOException { System.out.println(Integer.valueOf(127)==Integer.valueOf(127)); System.out.println(Integer.valueOf("127")==Integer.valueOf("127")); System.out.println(Integer.valueOf("128")==Integer.valueOf("128")); System.out.println(Integer.parseInt("128")==Integer.valueOf("128")); } // true false false true
Java中关于String类型的10个问题
http://www.importnew.com/12845.html
什么是字符串常量池?
http://www.importnew.com/10756.html
Java程序员们最常犯的10个错误
http://www.importnew.com/12074.html
Java编程提升性能时需注意的地方
http://blog.jobbole.com/16474/
尽可能使用基本数据类型代替对象:
String str = "hello";
上面这种方式会建立一个“hello”字符串,并且JVM的字符缓存池还会缓存这个字符串;
String str = new String("hello");
此时程序除建立字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o
Java中的substring真的会引发内存泄露么?
http://droidyue.com/blog/2014/12/14/substring-memory-issue-in-java/
浅谈StringBuilder
“for循环中使用 "+" 拼接为何这么慢”:用 "+" 进行拼接,都会转化成 StringBuilder 对象,可是在for循环中,每循环一次,就建立一个StringBuilder 对象,若是循环1千万次,就建立了1千万个StringBuilder 对象,性能会差不少。