尺度不变性是指什么不变_不变性的来龙去脉

尺度不变性是指什么不变

因此,在我的第一篇文章中,我谈到了一些构建器模式,并提到了一个非常强大但却被忽视的概念:不变性。

什么是不可变类? 这只是一个其实例无法修改的类。 类属性的每个值都在其声明或其构造函数中设置,并且在对象的整个生命周期中保留这些值。 Java有很多不变的类,例如String ,所有装箱的原语( DoubleIntegerFloat等), BigIntegerBigDecimal等。 这有一个很好的理由:不可变类比可变类更易于设计,实现和使用。 一旦实例化它们,它们只能处于一种状态,因此它们不易出错,并且,正如我们在本文后面会看到的那样,它们更加安全。

您如何确保类是不变的? 只需遵循以下5个简单步骤:

  1. 不要提供任何可修改对象状态的公共方法 ,也称为增变器(例如setter)。
  2. 防止扩展类 。 这不允许任何恶意或粗心的类扩展我们的类并损害其不变的行为。 这样做的通常且更简单的方法是将类标记为final ,但是我将在本文中提及另一种方法。
  3. 将所有字段定为最终值 。 这是让编译器为您强制执行第1点的方法。 此外,它清楚地使任何看到您的代码的人都知道,您不希望这些字段在设置后更改其值。
  4. 将所有字段设为私有 。 无论您是否考虑了不变性,这一点都应该很明显,并且您应该遵循它 ,但是我只是为了以防万一。
  5. 永远不要提供对任何可变属性的访问 。 如果您的类具有一个可变对象作为其属性之一(例如ListMap或您的域问题中的任何其他可变对象),请确保该类的客户端永远无法获得对该对象的引用。 这意味着您永远不要从访问器(例如,getter)直接返回对它们的引用,并且永远不要在构造函数中使用从客户端作为参数传递的引用来初始化它们。 在这种情况下,您应该始终制作防御性副本。

有很多理论知识,没有代码,因此让我们看看一个简单的不可变类是什么样子,以及它如何处理我之前提到的5个步骤:

public class Book {private final String isbn;private final int publicationYear;private final List reviews;private Book(BookBuilder builder) {this.isbn = builder.isbn;this.publicationYear = builder.publicationYear;this.reviews = Lists.newArrayList(builder.reviews);}public String getIsbn() {return isbn;}public int getPublicationYear() {return publicationYear;}public List getReviews() {return Lists.newArrayList(reviews);}public static class BookBuilder {private String isbn;private int publicationYear;private List reviews;public BookBuilder isbn(String isbn) {this.isbn = isbn;return this;}public BookBuilder publicationYear(int year) {this.publicationYear = year;return this;}public BookBuilder reviews(List reviews) {this.reviews = reviews == null ? new ArrayList() : reviews;return this;}public Book build() {return new Book(this);}}
}

我们将在这个非常简单的课程中讲解重点。 首先,您可能已经注意到,我再次使用了构建器模式。 这不仅是因为我是它的忠实拥护者,而且还因为我想说明一些我不想在之前的文章中没有首先对不变性概念有基本了解的观点。 现在,让我们看一下我提到的5个步骤,您需要遵循这些步骤使一个类不可变,并查看它们是否对本书示例有效:

    • 不要提供任何修改对象状态的公共方法 。 请注意,类上的唯一方法是其私有构造函数和其属性的获取器,但没有更改对象状态的方法。
    • 防止扩展类 。 这很棘手。 我提到确保这一点的最简单方法是将班级定为最终班,但Book班显然不是最终班。 但是,请注意,唯一可用的构造函数是private 。 编译器确保没有公共或受保护的构造函数的类不能被子类化。 因此,在这种情况下,不需要在类声明中使用final关键字,但是无论如何将其包括在内只是个好主意,以使看到您代码的任何人都可以清楚地知道。
    • 将所有字段定为最终值 。 非常简单,该类上的所有属性都声明为final
    • 永远不要提供对任何可变属性的访问 。 这实际上很有趣。 请注意Book类如何具有一个List <String>属性,该属性被声明为final并且其值在类构造函数上设置。 但是,此列表是可变对象。 也就是说,虽然评论参考一旦设置便无法更改,但列表的内容可以更改。 引用相同列表的客户端可以添加或删除元素,结果,在创建Book对象后更改其状态。 因此,请注意,在Book构造函数中,我们不直接分配引用。 相反,我们使用Guava库通过调用“ this.reviews = Lists.newArrayList(builder.reviews); ”来复制列表this.reviews = Lists.newArrayList(builder.reviews); ”。 在getReviews方法上可以看到相同的情况,在该方法中,我们返回列表的副本而不是直接引用。 值得注意的是,此示例可能有点简化,因为评论列表只能包含不可变的字符串。 如果列表的类型是可变的类,那么您还必须复制列表中的每个对象,而不仅仅是列表本身。

