首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Java】为什么说Java的String是不可变的?

【Java】为什么说Java的String是不可变的?

原创
作者头像
胖宝宝628
修改2025-07-04 00:05:56
修改2025-07-04 00:05:56
3580
举报

我们都知道,在Java中,String是一个引用类型,我们常说的String不可变,其实指的是字符串对象的内容不可变,也就是其内存中的内容不能被修改,我们平时修改的只是对这块内存的引用,而普通的对象(比如User)修改后,内存中的内容是可以变化的。

代码语言:javascript
复制
String s = "abc";
s = "123";						// 这里只是修改了引用,实际上创建了两个对象

那么,String内容的不可变具体是如何实现的呢?

截取了部分源码,可以看出String本身是基于一个字符类型的数组实现的(JDK8之前是char,JDK9后是byte类型),其中有两个final关键字,一个修饰了String类本身,一个修饰了字符数组。

我们都知道,final用来修饰引用类型时,表示引用的指向不变,也就是说byte不能再指向另外一个数组。

代码语言:javascript
复制
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    @Stable
    private final byte[] value;

说到这里,其实也不能解释为什么内容是不可变的,因为Java 数组是对象,哪怕它被 final 修饰,也只是表示引用不变,但数组里的元素还是可以改的。

那么在 String 中,是如何做到连数组内容也不允许修改的?有以下几个机制:

一、从外部来说,value 是 private final 修饰的

private保证外部根本访问不到这个字段,而且没有任何的get方法,外部拿不到这个数组,也就没有修改的机会

二、从内部来说,String不提供任何写方法

String不提供任何set方法,没有任何可以修改内部属性的方式,一但创建后其内容也永远不可变

三、 构造时使用防御性拷贝(defensive copy)

在构造方法中传入内容时,不是直接使用this赋值给内部字段,而是先把内容拷贝一份,避免外部篡改原数组

代码语言:javascript
复制
public String(char[] value) {
    this.value = Arrays.copyOf(value, value.length);
}

不使用防御性拷贝的后果:

代码语言:javascript
复制
public class ImmutableStudent {
    private final List<String> courses;

    public ImmutableStudent(List<String> courses) {
        // 错误示范:直接this赋值,没有防御性拷贝
        this.courses = courses;
    }

    public List<String> getCourses() {
        return courses;
    }

    @Override
    public String toString(){
        return courses.toString();
    }
}

为对象传参:

代码语言:javascript
复制
public class TestImmutable {
    public static void main(String[] args) {


        List<String> list = new ArrayList<>();
        list.add("数学");
        list.add("语文");
        list.add("英语");

        ImmutableStudent immutableStudent = new ImmutableStudent(list);

        System.out.println(immutableStudent.toString());

        list.add("物理");						// 外部进行修改

        System.out.println(immutableStudent.toString());

    }
}

输出:[数学, 语文, 英语]
    [数学, 语文, 英语, 物理]

可以看出两次打印的结果不一致,原因是外部通过原始的引用修改了对象的内容,导致共享副本的风险

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档