String类源码分析:
- String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被 final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用 以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。
- String类中char value[],int offset,int count,int hash成员属性,可以看出String类其实是通过char数组来保存字符串的。
- 从String类提供的方法比如subString,concat,replace等操作都算不是在原有的字符串对象上进行操作,subString是新new一个String对象,后两个都是新new一个char 数组,也就是说进行这些操作后,最原始的字符串并没有被改变。
特别提醒:对String对象的任何改变都不影响到原对象,相关的任何修改操作都会生成新的对象。
String StringBuffer StringBuilder的区别;
在 系统里面String可以满足任何操作,为什么还要有StringBuffer和StringBuilder类,给大家看段代码我想会更清楚的表达出来。
public class Main { public static void main(String[] args) { String string = ""; for(int i=0;i<10000;i++){ string += "hello"; } } }
我想这段代码大家很清楚就明白是什么意识,无非就是字符串循环相加,这样理解是没有错误的,在上面我们通过String类了解过,String类的任何操作都是新new一个对象, 而不是在原有的基础上进行相加,比如:string +="hello"这句话,如果10000次,那么就要创建10000个新StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象。也就 是说这个循环执行完毕new出了10000个对象,试想一下,如果这些对象没有被回收,会造成多大的内存资源浪费。从上面还可以看 出:string+="hello"的操作事实上会自动被JVM优化成:
StringBuilder str = new StringBuilder(string);
str.append("hello");
str.toString();
为了验证我的推理是正确的,可以通过如下代码进行验证:
public class Main { private static int time = 50000; public static void main(String[] args) { testString(); testOptimalString(); } public static void testString () { String s=""; long begin = System.currentTimeMillis(); for(int i=0; i<time; i++){ s += "java"; } long over = System.currentTimeMillis(); System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒"); } public static void testOptimalString () { String s=""; long begin = System.currentTimeMillis(); for(int i=0; i<time; i++){ StringBuilder sb = new StringBuilder(s); sb.append("java"); s=sb.toString(); } long over = System.currentTimeMillis(); System.out.println("模拟JVM优化操作的时间为:"+(over-begin)+"毫秒"); } }
运行结果为:
操作java.lang.String类型使用的时间为:3194毫秒 模拟JVM优化操作的时间为:3197毫秒
如果系统中很多地方都这样进行拼接,这将是计算机的灾难,为了提高字符串拼接的性能,让我们看一段StringBuilder的代码:
public class Main { public static void main(String[] args) { StringBuilder stringBuilder = new StringBuilder(); for(int i=0;i<10000;i++){ stringBuilder.append("hello"); } } }
从这里可以明显看出,这段代码的for循环开始到结束只进行了new操作,创建一个StringBuilder对象,字符串拼接都是通过append操作是在原有对象的基础上进行的。因此在循环了10000次之后,这段代码所占的资源要比上面小得多。有人有说了,既然StringBuilder可以提高字符串拼接的性能,那么怎么还要有StringBuffer,让我们在来看一段代码你就会明白。
StringBuilder 的insert操作代码 public StringBuilder insert(int index, char str[], int offset, int len){ super.insert(index, str, offset, len); return this; } StringBuffer 的insert操作代码 public synchronized StringBuffer insert(int index, char str[], int offset, int len){ super.insert(index, str, offset, len); return this; }
通过两段代码可以发现就是StringBuffer比StringBuilder多了一个synchronized多了一把锁,也就是说StringBuffer是线程安全的。
String StringBuffer StringBuilder的性能;
讲过这三个方法的区别,通过程序来区别一下这三个类的性能,到底哪个性能更好些。
public class test { private static int longCount = 50000; public static void main(String[] args) { testString(); testStringBuffer(); testStringBuilder(); test1String(); test2String(); } public static void testString () { String s=""; long begin = System.currentTimeMillis(); for(int i=0; i<longCount; i++){ s += "java"; } long over = System.currentTimeMillis(); System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒"); } public static void testStringBuffer () { StringBuffer sb = new StringBuffer(); long begin = System.currentTimeMillis(); for(int i=0; i<longCount; i++){ sb.append("java"); } long over = System.currentTimeMillis(); System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒"); } public static void testStringBuilder () { StringBuilder sb = new StringBuilder(); long begin = System.currentTimeMillis(); for(int i=0; i<longCount; i++){ sb.append("java"); } long over = System.currentTimeMillis(); System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒"); } public static void test1String () { long begin = System.currentTimeMillis(); for(int i=0; i<longCount; i++){ String s = "I"+"love"+"java"; } long over = System.currentTimeMillis(); System.out.println("字符串直接相加操作:"+(over-begin)+"毫秒"); } public static void test2String () { String s1 ="I"; String s2 = "love"; String s3 = "java"; long begin = System.currentTimeMillis(); for(int i=0; i<longCount; i++){ String s = s1+s2+s3; } long over = System.currentTimeMillis(); System.out.println("字符串间接相加操作:"+(over-begin)+"毫秒"); } }
运行结果为:
操作java.lang.String类型使用的时间为:3149毫秒 操作java.lang.StringBuffer类型使用的时间为:3毫秒 操作java.lang.StringBuilder类型使用的时间为:2毫秒 字符串直接相加操作:1毫秒 字符串间接相加操作:8毫秒
下面对上面的执行结果进行一般性的解释:
1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。
2)String、StringBuilder、StringBuffer三者的执行效率:
StringBuilder > StringBuffer > String
当然这个是相对的,不一定在所有情况下都是这样。
比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。
因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。