可变对象 不可变对象区别_对象应该是不可变的

可变对象 不可变对象区别

在面向对象的编程中,如果对象的状态在创建后无法修改,则它是不可变的 。

在Java中,不可变对象的一个​​很好的例子是String 。 创建后,我们无法修改其状态。 我们可以要求它创建新的字符串,但是它自己的状态永远不会改变。

但是,JDK中没有那么多不可变的类。 以类Date为例。 可以使用setTime()修改其状态。

我不知道为什么JDK设计师决定以不同的方式来制作这两个非常相似的类。 但是,我认为可变Date的设计有许多缺陷,而不变的String则更多地体现了面向对象范例的精神。

而且,我认为在一个完美的面向对象的世界所有类都应该是不变的 。 不幸的是,有时由于JVM的限制在技术上是不可能的。 尽管如此,我们应该始终追求最佳。

这是支持不变性的参数的不完整列表:

  • 不变的对象更易于构造,测试和使用
  • 真正不可变的对象始终是线程安全的
  • 它们有助于避免时间耦合
  • 它们的使用无副作用(无防御性副本)
  • 避免了身份变异性问题
  • 他们总是有失败原子性
  • 它们更容易缓存
  • 他们防止NULL引用, 这是不好的

让我们一一讨论最重要的论点。

线程安全

第一个也是最明显的论点是,不可变对象是线程安全的。 这意味着多个线程可以同时访问同一对象,而不会与另一个线程发生冲突。

如果没有对象方法可以修改其状态,那么无论它们有多少个以及被调用的频率是多少,它们都将在自己的堆栈内存空间中工作。

Goetz等。 在非常著名的《 Java Concurrency in Practice》 (强烈推荐)一书中,更详细地介绍了不可变对象的优点。

避免时间耦合

这是时间耦合的示例(代码发出两个连续的HTTP POST请求,其中第二个包含HTTP正文):

Request request = new Request("http://example.com");
request.method("POST");
String first = request.fetch();
request.body("text=hello");
String second = request.fetch();

此代码有效。 但是,您必须记住,在配置第二个请求之前,应该先配置第一个请求。 如果我们决定从脚本中删除第一个请求,则将删除第二行和第三行,并且不会从编译器中得到任何错误:

Request request = new Request("http://example.com");
// request.method("POST");
// String first = request.fetch();
request.body("text=hello");
String second = request.fetch();

现在,该脚本已损坏,尽管它编译时没有错误。 这就是时间耦合的意义所在—代码中总是有一些程序员必须记住的隐藏信息。 在此示例中,我们必须记住,第一个请求的配置也用于第二个请求。

我们必须记住,第二个请求应始终保持在一起,并在第一个请求之后执行。

如果Request类是不可变的,则第一个代码片段将一开始就无法使用,并且将被重写为:

final Request request = new Request("");
String first = request.method("POST").fetch();
String second = request.method("POST").body("text=hello").fetch();

现在,这两个请求没有耦合。 我们可以安全地删除第一个,第二个仍然可以正常工作。 您可能会指出存在代码重复。 是的,我们应该摆脱它,然后重新编写代码:

final Request request = new Request("");
final Request post = request.method("POST");
String first = post.fetch();
String second = post.body("text=hello").fetch();

看,重构没有破坏任何东西,而且我们仍然没有时间耦合。 可以安全地从代码中删除第一个请求,而不会影响第二个请求。

我希望这个例子能证明操作不可变对象的代码更具可读性和可维护性,因为它没有时间耦合。

避免副作用

让我们尝试在新方法中使用我们的Request类(现在它是可变的):

public String post(Request request) {request.method("POST");return request.fetch();
}

让我们尝试发出两个请求-第一个请求使用GET方法,第二个请求使用POST:

Request request = new Request("http://example.com");
request.method("GET");
String first = this.post(request);
String second = request.fetch();

方法post()具有“副作用”,它对可变对象request进行了更改。 在这种情况下,这些更改并不是真正预期的。 我们希望它发出POST请求并返回其主体。 我们不想阅读其文档只是为了发现它还在后台修改了我们作为参数传递给它的请求。