最后一点说明了为什么不可变的类导致更简洁的设计和更易于阅读的代码。 您只需共享那些不可变的对象,而不必担心防御性副本。 实际上,绝对不要制作任何副本,因为对象的任何副本都将永远等于原始副本。 一个必然的结论是,不变的对象只是简单的。 他们只能处于一种状态,并且一生都保持这种状态。 您可以使用类构造函数检查所有不变量(即需要在该类上有效的条件,例如其属性之一的值范围),然后可以确保这些不变量保持真实状态而无需付出任何努力您或您的客户。

不变对象的另一个巨大好处是它们本质上是线程安全的。 它们不能被同时访问对象的多个线程破坏。 到目前为止,这是在应用程序中提供线程安全性的最简单且不易出错的方法。

但是,如果您已经有一个Book实例并且想要更改其属性之一的值怎么办? 换句话说,您想要更改对象的状态。 在不可变的类上,按照定义,这是不可能的。 但是,与软件中的大多数事情一样,总有一种解决方法。 在这种情况下,实际上有两个。

第一种选择是在Book类上使用Fluent Interface技术,并具有类似于setter的方法,这些方法实际上创建一个对象,该对象的所有属性都具有相同的值,但要更改的对象除外。 在我们的示例中,我们将必须在Book类中添加以下内容:

private Book(BookBuilder builder) {this(builder.isbn, builder.publicationYear, builder.reviews);}private Book(String isbn, int publicationYear, List reviews) {this.isbn = isbn;this.publicationYear = publicationYear;this.reviews = Lists.newArrayList(reviews);}public Book withIsbn(String isbn) {return new Book(isbn,this.publicationYear, this.reviews);}

请注意,我们添加了一个新的私有构造函数,可以在其中指定每个属性的值,并修改了旧的构造函数以使用新的构造函数。 此外,我们添加了一个新方法,该方法返回一个新的Book对象,该对象具有我们想要的isbn属性值。 相同的概念适用于该类的其余属性。 之所以称为功能方法,是因为方法无需修改即可返回对其参数进行操作的结果。 这与程序命令式方法形成对比,在方法式命令式方法中,方法将一个过程应用于其操作数,从而更改其状态。

这种生成新对象的方法显示了不可变类的唯一真正缺点:它们要求我们为所需的每个不同值创建一个新对象,这会在性能和内存消耗方面产生可观的开销。 如果要更改对象的几个属性,则会放大此问题,因为在每个步骤中都将生成一个新对象,并且最终会丢弃所有中间对象,而仅保留最后一个结果。

我们可以在构建器模式的帮助下为多步操作提供更好的选择,例如我在上一段中描述的操作。 基本上,我们向构建器添加一个新的构造器,该构造器采用一个已经创建的实例来设置其所有初始值。 然后,客户端可以以通常的方式使用构建器来设置所有所需的值,然后使用build方法来创建最终对象。 这样,我们避免只使用我们需要的某些值来创建中间对象。 在我们的示例中,此技术在生成器方面看起来像这样:

public BookBuilder(Book book) {this.isbn = book.getIsbn();this.publicationYear = book.getPublicationYear();this.reviews = book.getReviews();
}

然后,在我们的客户上,我们可以:

Book originalBook = getRandomBook();Book modifiedBook = new BookBuilder(originalBook).isbn('123456').publicationYear(2011).build();

现在,显然构建器不是线程安全的,因此您必须采取所有通常的预防措施,例如不与多个线程共享构建器。

我提到过这样一个事实,即我们必须为状态的每个更改都创建一个新对象,这可能会增加性能,这是不可变类的唯一真正的缺点。 但是,对象创建是JVM不断改进的方面之一。 实际上,除特殊情况外,对象创建比您想象的要高效得多。 无论如何,提出一个简单明了的设计通常是一个好主意,然后仅在进行测量后才重构性能。 在尝试猜测代码花费大量时间的十分之九的时间中,有九次会发现自己错了。 此外,不变对象可以自由共享而不必担心后果,这一事实使您有机会鼓励客户端尽可能重用现有实例,从而大大减少了创建对象的数量。 一种常见的方法是为最常见的值提供公共静态最终常量。 此技术在JDK上大量使用,例如在Boolean.FALSEBigDecimal.ZERO中

