目录
1、String、StringBuffer与StringBuilder类关系
2、String、StringBuffer与StringBuilder的异同
5、String s = new String("abc");创建了几个对象?
【前言】
本篇博客将为大家介绍一下String、StringBuffer与StringBuilder的应用和它们之间的区别。
【内容】
1、String、StringBuffer与StringBuilder类关系
CharSequence
是java.lang包下的一个接口,此接口对多种不同的char访问的统一接口,像String、StringBuffer、StringBuilder类都是CharSequence的子接口;
CharSequence类和String类都可以定义字符串,但是String定义的字符串只能读,CharSequence定义的字符串是可读可写的;
对于抽象类或者接口来说不可以直接使用new的方式创建对象,但是可以直接给它赋值;
Appendable
表示实现此接口的子类可以对字符或字符串进行追加操作,StringBuffer和StringBuilder都实现了这个接口,它们可以进行字符序列的追加操作 ,而String没有实现此接口,因此它不能进行拼接操作,也就不能改变内容。
此接口并没有规定多线程访问是否安全,要在子类中规定
2、String、StringBuffer与StringBuilder的异同
String
具有不可变性,它被声明为final class,所有属性也都是fina的。
它的拼接等方法都会产生新的String对象,然而并没有改变原来的String对象。
若对字符串频繁进行拼接操作,则对应用性能有明显影响。
因此,StringBuffer可以解决此问题。
StringBuffer
为解决String类拼接产生太多中间对象的问题而提供的一个类。
可以用append或者add方法,把字符串添加到末尾或指定位置。
是一个线程安全的可修改的字符序列。
虽然保证了线程安全,但同时也带来了额外的性能开销。
StringBuilder
能力和StringBuffer没有本质区别。
通过去掉线程安全的部分,有效减小了开销。
在不需要线程安全的情况下,使用StringBuilder进行字符串拼接的首选。
三者底层均采用char[]存储
3、String为什么是不可变的?
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
String类中使用字符数组保存字符串,有final修饰符,所以可以知道string对象是不可变的。
为了能更清楚理解String的不可变性,用一个例子来说明可能存在的误区:
String s = "abcde"; //假设s指向地址0x0001
System.out.println("s = " + s);
s = "123456"; //重新赋值后,s指向地址0x0002,但0x0001地址中保存的值依然存在
System.out.println("s = " + s);
说明:s=“123456”执行后,s指向了新的地址
打印结果
s=abcde
s=123456
解析
String s = "abcde"执行之后
s = "123456"执行之后
因此,String的操作都是改变赋值地址,而不是改变值的操作。
但,如何保证s的引用地址不变,最终将输出的值变化呢?题目是这样的:
这个题目让你想到了什么?... 反射 本次题目使用反射可以解决。
public static void main(String[] args) {
String s = new String("abc");
// 在这中间可以添加N⾏代码,但必须保证s引⽤的指向不变,最终将输出变成abcd
System.out.println("修改前s的内存地址" + System.identityHashCode(s));
//获取String类的value字段
Field value = s.getClass().getDeclaredField("value");
//改变value属性的访问权限
value.setAccessible(true);
value.set(s, "abcd".toCharArray());
System.out.println("修改后s的内存地址" + System.identityHashCode(s));
System.out.println(s);
}
打印结果
可见s字符串序列已经被改变了,但是str的内存地址没有改变。
4、为什么将String类型设计成不可变?
主要基于安全性和效率两点考虑。
安全性
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。字符串本身是线程安全的。
String被许多Java类用来当做参数,比如网络连接地址URl、反射机制所需要的的String参数,如果String不是固定不变的,将会引起各种安全隐患。
效率
字符串不变性保证了hash码的唯一性,不必每次都取计算新的哈希码。
只有字符串是不可变的,常量池才得以实现。常量池是java堆内存中一个特殊的存储区域,当创建一个String对象,加入此字符串值已经存在与常量池中,则不会创建一个新的对象,而是引用已经存在的对象。
5、String s = new String("abc");创建了几个对象?
首先要看常量池里是否有“abc”这个字符串,如果有,则创建一个,如果没有,则创建两个。