不用说,这种副作用会导致错误和可维护性问题。 使用不可变的Request会更好:

public String post(Request request) {return request.method("POST").fetch();
}

在这种情况下,我们可能没有任何副作用。 任何人都不能修改我们的request对象,无论它在何处使用以及方法调用传递给调用堆栈的深度如何:

Request request = new Request("http://example.com").method("GET");
String first = this.post(request);
String second = request.fetch();

此代码是绝对安全且无副作用的。

避免身份变异

通常,如果对象的内部状态相同,我们希望它们相同。 Date类是一个很好的例子:

Date first = new Date(1L);
Date second = new Date(1L);
assert first.equals(second); // true

有两个不同的对象。 但是,它们彼此相等,因为它们的封装状态相同。 通过自定义的equals()hashCode()方法的重载实现,可以实现这一点。

这种方便的方法与可变对象一起使用的结果是,每次我们修改对象的状态时,它都会更改其身份:

Date first = new Date(1L);
Date second = new Date(1L);
first.setTime(2L);
assert first.equals(second); // false

在您开始将可变对象用作地图中的键之前,这看起来很自然:

Map<Date, String> map = new HashMap<>();
Date date = new Date();
map.put(date, "hello, world!");
date.setTime(12345L);
assert map.containsKey(date); // false

修改date状态对象时,我们不希望它更改其身份。 我们不希望仅因为其键的状态已更改而在映射中丢失条目。 但是,这正是以上示例中发生的情况。

当我们向地图添加对象时,其hashCode()返回一个值。 HashMap使用此值将条目放置到内部哈希表中。 当我们调用containsKey()时,对象的哈希码是不同的(因为它基于其内部状态),并且HashMap在内部哈希表中找不到它。

调试可变对象的副作用非常烦人且困难。 不可变的对象完全避免了它。

失效原子性

这是一个简单的示例:

public class Stack {private int size;private String[] items;public void push(String item) {size++;if (size > items.length) {throw new RuntimeException("stack overflow");}items[size] = item;}
}

显然,如果Stack类在溢出时引发运行时异常,则该对象将处于断开状态。 它的size属性将增加,而items将不会获得新元素。

不变性可以防止此问题。 对象永远不会处于损坏状态,因为它的状态仅在其构造函数中被修改。 构造函数将失败,拒绝对象实例化,或者成功,则将生成有效的固态对象,该对象永远不会更改其封装状态。

有关此主题的更多信息,请阅读Joshua Bloch撰写的有效Java,第二版 。

反对不变性的争论

有许多反对不变性的论点。

  1. “不可迁移性不适用于企业系统”。 我经常听到人们说不变性是一种奇特的功能,而在真正的企业系统中绝对不可行。 作为反驳,我只能显示一些仅包含不可变Java对象的实际应用程序示例: jcabi-http , jcabi-xml , jcabi-github , jcabi-s3 , jcabi-dynamo , jcabi-simpledb所有仅与不可变类/对象一起使用的Java库。 netbout.com和stateful.co是仅与不可变对象一起使用的Web应用程序。
  2. “更新现有对象比创建新对象便宜”。 Oracle 认为 :“对象创建的影响通常被高估了,并且可以被与不可变对象相关的某些效率所抵消。 其中包括由于垃圾收集而减少的开销,以及消除了保护可变对象免受损坏所需的代码。” 我同意。

如果您还有其他论点,请在下面发表,我将尝试发表评论。

翻译自: https://www.javacodegeeks.com/2014/09/objects-should-be-immutable.html

可变对象 不可变对象区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

判别分析分为r型和q型吗_SPSS聚类和判别分析参考.ppt

SPSS聚类和判别分析参考10.1主成分分析和因子分析简介 3 常用术语 (1)因子载荷 (2)变量共同度 (3)公共因子的方差贡献 10.1主成分分析和因子分析简介 10.1.2主成分和公因子数量的确定 (1) 确定时遵循几个原则 主成分的累积贡献率&#xff1a;一般来说&#xff0c;提取主成分的累…

