【软件构造】Mutable类型与Immutable类型


1.前言

在软件构造这门课中,对mutable类型和immutable类型的深入理解,有助于后续ADT、可维护性、可复用性的学习,因此我们有必要对其进行详细的分析说明。

我们首先明确的是,mutable类型和immutable类型均属于ADT的范围,二者关系如下图:


2.概念

immutable类:类的实例创建后成员变量值不变,若修改后,引用会指向一个实例对象。

mutable类:类的实例创建后可以通过类的方法就地修改值。


3.常见immutable类与mutable类

常见immutable类:String类;基本数据类型与其封装数据类型,如int、char、Interger、Boolean;Scanner类;经过 Collections.unmodifiableList/Map/Set() 方法处理后的集合。

常见mutable类:StringBuilder、StringBuffer、Map类、Collection类。


4.代码实践

考虑如下代码:

String str=new String("123");
str.concat("4");
System.out.println(str);

输出结果为:

为什么结果不是“1234”呢?

我们知道,java数据类型分为基本数据类型和对象数据类型(引用类型),后者类型的对象会按引用传递,这个引用,本质上是一个指针,指向存储在堆里的对象实体。所以,对于这样的变量,有着直接修改被指向的数据值让引用重新指向一个新对象两种方式。

而对于immutable类型,一旦该类初始化为一个新对象,其指向的堆中的值不可以修改,除非让其指向新的堆位置。所以,上述contact()会使用str引用指向的值重新创建一个新的对象,而不是修改str指向的对象的值。

其相应代码快照图为:

再考虑如下代码:

StringBuffer strbuf1= new StringBuffer("123");
StringBuffer strbuf2=strbuf1;
strbuf1.append("4");
System.out.println(strbuf1);
System.out.println(strbuf2);

输出为:

这里Stringbuffer为mutable类型,调用其成员方法append时,可以在引用所指向的堆中直接修改值,故输出均为“1234”。

其代码快照图为:

对于mutable类型的对象,若有多个引用,其中某一个引用对对象的值修改时,由于所有引用指向同一个对象,所以在其他引用的值被“偷偷地改变了”,而这种改变,往往是被忽略的,因此会有潜在的危险性。

比如如下代码:

strbuf2.append("5");
System.out.println(strbuf1);

在输出strbuf2时,输出结果也是“12345”。

此外在函数调用时,对于mutable类也会出现非法篡改的情况:

 1     public static StringBuilder addstr(StringBuilder p){
2 p.append("d");
3 return p;
4 }
5 public static void main(String[] args) {
6 StringBuilder str=new StringBuilder("abc");
7 System.out.println(str);
8 addstr(str);
9 System.out.println(str);
10 }

其输出为:


5.针对immutable类非法篡改的解决方案

方案一:defensive copy

在传参之前,或者在函数体内修改传入参数之前,拷贝一个新的对象

 1     public static StringBuilder addstr(StringBuilder p){
2 StringBuilder copy=new StringBuilder(p);
3 copy.append("d");
4 return copy;
5 }
6 public static void main(String[] args) {
7 StringBuilder str=new StringBuilder("abc");
8 System.out.println(str);
9 StringBuilder str1=addstr(str);
10 System.out.println(str);
11 System.out.println(str1);
12 }

输出为:

abc
abc
abcd

方案二:使用相应的immutable类替换mutable类的引用

 1     public static String addstr(String p){
2 return p+"de";
3 }
4 public static void main(String[] args) {
5 String str=new String("abc");
6 System.out.println(str);
7 String str1=addstr(str);
8 System.out.println(str);
9 System.out.println(str1);
10 }

输出为:

abc
abc
abcde

6.总结

immutable类更加安全,在软件构造过程中同时使用immutable类型的类,保证变量的值始终不变,可以重复使用,但对其修改需要进行大量拷贝,浪费时间与存储空间;

mutable类的修改不会造成空间的浪费,适合作为共享数据使用,但对其修改一定要谨慎进行。


完结~感谢阅读~️️️

