String、StringBulid、StringBuffer

1.String

1.1 String不变性原理

我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。

String 真正不可变有下面几点原因:

  • 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
  • String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

String类源码:
在这里插入图片描述

1.2 String对象和常量池

        String s1 = "Aismy";              // String 直接创建(常量池)
        String s2 = "Aismy";              // String 直接创建(常量池)
        String s3 = s1;                    // 相同引用
        String s4 = new String("Aismy");   // String 对象创建 
        String s5 = new String("Aismy");   // String 对象创建
        System.out.println(s4==s5);  //比较的是引用的地址
        System.out.println(s1==s2);  //s1和s2指向同一地址,因此相等
        System.out.println(s4.equals(s5));  //因为String重写equals方法,因此比较的是两个对象的值是否相等

回顾==与equls

  • 对于基本变量比较的是值是否相等
  • 对于引用对象比较的是地址是否相等
  • 在String中重写了equls,若不重写则和==效果一样;

2.StringBuffer 和 StringBuilder 类

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。

和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

2.1StringBuffer构造器:

构造一个字符串缓冲区,其中没有字符,初始容量为 16 个字符
public StringBuffer() {
super(16);
}
构造一个没有字符且具有指定初始容量的字符串缓冲区。
参数:
容量——初始容量
public StringBuffer(int capacity) {
super(capacity);
}
构造一个字符串缓冲区,初始化为指定字符串的内容。字符串缓冲区的初始容量是16加上字符串参数的长度。
参数:
str - 缓冲区的初始内容
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
构造一个字符串缓冲区,其中包含与指定CharSequence相同的字符。字符串缓冲区的初始容量是16加上CharSequence参数的长度。
如果指定的CharSequence的长度小于或等于 0,则返回容量为16的空缓冲区。
参数:
seq - 要复制的序列。
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
2.2 StringBuffer 线程安全原因

StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

  @Override
        public synchronized StringBuffer append(Object obj) {
            toStringCache = null;
            super.append(String.valueOf(obj));
            return this;
        }

3.总结

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

  • 操作少量的数据: 适用 String
  • 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

4.相关面试题

4.1字符串拼接用“+” 还是 StringBuilder?

Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的元素符。

String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;

对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。
不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。

String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {
    s += arr[i];
}
System.out.println(s);

StringBuilder 对象是在循环内部被创建的,这意味着每循环一次就会创建一个 StringBuilder 对象。

因此在循环中拼接字符串使用StringBuilder 会避免这个问题

4.2String#equals() 和 Object#equals() 有何区别?

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Object 的 equals 方法是比较的对象的内存地址。

4.3字符串常量池的作用了解吗?

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
JDK1.7 之前运行时常量池逻辑包含字符串常量池存放在方法区。JDK1.7 的时候,字符串常量池被从方法区拿到了堆中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值