计算机应用基础人才培养方案,1. 培养方案(计算机应用基础课程).doc

人才培养方案一、课程定位和课程目标1、课程性质和任务  《计算机应用基础》是高职高专教育中的一门理论与实践相结合的基础必修课&#xff0c;是培养大学生信息素养的必修课程。但与普通的素质教育课程不同&#xff0c;由于计算机应用的普及性和广泛性&#xff0c;学生的计算…

使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第二部分

在本系列的第一篇文章中&#xff0c;我们创建了一个基本的身份验证服务&#xff0c;以使用SoapUI进行调用。 因此&#xff0c;在第二篇文章中&#xff0c;我们将逐步演示如何使用此工具成功调用这种服务。 使用SoapUI的1-Basic WCF SOAP –创建新的SOAP项目 首先&#xff0c;我…

r语言electricity数据集_R语言实验报告.doc

R语言实验报告R语言实验报告R语言判别分析实验报告班级&#xff1a;应数1201学号姓名&#xff1a;麦琼辉时间&#xff1a;2016年11月28号1 实验目的及要求1) 了解判别分析的目的和意义&#xff1b;2) 熟悉R语言中有关判别分析的算法基础。2 实验设备及要求个人计算机一台&#…

linux rar加压_Linux之rar文件解压之路

导读相信大家在使用Linux系统时有时候会碰到有关于rar文件的解压缩。网上下载rar 压缩文件的使用&#xff0c;在linux在我们需要对其进行解压缩&#xff0c;这个时候&#xff0c;我们需要安装 rar相关的文件&#xff0c;来进行解压缩。1. 下载我们进入rarlab网站&#xff0c;进…

全国计算机二级office基础知识,全国计算机二级office考试内容

计算机领域中所运用的技术方法和技术手段。计算机技术具有明显的综合特性&#xff0c;它与电子工程、应用物理、机械工程、现代通信技术和数学等紧密结合&#xff0c;发展很快。下面是小编整理的关于全国计算机二级office考试内容&#xff0c;希望大家认真阅读!基本要求1. 掌握…

insight切换窗口 source_source insight的使用方法逆天整理(1)

A. why SI:为什么要用Source Insight呢&#xff1f;因为她比完整的IDE要更快啊&#xff0c;比一般的编辑器便捷啊&#xff01;她有逆天的查看定义&#xff0c;查看调用&#xff0c;查看引用功能以及方便的Context Window上下文显示&#xff0c;像这样的东西非常有利于查看大量代…

html table nei边框线,GitHub - meichuanneiku/TableCell: 在TableBank的基础上,进一步标注到单元格精度,利用目标检测/分割实现单元格定位。...

项目说明本项目是我2019年7月份的实习工作的**展示与记录**&#xff1a;把倾斜的表格旋转水平&#xff1b;制作5000张表格数据集&#xff0c;需要标注每一个单元格&#xff0c;并实现单元格检测第一项比较简单&#xff0c;仿射变换、透视变换已经很成熟了&#xff0c;关键是第二…

前缀命名

如果您是第一次查看Takes或Cactoos的源代码&#xff0c;则很可能会像其他名称一样被命名约定触发&#xff0c;这意味着大多数类名称都有两个字母的前缀&#xff1a; BkSafe &#xff0c; RqFake &#xff0c; RsWithStatus &#xff0c; TkGzip等。 老实说&#xff0c;我还没有…

python中excel制作成绩报表_python制作简单excel统计报表2之操作excel的模块openpyxl简单用法...

python制作简单excel统计报表2之操作excel的模块openpyxl简单用法# codingutf-8 from openpyxl import Workbook, load_workbook from openpyxl.drawing.image import Image from openpyxl.styles import Font,colors from datetime import datetime import MySQLdb class Exce…

2018软科计算机科学与技术排名,又一中国高校“计算机学科排名”发布,清华第1,浙大第2...

计算机类专业是近些年最热门的专业之一&#xff0c;这类专业不仅就业情况好&#xff0c;工资待遇水平也很高&#xff0c;就是在考公务员时也很有优势。现在&#xff0c;大部分高校都已经开设了计算机类专业&#xff0c;我国学习计算机类专业的学生数量非常多&#xff0c;所以&a…

