关于Java中的Mutable 和 Immutable类型

概念
immutable(不可变的):一旦创建,就不能更改它的值/引用。
mutable(可变的):创建之后,该对象拥有可以更改其值/引用的方法,可以利用这些方法改变。
需要注意的一点是:
改变一个变量的引用:将该变量指向另一个值的存储空间
改变一个变量内的值:将该变量当前指向的值的存储空间中写入一个新的值。

实例
String
String类型是immutable的,一旦创建,不能更改它指向的值,只能改变它的引用

String s = "a";
s = s.concat("b"); // s+="b" and s=s+"b" also mean the same thing

这段代码的内存,用快照图的方式可以画成这样:
在这里插入图片描述
也就是说,如果初始化了一个String类型的对象,再去给它加上一段字符,则会更改它指向的地址,而不是更改地址里的值。

StringBuilder
StringBuilder类型是mutable的,创建成功后,可以利用一些方法来改变它指向地址的值。

StringBuilder sb = new StringBuilder("a");
sb.append("b");

在这里插入图片描述
可以发现,sb指向的内存地址没变,只是地址内的内容改变了。

虽然看起来,两种方式并没有什么区别,但是,参考如下代码:

String t = s;
t = t + "c";

StringBuilder tb = sb;
tb.append("c");

可以发现,当有变量t指向s,tb指向sb时,问题就呈现出来了,当改变t的值时,s的值并没有改变;但当改变tb的值时,sb的值跟着改变,这就会造成一些可怕的后果。

可变类型的必要性
大家可能会想,既然可变类型会产生一些意外的后果,而且我们已经有了String类型,那么要StringBuilder类型做什么呢?参考下面代码:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + i;
}

如果s被设为String类型,那么,每次在s后面接字符串的时候,都会将之前s中的内容复制一份再加上n,然后放到另一块内存区域,由于要更改s的指向,所以每次都需要复制之前的字符串,“0”被复制了n次,这样本来增加了n个字符,结果却需要O(n^2);
然而使用StringBuilder就可以直接在原字符串后增加,效率提高了很多,当需要转成字符串时,只需利用toString()方法。

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(i));
}
String s = sb.toString();

可变类型的危险

参数发生变化
首先写一个sum函数,计算参数列表中元素之和:

/** @return the sum of the numbers in the list */
public static int sum(List<Integer> list) {
    int sum = 0;
    for (int x : list)
        sum += x;
    return sum;
}

然后再写一个计算列表元素绝对值之和:

/** @return the sum of the absolute values of the numbers in the list */
public static int sumAbsolute(List<Integer> list) {
    // let's reuse sum(), because DRY, so first we take absolute values
    for (int i = 0; i < list.size(); ++i)
        list.set(i, Math.abs(list.get(i)));
    return sum(list);
}

然后查看下面函数的输出:

public static void main(String[] args) {
    // ...
    List<Integer> myData = Arrays.asList(-5, -3, -2);
    System.out.println(sumAbsolute(myData));
    System.out.println(sum(myData));
}

会发现,输出的都是10,而不是我们预期的10的-10,可见,可变数据类型发生了参数的更改,这在我们写程序的时候会发生致命的问题。

返回值发生变化
另外,如果使用mutable,不仅函数的参数会发生改变,函数返回值也会发生变化,这个时候,为了不让客户端程序员更改,我们可以使用防御性复制的方法,即返回复制的副本。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值