【软件构造】Mutable类型与Immutable类型的更多相关文章

  1. apache软件no_ssl和openssl两种类型的区别

    apache软件同一版本有两种类型:no_ssl和openssl: openssl多了个ssl安全认证模式,它的协议是HTTPS而不是HTTP,这就是带有SSL的服务器与一般网页服务器的区别了. 一般 ...

  2. 函数索引引用的函数必须是immutable类型

    用户在使用中,可能会用到基于函数的索引,但是函数是非 immutable 类型的,导致函数索引无法创建.如: test=# create index ind_t1 on t1(to_char(crea ...

  3. 麻省理工18年春软件构造课程阅读02“Java基础”

    本文内容来自MIT_6.031_sp18: Software Construction课程的Readings部分,采用CC BY-SA 4.0协议. 由于我们学校(哈工大)大二软件构造课程的大部分素材 ...

  4. typescript枚举,类型推论,类型兼容性,高级类型,Symbols(学习笔记非干货)

    枚举部分 Enumeration part 使用枚举我们可以定义一些有名字的数字常量. 枚举通过 enum关键字来定义. Using enumerations, we can define some ...

  5. HIT2019春软件构造->Git&Github学习笔记

    由于软件构造课程需要,学习使用git,以下作为学习笔记. 一.Git初始化及仓库创建和操作  1.基本信息设置(设置签名)  命令        项目级别/仓库级别:仅在当前本地库范围内有效 git ...

  6. python的mutable变量与immutable变量

    python的变量分为mutable(可变的)和immutable类型. mutable:dict, list immutable:int , string , float ,tuple..

  7. 面向对象软件构造 (Bertrand Meyer 著)

    Part A: The Issues 议题 第一章 软件品质 第二章 面向对象的标准 Part B: The Road To Object Orientation 通向面向对象之路 第三章 模块性 第 ...

  8. 由软件构造引申的OOP与POP的心得体会

    在大一初学C语言的时候,所解决的问题都是一些轻量级的简单问题,当时写过一个教学管理系统.这个教学管理系统的功能很简单,思想就是“流水线”:按部就班的实现所有流程.要完成整个教学管理系统,实际上就是完成 ...

  9. 哈工大软件构造Lab1(2022)

    目录 一.实验目标概述 二.实验环境配置 1.安装编写java程序的IDE--IntelliJ IDEA 2.安装Git 3.安装Junit 4.GitHub Lab1仓库的URL地址 三.实验过程 ...

随机推荐

  1. 如何监控 Elasticsearch 集群状态?

    Marvel 让你可以很简单的通过 Kibana 监控 Elasticsearch.你可以实时查看你 的集群健康状态和性能,也可以分析过去的集群.索引和节点指标.

  2. Elasticsearch 对于大数据量(上亿量级)的聚合如何实现?

    Elasticsearch 提供的首个近似聚合是 cardinality 度量.它提供一个字段的基数, 即该字段的 distinct 或者 unique 值的数目.它是基于 HLL 算法的.HLL 会 ...

  3. NULL 是什么意思 ?

    NULL 这个值表示 UNKNOWN(未知):它不表示""(空字符串).对 NULL 这 个值的任何比较都会生产一个 NULL 值.您不能把任何值与一个 NULL 值进行比 较,并 ...

  4. 学习RabbitMQ(三)

    1 用户注册后(会立即提示注册成功),过一会发送短信和邮件通知 发布/订阅模型 以上模式一般是用户注册成功后,写入一条数据到mysql,在发送一条消息到MQ! 如果不用消息中间件(或者简单的做成异步发 ...

  5. 攻防世界shrine

    shrine import flask import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG' ...

  6. C++ | 动多态 | 虚函数表

    多态机制 C++语言有三大特性:封装.继承.多态. 其中所谓的多态,即 "同一接口,不同形态".接口在我们 C/C++ 语言中可以理解为函数名,不同形态可以理解为函数执行的功能不同 ...

  7. 在页面未加载完之前显示loading动画

    在页面未加载完之前显示loading动画 这里有很多比这篇博客还优秀的loading动画源码 我还参考这篇博客 loading动画代码demo 我的demo预览 <!DOCTYPE html&g ...

  8. 从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命

    本系列文章对应游戏代码已开源 Sinuous game. 每个游戏都会包含场景和角色.要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来. 场景坐标 canv ...

  9. 在 MarkDown 中添加表格(例如:在 CSDN 中添加表格)

    内容 一.使用 Markdown 创建表格(例如:在 CSDN 中创建表格) 1. 表格格式 对齐方式 -: 设置内容和标题栏居右对齐: :- 设置内容和标题栏居左对齐: :-: 设置内容和标题栏居中 ...

  10. 微信小程序命名规则

    目录分析 src是主要的开发目录,各个文件实现功能如下所示: ├─.idea │ └─libraries ├─.temp ├─config └─src ├─assets │ └─images ├─co ...