String StringBuffer StringBuilder详细分析和面试题 - Go语言中文社区

String StringBuffer StringBuilder详细分析和面试题


转自:
作者:海子
出处:http://www.cnblogs.com/dolphin0520/
对其中一些地方加入了自己的思考和整理,供自己和同学们学习参考。

要说Java中什么哪个类用的最频繁,要数String类了。关于String 、StringBuffer、StringBuilder是有必要详细研究一下的。

从String类源码中了解String

在这里插入图片描述
根据源码,可以得到:

  1. String类是final类,表示终态类,不可继承,不可修改。早期,被final修饰的方法会被转化为内嵌调用以提升效率,Java SE5/6以后,该方式被摒弃。故,这样定义的原因是不想让该方法被覆盖。
  2. String类是通过char数组保存字符串。
    再看看substring()方法和concat()方法:
    concat源码
    substring源码
    对String对象的任何改变都不会影响原有对象,任何改变对象的操作都会生成一个新的对象,这一点从成员方法value用final修饰,可以看出。

StringBuffer

由于源码冗长,不截图展示了,有兴趣的同学自己去查看,我是查看方法。
StringBuffer类也被定义成final类,与String类相同。但是StringBuffer类对象的字符序列是可修改的,类本身写好了方法改变序列类容和长度。并且StringBuffer是线程安全的,类的各个方法都用synchronized修饰。可以这样理解StringBuffer类:可修改的String,并且线程安全。(线程安全带来的缺点是效率相对StringBuilder而言更低)

StringBuilder

JDK官方文档里是这样说的:一个可变的字符序列。 此类提供与StringBuffer的API,但不保证同步。 此类设计用作简易替换为StringBuffer在正在使用由单个线程字符串缓冲区的地方(如通常是这种情况)。 在可能的情况下,建议使用这个类别优先于StringBuffer ,因为它在大多数实现中将更快。 简单来说:StringBuilder是StringBuffer的特殊优化版本,不考虑线程安全,效率最高。

JVM的主动优化

public class solution{
	public static void main(String[] args){
		String str = "hello";
		for(int i=0;i<10000;i++){
			str+= "world";
		}
	}
}

str+= "world"会被JVM优化成:

	StringBuilder str = new StringBuilder(string);
	str.append("hello");
	str.toString();

性能总结

  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。

常见面试题

  1. 下面这段代码的输出结果是什么?
   String a = "hello2"; 
   String b = "hello" + 2;   
   System.out.println((a == b));

答:true。为什么呢?因为"hello" + 2在编译期间被优化成了"hello2",故运行期间a、b指向的是保存在常量池中的同一对象。

  1. 下面这段代码的输出结果是什么?
	String a = "hello2"; 
	String b = "hello"; 
	String c = b + 2;
	System.out.println((a == c));

答:false。由于有引用符号的存在,所有b + 2不会再编译期间被优化,不会吧b + 2当做字面常亮来处理,这种生成对象是保存在堆上。而a的对象是在常量池中。故a和c指向的并不是同一个对象。

  1. 下面这段代码的输出结果是什么?
	String a = "hello2";
	final String b = "hello";   
	String c = b + 2;       
	System.out.println((a == c));

答:true。对于被final修饰的变量,会在class文件常量池中保存一个副本,也就是说不会通过连接而进行访问,对final变量的访问在编译期间都会直接被替代为真实的值。那么String c = b + 2;在编译期间就会被优化成:String c = “hello” + 2。

  1. 下面这段代码输出结果为:
public class Main {
    public static void main(String[] args) {
        String a = "hello2";
        final String b = getHello();
        String c = b + 2;
        System.out.println((a == c));
    }
     
    public static String getHello() {
        return "hello";
    }
}

答:false。这里面虽然将b用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此a和c指向的不是同一个对象。

  1. 下面这段代码的输出结果是什么?
public class Main {
    public static void main(String[] args) {
        String a = "hello";
        String b =  new String("hello");
        String c =  new String("hello");
        String d = b.intern();
         
        System.out.println(a==b);
        System.out.println(b==c);
        System.out.println(b==d);
        System.out.println(a==d);
    }
}

答:false false false true。在String类中,intern方法(intern详解)是一个本地方法,在JAVA SE6之前,intern方法会在运行时常量池中查找是否存在内容相同的字符串,如果存在则返回指向该字符串的引用,如果不存在,则会将该字符串入池,并返回一个指向该字符串的引用。因此,a和d指向的是同一个对象。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_35713860/article/details/82766350
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-27 11:44:21
  • 阅读 ( 2503 )
  • 分类:面试题

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