Java教程

String 不可变的好处和坏处

本文主要是介绍String 不可变的好处和坏处,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

String 为什么不可变,有什么好处?

字符串,顾名思义就是一串连续的字符,也就是一串连续的 char

C 语言中就是用 char 数组来表示字符串,

java的一切皆对象的世界中,将char数组进行了封装,

String 类型来表达字符串,点开 String 的源码,就会发现char数组的身影。

// Java 8 源码
public final class string{
    private final char value[];
}

char 数组被 final 关键字修饰,并且是私有成员变量,为什么 java 要这样设计呢?

这就要说到 String 的不可变性。

// Person 是可变的
Person p = new Person(18);
p.setAge(20);

// String 是不可变的
String s = "abc";

一个对象创建后,如果可以修改对象的属性,我们会说这个对象是可变的,反之则是不可变的。

为什么 String 不可变呢?

首先,它是被 fianl 修饰,代表它不可指向新数组,(但不代表数组本身的数据不能被改变)并且不能被继承。

真正不可变的原因是 它还被 private 修饰了,并且 String 提供和暴露任何修改字符数组的方法,都是返回新的 String 对象。

获取其底层字符数组时,都是获取一个新的数组进行返回,原数组,不会收到影响。

这样设计有什么好处呢?

首先,只有String 不可变了,字符串常量池才能发挥作用,

用字面量创建字符串时,字符串常量池会返回,已有对象的引用,

如果字符串可变,引用的值就可以被修改,这样还谈何复用呢?如何节省资源呢?

String 不可变,可以保证它的哈希码也不会被改变。

public final class String{
    private int hash; // Default to 0
    private int hash;
    
    public int hashCode() {
        int h = hash;
        if(h = 0;&& value.length > 0){
            char val[] = value;
             for(int i = 0;i < value.length; i++){
                 h = 31 * h + val[i];
             }
            // 计算一次后即可将哈希码存储
            hash = h;
        }
        return h;
    }
}

就可以将其缓存,再用到时,就不用在计算了。

也正是因为 哈希码 不会变,可以放心的去使用,和哈希计算相关的对象

比如 hashMap 、HashSet,如果 哈希码会改变,那就会影响这些对象的哈希计算,从而导致预期之外的结果。

比如你之前明明存储了一个String 对象。到后面你会发现找不到了。

最后,不可变对象都是线程安全的。

不必担心,当前线程使用的对象,会被其它线程修改。

String 不可变的弊端

​ 正是因为String不可变,所以导致添加新对象时,会创建一个新对象,从而导致性能低下。

for(int i = 0;i<9999;++i){
    string+=i;  // 大量创建新对象
}
System.out.print(string);

​ 为了解决这一问题,java 提供了StringBuilder 这个可变的字符串类型,我们简称它 为 sb ,小 sb继承了一个 老 sb (AbstractStringBuilder) 其底层和String一样都是用的 char 数组来表示字符串,不过老sb char 数组,没有加 private final 修饰符,代表这个数组可被访问且可变。

abstract class AbstractStringBuilder{
    char[] value;
}
public final class StringBuilder extends AbstractStringBuilder{
    
}

​ 而且 sb父子俩还提供了许多方法供我们修改字符串,比如append方法就可以原字符串后面添加字符,这些方法修改的都是自身数据,返回的 sb 对象,

​ 也就是它自己本身,不像String一样返回的都是新对象。

​ 这样一来我们频繁修改字符串就方便了许多。

所以在拼接字符串的时候,谨慎使用 String,优先选择 sb

sb 的缺点:它是一个可变对象,那它就是线程不安全的。

为了解决这个问题 java 还推出了 sb 的兄弟,StringBuffer

它和 sb 不一样的是,它内部使用了 synchronized 关键字,修饰了字符串操作方法,从而保证了线程安全。

有得必有舍,正是由于它每次操作字符串时都会加锁,这回导致它性能没有 sb 高,但是比String 还是要高的。

类型 特点 适用场景
String 不可变,线程安全 操作少量数据,或不需要操作数据
StringBuilder 可变,线程不安全 需要频繁操作数据,但是不用考虑线程安全
StringBuffer 可变,线程安全,性能较低 需要频繁操作数据,要考虑线程安全

StringJoiner 拼接字符串

String[] names = {"A","B","C","D"};
StringJoiner sj = new StringJoiner(",","[","]");  // 设置分隔符,要想设置开头和结尾,在构造方法后面添加两个参数即可
for(String name:names){
    sj.add(name);
}
System.out.println(sj);  // [A,B,C,D]




java 标准库中的一些拼接操作,其实就用到了,StringJoiner

比如 String 的静态方法, join,以及Stream 流中常用的 joining

String[] names = {"A","B","C","D"};

String.join(",",names);

Arrays.stream(names).collect(Collectors.joining(","));
这篇关于String 不可变的好处和坏处的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!