马上就要面临面试,最近也在准备些面试的东西,不总结总结,总归不得劲。理解的不深,文笔也不好,算是写给自己看看吧。
今个说说String,StringBuffer,StringBuilder.
通过api知道,String类是一个final类,不能被继承;类中方法也都为final修饰;类中的substring,replace等方法都是生成一个新的String对象,原对象并没有被改变,其实,对String对象的任何操作都不会改变原本的对象,最多只会生成一个新的对象。
作为新手的一重点:String str1="hello"; 和 String str2=new String(“hello”)的区别:
第一种方式,在jvm中,会有一个字符串常量池专门存放此字符串,而当另一个字符串赋值的时候,虚拟机会先去常量池查看,有没有和将要赋值的常量值相等的常量,如果有,则将新定义的常量指向已有的常量值,如果没有,另开辟一份空间用来放置新定义的常量。即:String str3="hello",str1==str3 为true;
第二种方式,是生成的一个新的对象,保存在堆中。假设定义 String str4=new String("hello"); str4==str2 返回false,因为只会比较两者是否为同一个对象,和本身的值,没有关系。
另一个,StringBuilder,StringBuffer的作用。
为啥要使用Stringbuilder和buffer?
我们可以用下面的例子引出
String string =
""
;
for(int i=0;i<10000;i++){
string+=i;
}
StringBuilder sb=new StringBuilder();
前后加上System.nanoTime()可以看出两者的执行效率区别,显然,StringBuilder效率更高,而且不是几倍的问题。
反编译字节码文件可以看到
5: iload_2
6: sipush 1000
9: if_icmpge 37
12: new #3 // class java/lang/StringBuilder
15: dup
16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
19: aload_1
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: iload_2
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
27: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_1
31: iinc 2, 1
34: goto 5
,string+=i;被jvm优化成了
StringBuilder str = new StringBuilder(string);
str.append(I);
str.toString();
上面提到,String的任何改变都不会改变原本的对象,只会生成新的对象,所以,他本质是把原本的string指向的对象取出来,与I拼接,生成另一个对象,在将string指向新生成的对象。而new StringBuilder是在循环里进行的,也就是要new 1000个对象。
反编译StringBuilder的class文件可以看到。
14: if_icmpge 30
17: aload_1
18: ldc #4 // String hello
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
在1000次循环中没有new新的对象出来,只在循环外定义的一个StringBuilder,循环体中只有append,节省大量效率。
StringBuilder和StringBuffer的区别:
查看源代码,对比,就会发现他俩几乎相同,唯一的区别在于StringBuffer中加入了synchronized 关键字,也就是说StringBuffer是线程安全的。安全和效率一直是程序开发中相斥的两个因素,相对安全意味着相对的效率低。通常情况下,StringBuffer效率低于StringBuilder。而两者效率都高于string的直接相加。
当然,这并不是绝对的,在大量数据的拼接时,两者效率高于String的直接相加,但是数据少时,比如String a="hello"+"world" 和StringBuilder b=new StringBuilder("hello").append("world”)相比。前者效率高些,不信你试试。。
上面两次循环对比的时候,我用了两种,一个是str+=i,另一个是str.append("hello");一个给的是常量,一个给的是变量,那么,常量直接相加和变量相加有什么区别呢?
jvm会自动优化,我们知道。比如,str=“hello”+“world”;str1=“hello”;str2=“world”;str3=str1+str2;
在程序编译的时候,str已经成为了helloworld,而str3还是两个变量的相加,在运行期间才会真正赋值;所以,后者效率要低于前者。
一个问题:str==str3 返回true还是false呢? obviously,false;
因为“==”只会比较左右两边是否是同一个对象,并不会关心他俩的地址是否相同,值是否相同。str是在常量池放置的一个对象,而str3因为引用了另一个对象,会在运行时,在堆里创建对象,即使这俩的hashcode和值都相等,但是并不是同一个对象。
另一题:
public static void main(String[] args) { StringBuffer s1=new StringBuffer("Hello"); StringBuffer s2=new StringBuffer("Hello"); System.out.println("s1方法之前:"+s1.hashCode()+" s2: "+s2.hashCode()); changestring(s1,s2); System.out.println("s1: "+s1); System.out.println("s2: "+s2); } private static void changestring(StringBuffer ss1, StringBuffer ss2) { System.out.println("ss1方法赋值之前:"+ss1.hashCode()+" ss2: "+ss2.hashCode()); System.out.println("ss1:"+ss1+" ss2: "+ss2); ss1.append("world"); ss2=ss1; System.out.println("ss1方法赋值之后:"+ss1.hashCode()+" ss22222: "+ss2.hashCode()); System.out.println("ss11111:"+ss1+" ss22222: "+ss2); }
怎么输出?
s1方法之前:356573597 s2: 1735600054
ss1方法赋值之前:356573597 ss2: 1735600054
ss1:Hello ss2: Hello
ss1方法赋值之后:356573597 ss22222: 356573597
ss11111:Helloworld ss22222: Helloworld
s1: Helloworld
s2: Hello
重点在于:s1可以理解,但ss2的地址值变为ss1的,值也和s1相等,而s2没有改变
为啥?
在调用changestring的时候,传递的参数是两个对象的引用,也就是地址值,ss1.append()会改变ss1指向的字符串的值,相应的也就是改变了s1指向的地址的值。
ss2=ss1这一步,改变了ss2指向的地址,相当于一个新的对象,而s2指向的地址没有改变,所以对s2没有任何影响;
好吧,先说这么多了。不知道过些日子回头看看什么样,有人看到错误,不同见解请指出,笔者正在学习路上行走。。