java数字格式化_Java数字格式

java数字格式化当我看到其他人编写不必要的Java代码并且由于缺乏对已经提供所需功能的JDK类的了解而编写了不必要的Java代码时&#xff0c;我会想到很多次。 这样的一个例子是时间相关的常量的使用硬编码值的写入&#xff0c;如60 &#xff0c; 24 &#xff0c; 1440 &#xff…

快手小筷子机器人_小筷子app官方版下载-快手控场机器人小筷子app下载v1.0.0安卓版_289手游网...

快手控场机器人小筷子app是一个专门为快手主播打造的专业实用工具&#xff0c;是每个快手主播的最佳直播伴侣&#xff01;快手控场机器人小筷子app能够实时与观众弹幕互动&#xff0c;还能语音播报各种礼物答谢等等&#xff0c;帮助每个主播更好的聚拢粉丝。快手控场机器人小筷…

计算机专业英语主要句型及翻译技巧,计算机专业英语单词及翻译等技巧-20210420072747.ppt-原创力文档...

New Words & Expressions:computerlike a. 计算机似的electromechanical a. 机电的, 电机的vacuum tubes 真空管Census Bureau 人口普查局thousands of 成千上万的 known as 通常所说的&#xff0c;以……著称1.1 The Invention of the ComputerAbbreviations:ENIAC(Electr…

安川机器人编程加电弧_安川AR2010机器人

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼安川AR2010机器人 微信hao123ali联系15067165514 安川机器人末端工具快换_桥田_品质承诺实现安川机器人的一机多用&#xff0c; 工装夹具高效切换&#xff0c;专注于提升工业自动化生产线柔性&#xff0c;技术领先&#xff0c;荣获…

再访PMML

嗨伙计&#xff01; 从今年年初开始&#xff0c;就有了重新设计Drools PMML模块的计划。 在这篇文章中&#xff0c;我将描述我们将如何处理它&#xff0c;目前的状态&#xff0c;未来发展的想法等&#xff0c;等等……敬请期待&#xff01; 背景 PMML是一个标准&#xff0c;旨…

用计算机怎么弹离人愁数字,拇指琴新手入门曲谱——离人愁

喜欢古风的朋友赶快凑过来啦&#xff0c;最近抖音上超火的离人愁拇指琴教学&#xff0c;喜欢离人愁的小姐姐小哥哥赶快学起来啦&#xff01;以下琴谱适用于Hugh Tracey G调17键。南非琴出厂调音是G调排列&#xff0c;习惯了C调音阶排列的朋友可能对G调排列不是很适应。因为两者…

支持nvme的linux_Linux nvme驱动初探

本篇研究的nvme驱动基于Linux 3.10.73 ,为什么选择这个版本呢&#xff0c;因为这个版本之后Linux 块层马上就换成支持多队列(可以参考Linux块层多队列之引入内核)&#xff0c;小编的SUSE 11.3也正好能编译这个相对比较低的版本。(随后再看最新版本内核上nvme驱动的实现)通过nvm…

jvm7 jvm8_JVM PermGen –您在哪里?

jvm7 jvm8这篇文章介绍了JVM内存结构的一些基础知识&#xff0c;并快速窥视了PermGen&#xff0c;以了解自Java SE 8出现以来它已消失的地方。 裸基础 JVM只是系统上运行的另一个进程&#xff0c;魔术始于java命令。 像任何OS进程一样&#xff0c;它需要内存才能运行。 请记住…

2019网络教育计算机统考模拟试题,最新2019年网络远程教育《计算机应用基础》统考模拟题库500题(含答案)...

2019年网络远程教育统考《计算机应用基础》考试题库500题[含答案]一、选择题1&#xff0e;启动ExCEl2003应用程序后自动建立的工作簿文件的文件名为_______。A.工作簿B.工作簿文件C.BookFilE1D.Book12&#xff0e;OutlookExprEss的主要功能是__________。A.创建电子邮件账户B.搜…