可变或不可变的类?

时间:2009-08-16 16:44:09

标签: java scalability immutability

我在一些设计书中读到,不可变类提高了可伸缩性,并且尽可能地编写不可变类的良好实践。但我认为如此不可改变的阶级会增加对象的扩散。因此,为了提高可伸缩性,使用静态类(具有静态的所有方法的类)是不是更好的可扩展性?

8 个答案:

答案 0 :(得分:12)

immutable类的主要好处是,您可以公开不可变的内部数据成员,因为调用者无法修改它们。这是一个很大的问题,比如java.util.Date。它是可变的,因此您无法直接从方法返回它。这意味着你最终会做各种defensive copying。这增加了对象的扩散。

另一个主要好处是根据定义,不可变对象没有synchronization个问题。这就是scalability问题的来源。编写multithreaded代码很难。不可变对象是(主要)绕过问题的好方法。

至于“静态类”,通过你的评论,我认为它是指factory methods的类,这是它通常描述的方式。那是一种无关的模式。可变类和不可变类都可以具有公共构造函数或具有静态工厂方法的私有构造函数。这对类的(im)可变性没有影响,因为可变类是在创建后可以更改状态的类,而在实例化后不能更改不可变类的状态。

静态工厂方法可以有其他好处。我们的想法是封装对象创建。

答案 1 :(得分:5)

不可变类确实会促进对象扩散,但是如果你想要安全,可变对象会促进更多对象扩散,因为你必须返回副本而不是原始副本以防止用户更改你返回的对象

至于使用所有静态方法的类,在大多数可以使用不变性的情况下,这不是一个真正的选择。从RPG中获取此示例:

public class Weapon
{
    final private int attackBonus;
    final private int accuracyBonus;
    final private int range;

    public Weapon(int attackBonus, int accuracyBonus, int range)
    {
        this.attackBonus = attackBonus;
        this.accuracyBonus = accuracyBonus;
        this.range = range;
    }

    public int getAttackBonus() { return this.attackBonus; }
    public int getAccuracyBonus() { return this.accuracyBonus; }
    public int getRange() { return this.range; }
}

如何使用仅包含静态方法的类实现此目的?

答案 2 :(得分:1)

正如 cletus 所说,不可变类简化了同步方法中的类设计和处理。

它们还简化了集合中的处理,即使在单线程应用程序中也是如此。不可变类永远不会改变,因此密钥和哈希码不会改变,所以你不会搞砸你的集合。

但是你应该记住你正在建模的东西的生命周期和构造函数的“重量”。如果您需要更改该东西,则不可变对象变得更加复杂。您必须替换它们,而不是修改它们。并不可怕,但值得考虑。如果构造函数需要非常重要的时间,那也是一个因素。

答案 3 :(得分:1)

要考虑的一件事:如果您打算将类的实例用作HashMap中的键,或者如果您要将它们放在HashSet中,那么使它们成为不可变的更安全。

HashMap和HashSet指的是只要对象在map或set中,对象的哈希码就保持不变。如果你将一个对象用作HashMap中的一个键,或者你把它放在一个HashSet中,然后改变对象的状态,以便hashCode()返回一个不同的值,那么你就会混淆HashMap或HashSet,你会得到奇怪的东西;例如,当您迭代地图或设置对象时,但当您尝试获取它时,就好像它不存在一样。

这是由于HashMap和HashSet如何在内部工作 - 它们通过哈希码组织对象。

Java并发大师Brian Goetz的

This article概述了不可变对象的优缺点。

答案 4 :(得分:0)

不变性通常用于实现可伸缩性,因为不可变性是Java中并发编程的推动因素之一。因此,正如您所指出的那样,“不可变”解决方案中可能存在更多对象,这可能是提高并发性的必要步骤。

另一个同样重要的用途og不变性就是消耗设计意图;制作不可变类的人意图将其置于其他地方。如果你开始改变那个类的实例,你可能会破坏设计的初衷 - 谁知道后果可能是什么。

答案 5 :(得分:0)

考虑字符串对象,作为示例。有些语言或类库提供可变字符串,有些则不提供。

使用不可变字符串的系统可以进行某些优化,使用可变字符串的系统不能。例如,您可以确保只有任何唯一字符串的副本。由于对象“开销”的大小通常远小于任何非平凡字符串的大小,因此这可能节省大量内存。还有其他潜在的空间节省,例如实习子串。

除了可能节省的内存之外,不可变对象还可以通过减少争用来提高可伸缩性。如果您有大量线程访问相同的数据,则不可变对象不需要精心设计的同步过程来进行安全访问。

答案 6 :(得分:0)

再考虑一下这个问题。使用不可变对象允许您缓存它们而不是每次都重新创建它们(即字符串)它对您的应用程序性能有很大帮助。

答案 7 :(得分:0)

我认为,如果你想在不同的变量之间共享同一个对象,它需要是不可变的。

例如:

String A = "abc";
String B = "abc";

Java中的String对象是不可变的。现在A& B指向相同的“abc”字符串。 现在

A = A + "123";
System.out.println(B);

它应输出:

abc

因为String是不可变的,所以A只会指向新的“abc123”字符串对象,而不是修改前一个字符串对象。