总结一下这篇文章,如果您想从中学到一些东西,那就这样吧: 除非有充分的理由使它们可变,否则类应该是不可变的 。 不要为每个类属性自动添加设置器。 如果出于某种原因您绝对不能使您的类不可变,那么请尽可能限制其可变性。 一个对象可以处于的状态越少,就越容易考虑该对象及其不变量。 并且不必担心不变性的性能开销,很有可能您不必担心它。

参考: JCG合作伙伴 Jose Luis在开发上的 不变性的来龙去脉 。

翻译自: https://www.javacodegeeks.com/2013/01/the-ins-and-outs-of-immutability.html

尺度不变性是指什么不变

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

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

相关文章

光模块

一、光模块&#xff1a; 1、光模块&#xff08;opTIcalmodule&#xff09;由光电子器件、功能电路和光接口等组成&#xff0c;光电子器件包括发射和接收两部分。 发射部分是&#xff1a;输入一定码率的电信号经内部的驱动芯片处理后驱动半导体激光器&#xff08;LD&#xff09;…

radmin提示授权码过期_IdentityServer4 客户端授权模式(Client Credentials)

(给DotNet加星标&#xff0c;提升.Net技能)转自&#xff1a;朝闲cnblogs.com/Zing/p/13361386.html前言1、源码(.NET Core 2.2)git地址&#xff1a;https://github.com/yizhaoxian/CoreIdentityServer4Demo.git2、相关章节2.1、《IdentityServer4 (1) 客户端授权模式(Client Cr…

OpenCV访问像素点的灰度值

1.Mat矩阵数值的存储方式 这里以指针的方式访问图像素为例 (1)单通道 定义一个单通道图像&#xff1a; cv::Mat img_1 (320, 640, CV_8UC1, Scalar(0)); 对于单通道M(i,j)即为第i行j列的其灰度值&#xff1b;程序中表示为&#xff1a; img_1.ptr<uchar>(i)[j]; (2)多通道…

asp.net中大文件下载

因为IIS支持的最大文件为int32的最大值位数的文件下载&#xff0c;所以&#xff0c;超过2G的文件无法通过IIS进行下载。 通过网上查找的资料&#xff0c;如下可实现文件的下载&#xff0c;使用filestream进行下载。 public void download(){System.IO.Stream iStream null;byt…

Java的终结器仍然存在

当我第一次学习Java并从C 过渡到Java时&#xff0c;我记得我经常被告知&#xff0c;经常读到它不应该像C 析构函数那样对待Java终结器&#xff0c;也不应该依靠它。 该建议的频率和坚持性对我产生了影响&#xff0c;以至于我无法回忆起我上一次编写finalize&#xff08;&#x…

时隙aloha协议仿真程序_工控ModbusTCP/IP协议仿真环境搭建

01ModbusTCP/IP协议简介Modbus TCP/IP是简单的、中立厂商的用于管理和控制自动化设备的MODBUS系列通讯协议的派生产品。它覆盖了使用TCP/IP协议的“intranet”和“internet”环境中MODBUS报文的用途。协议的最通用用途是为诸如PLC’S&#xff0c;I/O模块&#xff0c;以及连接其…

vector的初始化及常用操作

1.vector的初始化&#xff1a;可以有五种方式,举例说明如下&#xff1a; &#xff08;1&#xff09; vector<int> a(10); //定义了10个整型元素的向量&#xff08;尖括号中为元素类型名&#xff0c;它可以是任何合法的数据类型&#xff09;&#xff0c;但没有给出初值&a…

查看Linux版本

suse版本 lsb_release -a suse版本之间差距还是有的&#xff0c;在suse12.2跑的脚本没有问题&#xff0c;到suse12.4上就出现问题了。 centos版本 cat /etc/redhat-release 转载于:https://www.cnblogs.com/jkhere/p/10900659.html

ps法线贴图插件_法线与置换贴图原理讲解以及烘焙制作!

​首先在讲解之前先给大家讲解下什么是法线贴图&#xff0c;法线贴图其实就是一张带有颜色的凹凸贴图&#xff0c;分别是有红绿蓝&#xff0c;三种颜色构造而成的&#xff0c;如下图所示&#xff1a;每一个颜色通道分别控制了每个像素的方向&#xff0c;当你把法线贴图用到低解…

从向量中读取元素:

从向量中读取元素&#xff1a; 1通过下标&#xff1a; int a[6] { 1,2,3,4,5,6 }; vector<int> b(a, a 4); for (int i 0; i < b.size() - 1; i) cout << b[i] << " "; 2通过迭代器 int a[6] { 1,2,3,4,5,6 }; vector<int> b(a, …

Nim博弈

忽然发现博弈论是个很好玩的东西哎 之前假期学长讲课的时候就发现这种必胜的战略可以用来坑人做题 这两天终于做了第一道博弈论的题&#xff0c;写篇博客纪念一下 灵感来源&#xff1a;洛谷P1247 Pre-scene 众所周知&#xff0c;李明和Jenny都喜欢Danny&#xff0c;为了争夺Dan…

java调用怎么调用方法区_Java中的方法调用有多昂贵

java调用怎么调用方法区我们都去过那儿。 在查看设计不良的代码的同时&#xff0c;听听作者对人们永远不应该牺牲性能而不是设计的解释。 而且&#xff0c;您不能说服作者摆脱其500行方法&#xff0c;因为链接方法调用会破坏性能。 好吧&#xff0c;这可能在1996年左右是正确的…

python中的np array函数_numpy中的np.ascontiguousarray()函数

"Return a contiguous array (ndim > 1) in memory (C order)."用途ascontiguousarray函数将一个内存不连续存储的数组转换为内存连续存储的数组&#xff0c;使得运行速度更快。C order vs Fortran orderC order 指的是行优先的顺序(Row-major Order)&#xff0c;…

OpenCV版本

#include <opencv2/opencv.hpp>using namespace cv;void main(){/*printf("\t当前使用的opencv版本为 OpenCV"CV_VERSION);getchar();*/std::cout << "\t当前使用的opencv版本为 OpenCV" << CV_VERSION << std::endl;system(&quo…

使用jstat的JVM统计信息

过去&#xff0c;我已经写过关于Oracle和/或OpenJDK Java开发工具包&#xff08;JDK&#xff09;随附的几个命令行工具的信息&#xff0c;但是我从来没有专门写过jstat工具 。 Oracle JDK 9文档Early Access指出jstat用于“监视Java虚拟机&#xff08;JVM&#xff09;统计信息”…

每天一点点之 taro 框架开发 - taro路由及传参

1.路由 taro的路由是自带的&#xff0c;不需要我们额外配置&#xff0c;只需要我们在app.js下config中配置pages即可 class App extends Component {config {pages: [pages/test/test,pages/index/index],} } 2.taro通过api实现跳转&#xff0c;替换 官网地址&#xff1a;taro…

调用支付jsapi缺少参数:total_fee_小程序支付问题怎么解决?

微信小程序开发的过程一定会遇到各种问题&#xff0c;最让人棘手的就是支付问题&#xff0c;因为没有支付做商城类似的小程序就没有办法完成最关键的一步。那么支付失败到底什么原因呢&#xff1f;一下子收集了几个错误类似&#xff0c;希望对你有帮助&#xff1a;No.1{err_cod…

Visual Stutio中win32控制台应用程序、win32项目、MFC项目的区别:

我的理解&#xff0c;我首先把这几个货分为两大类&#xff1b;第一类包括win32控制台应用程序&#xff0c;第二类就是剩下那俩货。而剩下那俩货又有一个包含关系&#xff0c;即win32项目包含mfc。&#xff08;我个人是这个区分的。&#xff09; 一、 区别: &#xff08;1&…

二分图匹配【模板】

传送门&#xff1a; #include <cstdio> #include <cstring> #include <cmath> using namespace std; #define ll long long #define re register const int N1005; inline void read(int &a) {a0;int d1;char ch;while(chgetchar(),ch>9||ch<0)if(…

c++两个数组对比去掉重复的元素_30 数组案例

数组案例案例一&#xff1a;筛选指定数组元素要求&#xff1a;将数组[2,0,6,1,77,0,52,0,25,7]中的0去掉后&#xff0c;形成一个不包含0的新数组。var arr [2,0,6,1,77,0,52,0,25,7];var newArr [];for(var i0; i<arr.length; i){if(arr[i]!0){newArr[newArr.length]arr[i…