java:示例中不可变对象的优点

请举例说明我可以看到不可变对象的优点。 我在互联网上发现的信息集中在线程中。 我还不知道线程。 如果例子使用简单的原则会很棒

不可变性在multithreading程序中很重要,因为你知道一个线程不会破坏另一个线程中使用的值。 但它在单线程程序中也很有用。

这是一个简单的例子:

Integer i=Integer.valueOf(17); foo(i); bar(i); 

您可能想知道,传递给bar()的值是多少?

假设foo()是一个很大的复杂函数。 在这个例子中,我知道一个绝对的事实,当foo完成时,我仍然等于17,因为整数是不可变的。 如果不是这样,我将不得不研究foo来判断它是否可能被改变。

这是一个稍微复杂的例子。 假设我有一些类似于整数的对象,但是它是可变的。 我们称之为MutableInteger。 然后说我写这个:

 MutableInteger currentInventory=findQtyInInventory(); MutableInteger neededInventory=currentInventory; // copy current for starters ... bunch of other code ... neededInventory.subtract(allocatedToSales); currentInventory.add(arriving); ... bunch of more code ... if (neededInvenory.compareTo(currentInventory)>0) display("Shortage!"); 

你看到上面的问题了吗? neededInventory和currentInventory指向同一个对象。 所有的加法和减法都是在相同的值上运行,而不是两个不同的值,所以当我们进行测试时,它总是相等的。 如果对象是可变的,上面的代码将永远不会工作。 如果它们是不可变的,则添加和减去必须返回结果对象而不是在适当的位置更新,这将起作用。

几年前我使用了Fortran编译器,其中整数是可变的。 我们有一个函数接受了几个参数,其中一个是整数。 在极少数情况下,函数更新整数。 然后有一天有人写了一个对这个函数的调用,将常量“2”作为整数传递。 该函数决定更新参数,从而将“常量”2改为1! 程序中使用常量2的每个其他位置现在神秘地得到值1。 这花了很长时间来调试。

这不是一个可以通过示例有用地解释的概念。 不可变对象的优点是你知道他们的数据不能改变,所以你不必担心这一点。 您可以自由地传递它们,而不必记住传递它们的方法是否可以以您的代码不准备处理的方式更改它们。 这使得处理不可变数据变得更容易。

对于multithreading,这个优势更为重要,因为基于多个线程以不应该更改的方式更改数据的错误通常是不可重现的 – 它们依赖于时序,因此有时会发生,有时不会发生,这使得它们很难分析并修复。

当Java按值返回时(即从方法返回对象引用),如果我返回一个如下所示的String:

 private String myString = "foo"; public String getString() { return this.myString; } 

并且String类不是不可变的,那么getString()的调用者可以修改myString ,这可能不是所需的行为; 系统的其他部分可能不希望或期望myString更改。 因此调用者只能更改myString指向的对象,而不是myString本身。

它有点同义反复,但不可变对象的主要优点是它们无法改变 。 当对象可以改变时,你必须考虑它们可能发生的事情。 你必须考虑如何,何时以及为什么要改变它们。 您必须考虑应用程序中的其他代码可能有权访问同一对象,以及在您不知情的情况下可能会更改的内容。 不可变对象有效地减少了您必须在系统中进行操作的“移动部件”的数量(和粒度),并使您的生活更轻松。

如您所知,这是multithreading的一个很好的模式。

它还意味着更好的封装 。 您可以传递这些对象并共享它们,您永远不必担心有人会更改对象的状态。

Java核心库中有一些很好的例子。 数字子类是一个,但我认为最好的例子是String 。 你传递它们,连接,获得子串等,永远不需要考虑其他地方。 如果它像C / C ++ char[]一样可变,那么你总是需要牢记这一点。

出于同样的原因,它还会导致更易读和可维护的代码 。 无需关心对象的其他用户。

这两个原因导致我们进入另一个称为价值对象的重要模式。 简而言之,当您关心某个特定值 (日期,数字,字符串,间隔,金钱或某些稍微复杂的对象,如果您需要)时,它是有意义的, 但值本身没有身份 ,即它具有无论上下文如何,完全相同的含义。

当对象通常被共享时,不可变对象很有用 – 不仅仅是线程,而是在单线程程序中,对象有很多客户端。

例如, String可能是Java中最常用的不可变对象。 阻止该字符串的用户更改其内容是不可改变的。 如果String是可变的,那就意味着每个用户都必须创建该字符串的唯一副本,以确保没有其他人在改变它。

不可变数据也具有安全隐患。 例如,与用户相关联的安全令牌应该是不可变的,否则恶意程序可以轻松地更改与该令牌相关联的用户。

这里的主要思想是通过使用不可变对象使您的类线程安全。 我认为这篇文章对此很好。

希望这可以帮助!

一个很好的例子是String类:

可以在此处找到不可变对象的原因的一个很好的总结