String、StringBuilder以及StringBuffer详解 以及其常考面试题和答案解析

1.String、StringBuilder以及StringBuffer区别
(1).String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的
例子:

public static void main(String[] args) {
		String a = "123";
		System.out.println("String:"+a.hashCode());
        a += "456";
        System.out.println("String:"+a.hashCode());
      
		
	}

在这里插入图片描述
如图可看到同样一个对象,String类型重新赋值过后的hashcode不一样,证明他们不是同一个对象。java中对String类型操作是一个不断创建新对象并回收旧对象的过程,所以这个过程很慢,所以设计到字符串拼接,更改的操作都是使用StringBuilder或者StringBuffer。
(2).StringBuilder和StringBuffer的区别:
StringBuilder线程不安全,StringBuffer线程安全
StringBuffer之所以线程安全加了synchronized关键字
在这里插入图片描述
所以3者运行速度StringBuilder >StringBuffer >String(因为加了synchronized关键字StringBuffer速度会稍慢一些

额外补充:

 public static void main(String[] args) {
        String str1 = "hello world";
        String str2 = "hello world";
        String str3 = "hello world";
        String str4 = new String("hello world");
        String str5 = new String("hello world");
        String str6 = new String("hello world");
         
            System.out.println(str1==str4);
	        System.out.println(str1==str2);
	        System.out.println(str4==str5);
    }

在这里插入图片描述
第一个false的原因str1一个是字符串常量,它处于字符串常量池,在讲之前先了解下字符串常量池在哪里。
首先了解一下

存储的是对象,每个对象都包含一个与之对应的class

JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定

每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)

每个栈中的数据(原始类型和对象引用)都是私有的

方法区

静态区,跟堆一样,被所有的线程共享

方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量

字符串常量池则存在于方法区
这张图可以很好的理解
在这里插入图片描述
所以str1它是整个程序中永远唯一的元素和str4 是两个不同的对象
第二个为true的原因str1和str2不是通过new出来的对象所以它存在与常量池中,常量池中如果是相同的字符串,他都会返回同一个对象。
第三个为false的原因str4和str5都是new出来的对象存在于堆中,堆中一个new代表一个唯一对象,所以他两是不同的对象

面试题:

String str4 = new String(“abc”) 创建多少个对象?

1.在常量池中查找是否有“abc”对象

(1).有则返回对应的引用实例

(2).没有则创建对应的实例对象

2.在堆中 new 一个 String(“abc”) 对象

3.将对象地址赋值给str4,创建一个引用

所以,常量池中没有“abc”字面量则创建两个对象,否则创建一个对象,以及创建一个引用

面试题:

String a = "hello2";   
String b = "hello" + 2;  
System.out.println((a == b));

输出结果为:true。原因很简单,“hello”+2在编译期间就已经被优化成"hello2",因此在运行期间,变量a和变量b指向的是同一个对象。
  
面试题:

 String a = "hello2";    
 String b = "hello";      
  String c = b + 2;       
 System.out.println((a == c));

输出结果为:false。由于有符号引用的存在,所以 String c = b + 2;不会在编译期间被优化,不会把b+2当做字面常量来处理的,因此这种方式生成的对象事实上是保存在堆上的。因此a和c指向的并不是同一个对象。

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替 比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

面试题:

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;

面试题:

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指向的不是同一个对象。

借鉴博客:https://www.cnblogs.com/dolphin0520/p/3778589.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值