String为什么要设计成不可变的!

String 对象是不可变的,字符串是常量,不是变量。我们来分析下为什么Java中String要设计成不可变的。

首先我们来看一段代码:

public class Test {
    public static void main(String []args)
    {
        String s1 = "abcdef";
        System.out.println(s1);
        s1 = "123456";
        System.out.println(s1);
    }
}

打印结果:
在这里插入图片描述
结果不是变了吗? 为啥还说String是不可变的呢?
s1 仅仅是一个对象引用,并不是对象的本身。对象是在内存中的一块内存区域,而s1是指向这块区域的一个引用。创建对象s1时,s1指向内存中的”abcdef“,当执行了 s1 = "123456"后,s1又指向内存中的”123456“,而”abcdef“并没有改变,还在内存中存在。

String 本身提供了一个replace()方法,不是可以改变内容吗?

public class Test {
    public static void main(String []args)
    {
        String s1 = "abcdef";
        System.out.println(s1);
        s1.replace("a","A");
        System.out.println(s1);
    }
}

结果:
在这里插入图片描述
字符串并没改变。
改变下写法:

public class Test {
    public static void main(String []args)
    {
        String s1 = "abcdef";
        System.out.println(s1);
        s1 = s1.replace("a","A");
        System.out.println(s1);
    }
}

结果:
在这里插入图片描述
这样的话,数值就会改变了。s1.replace(“a”,“A”) 会在内存中产生一个新的对象”Abcdef“,s1 = s1.replace(“a”,“A”)执行后,s1指向内存中这块新区域,所以内容改变了,原来的”abcdef“并没有改变,还保存在内存中。

String 是如何实现不可变的呢?

首先看下成员变量:

/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0
  1. value数组是用来存放字符的,hash用来存放该对象的哈希值。
  2. value 对象被private final 修饰,hash 被private修饰,String类并没有对外提供value和hash的set方法,所以外部无法修改。并且通过final关键字保证只要初始话数值了,就无法在改变了。

所以认为String对象是不可改变的。但是value本身又是一个引用,它指向存放真正数据的数组对象。

不可变对象的好处
  1. 不可变对象更容易构造,测试与使用;
  2. 真正不可变对象都是线程安全的;
  3. 不可变对象的使用没有副作用(没有保护性拷贝);
  4. 对象变化的问题得到了避免;
  5. 不可变对象的失败都是原子性的;
  6. 不可变对象更容易缓存,且可以避免null引用;
  7. 不可变对象可以避免时间上的耦合;

String,StringBuffer,StringBuilder,都是final类,不允许被继承,在本质上都是字符数组。不同的是String长度不可变,其他两个在添加字符串时可以增加。String在每次拼接时都会返回一个新的实例,StringBuffer和StringBuilder 通过append添加数据是在原对象添加。所以在进行大量字符串操作时,不推荐使用String
StringBuffer是线程安全的,因为它在append方法前增加synchronized修饰符,起同步作用,但是效率比StringBuilder 低。

String的这种不可变,我们可以通过其他方法突破的

前面我们提到String类是通过char[] value 来存储的。value本身就是一个引用,虽然不能改变它的引用地址,我们可以修改真正数组数据。因为value是私有,我们通过反射来处理。

public class Test {
    public static void main(String []args)
    {
        try {
            String s1 = "abcdef";
            System.out.println(s1);
            Field valueField = null;
            valueField = s1.getClass().getDeclaredField("value");
            valueField.setAccessible(true);

            char[] value = (char[]) valueField.get(s1);
            value[0] = '1';
            value[2] = '2';
            value[4] = '3';
            System.out.println("s = " + s1);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值