ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

String 不可变的好处和坏处

2022-04-24 23:03:26  阅读:192  来源: 互联网

标签:String 坏处 char 好处 线程 数组 字符串 sb


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,坏处,char,好处,线程,数组,字符串,sb
来源: https://www.cnblogs.com/zou-ma/p/16188158.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有