区分java中String+String和String+char
我们来考虑一个关于java中String的问题:"abc"+'/'和"abc"+"/"的区别.通过这个例子,我们可以顺便练习一下JDK工具中javap的用法,原问题是这样的:
把斜杠/当作字符或字符串有什么区别呢?
一个是当作基本数据类型char,一个是对象String。具体有什么区别呢?
当作字符效率会更高吗?
Stringstr="abc"+'/';
和
Stringstr="abc"+"/";
1、编译器优化
首先大家应该知道,上面那两句效果是一样的,因为编译器会把上面那两句都优化成下面的样子:
Stringstr="abc/";
我们可以通过javap证明这一点.关于javap,可以参考《每个Java开发者都应该知道的5个JDK工具》.我们首先创建一个类:StringOne,在主方法中填入下面的代码:
StringOne.java Stringstr1="abc"+'/'; Stringstr2="abc"+"/"; System.out.println(str1==str2);
编译并运行,输出结果为true.接下来,该我们的javap登场了,在命令行中输入下面的命令:
javap-v-lStringOne.class>StringOne.s
然后查看生成的StringOne.s文件.就会发现其中有这么几行
StringOne.s #2=String#20//abc/ ... #20=Utf8abc/ ... 0:ldc#2//Stringabc/ 2:astore_1 3:ldc#2//Stringabc/ 5:astore_2
说明str1和str2都引用字符串"abc\".
2、使用javap分析差异
现在我们换一个问法,下面的代码中stringAddString和stringAddChar方法有什么区别?
StringTwo publicstaticStringstringAddString(Stringstr1,Stringstr2){ returnstr1+str2; } publicstaticStringstringAddChar(Stringstr,charch){ returnstr+ch; }
这次再使用javap进行反编译,生成文件的部分内容如下所示
StringTwo.s publicjava.lang.StringstringAddString(java.lang.String,java.lang.String); descriptor:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; flags:ACC_PUBLIC Code: stack=2,locals=3,args_size=3 0:new#2//classjava/lang/StringBuilder 3:dup 4:invokespecial#3//Methodjava/lang/StringBuilder."<init>":()V 7:aload_1 8:invokevirtual#4//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 11:aload_2 12:invokevirtual#4//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15:invokevirtual#5//Methodjava/lang/StringBuilder.toString:()Ljava/lang/String; 18:areturn publicjava.lang.StringstringAddChar(java.lang.String,char); descriptor:(Ljava/lang/String;C)Ljava/lang/String; flags:ACC_PUBLIC Code: stack=2,locals=3,args_size=3 0:new#2//classjava/lang/StringBuilder 3:dup 4:invokespecial#3//Methodjava/lang/StringBuilder."<init>":()V 7:aload_1 8:invokevirtual#4//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 11:iload_2 12:invokevirtual#6//Methodjava/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder; 15:invokevirtual#5//Methodjava/lang/StringBuilder.toString:()Ljava/lang/String; 18:areturn
现在,我们已经可以很清楚的看出这两个方法执行的流程了:
stringAddString
- 创建一个StringBuilder对象
- 使用append方法,依次将两个参数添加到刚才创建的StringBuilder中.
- 调用toString方法.
- returntoString方法的返回值.
stringAddChar的过程和stringAddString一样,只是在第二次调用append方法时stringAddString的参数是String类型,而stringAddChar的参数是char类型.
3、StringBuilder类的append(char)方法和append(String)方法
这里,我们直接查看源码就好了(我的是jdk1.8.0_60附带的源码)。注意,虽然文档上显示StringBuilder继承自Object,但是从源码来看,它是继承自抽象类AbstractStringBuilder的。而且append方法是由AbstractStringBuilder实现的。
AbstractStringBuilder.java
publicAbstractStringBuilderappend(charc){ ensureCapacityInternal(count+1);//确保数组能够容纳count+1个字符 value[count++]=c; returnthis; } publicAbstractStringBuilderappend(Stringstr){ if(str==null) returnappendNull(); intlen=str.length(); ensureCapacityInternal(count+len); str.getChars(0,len,value,count);//拷贝字符串中的字符数组到本对象的字符数组中 count+=len; returnthis; }
剩下的就不再贴出来了。String.getChars(int,int,char[],int)最终依赖于publicstaticnativevoidarraycopy(Object,int,Object,int,int)。也就是说有可能是C语言写的,在拷贝大型数组时效率应该会比java写的程序好一些。那么,现在说说我的理解:
从直接内存来说,由于String中包含char数组,而数组应该是有长度字段的,同时String类还有一个inthash属性,再加上对象本身会占用额外的内存存储其他信息,所以字符串会多占用一些内存.但是如果字符串非常长,那么这些内存开销差不多就可以忽略了;而如果像"/"这种情况,字符串比较(非常)短,那么就很可能有许多个共享引用来分担这些内存开销,那么多余的内存开销还是可以忽略的.
从调用堆栈上,由于这里String只比char多了一两层函数调用,所以如果不考虑函数调用开销(包括时间和空间),应该差不多;考虑函数调用开销,应该"abc"+'/'更好一些;但是当需要连接若干个字符时(感觉这种情况应该更常见吧?),由于使用char需要循环好多次才能完成连接,调用的函数次数只会比使用String更多.同时拷贝也不会比String直接拷贝一个数组更快.所以这个时候就变成了"abc"+"/"吞吐量更大.
现在感觉这个问题像是在问:读写文件时使用系统调用效率高,还是使用标准函数库中的IO库效率高.个人感觉,虽然标准IO库最后还得调用系统调用,而且这之间会产生一些临时变量,以及更深层次的调用堆栈,但是由于IO库的缓冲等机制,所以IO库的吞吐量会更大,而系统调用的实时性会好一些.同样,虽然String类会多几个字段,有更深层次的函数堆栈,但是由于缓存以及更直接的拷贝,吞吐量应该会更好一些.
新的问题
从上面javap的反编译代码来看,两个String相加,会变成向StringBuilder中append字符串.那么理论上,下面哪段代码的效率好呢?
Stringstr1="abc"+"123";//1 StringBuilderstringBuilder=newStringBuilder();//2 stringBuilder.append("abc"); stringBuilder.append("123"); Stringstr2=stringBuilder.toString();
这个问题就留给大家思考吧!
以上就是本文的全部内容,帮助大家更好的区分java中String+String和String+char,希望对大家的学习有所帮助。