String方面的面试事项

马上就要面临面试,最近也在准备些面试的东西,不总结总结,总归不得劲。理解的不深,文笔也不好,算是写给自己看看吧。

今个说说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();
for(int i=-;i<10000;i++)
{
sb.append("hello");
}

前后加上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没有任何影响;


好吧,先说这么多了。不知道过些日子回头看看什么样,有人看到错误,不同见解请指出,笔者正在学习路上行走。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值