本问题已经有最佳答案,请猛点这里访问。
我找到了一篇有趣的代码文章:
public class Employee {
private String firstName;
private String lastName;
//private default constructor
private Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public static Employee valueOf (String firstName, String lastName) {
return new Employee(firstName, lastName);
}
}
我真的很好奇理解创建此类的好处。 我知道这里的此类对象是不可变的,因为一旦初始化就无法更改其变量值。 我以前从未做过这样的事情,而且我真的不了解它的优势。
为什么是个好习惯?
您能说出可以使用这种方法的情况吗?
常量或只读变量呢? 这不是很相似吗?
在文章中说,这对应用程序的性能不利。 但为什么?
可变状态使得很难推断代码的作用。 查看函数式编程。
此类没有访问器方法。 那是故意的吗?
有用的文章,如果您还没有阅读的话:javapractices.com/topic/TopicAction.do?Id=29
附带说明:为了使该类不可变,该类必须是最终的; 这些字段也必须是最终字段。
那篇文章太糟糕了! 不要听从它的建议,它所作的许多陈述是完全错误的。 例如,"报价1"是错误的:在现代JVM中,对象实例化非常快。"报价2"指出将实例字段公开是不好的,然后显示了一种"最佳方法",这同样很糟糕:从吸气剂中泄漏出可变对象(工作日数组)。 啊。 我认为该文章中的每个观点至少都有一件事是错的。
您提到的示例是不可变对象。它在编程语言中得到了广泛使用的概念。
从上面的链接报价。优点是
易于构建,测试和使用
自动是线程安全的,并且没有同步问题
不需要复制构造函数
不需要克隆的实现
允许hashCode使用延迟初始化,并缓存其返回值
用作字段时不需要防御性地复制
制作好Map键和Set元素(这些对象在集合中时不得更改状态)
在构造时就建立了其类不变式,因此无需再次检查
总是具有"失败原子性"(约书亚·布洛赫(Joshua Bloch)使用的术语):如果一个不可变的对象-引发异常,则它永远不会处于不希望的状态或不确定的状态
做好列出优势的工作。我只想补充一句,通常不需要以不变的方式实现员工类,因为那时不需要上述任何要点,但是共享的可变状态可以更轻松地使员工的所有视图保持一致,即对员工的任何修改都会引用它的每个人都可以立即看到。如果我们必须创建一个新对象来表示更改后的状态,则旧引用仍会看到旧状态。有时这是所希望的,有时是不希望的,并且更新所有引用以指向新对象是低效且麻烦的。
我不确定"允许hashCode使用延迟初始化并缓存其返回值"。您是说在第一次调用对象的hashCode()方法时计算对象哈希码并将其存储在字段中,然后将该字段返回吗?因为在那种情况下,该对象实际上是不可变的,并且失去了"自动线程安全"属性。多个线程不能同时安全地调用hashCode(),除非它们可以确定该字段先前已初始化(或者除非使用synchronized / volatile / whatnot)。
@Ruakh:是的。我相信即使在那种情况下,也有办法使它成为多线程安全的-例如,如果您可以自动确定尚未计算出哈希码,然后在初始化哈希码的同时计算并返回正确的值,那么您知道对于本地执行线程,正确的值始终是返回的值。您可能会两次计算该值,但我认为您仍将始终获得正确的值。我希望我能方便地使用" Java Concurrency in Practice",以便我可以查找详细信息(并确保Im正确)。
@jprete:当然有很多方法,但是它们需要明确的动作;它与真正不变的对象所提供的" automatic []线程安全[ty]"完全不同。
@jprete:无法自动确定是否未计算哈希码,如果没有,则无法进行计算。正如ruakh所暗示的那样,如果对象最初确实是不可变的,则没有理由不能在构造函数中计算哈希码。
不变类是:
默认情况下是线程安全的(永远不会发生并发写入)
可骑
您可以在《有效Java》中Java语言的扩展中阅读很多有关它们的信息。
equality check can be done with ==不正确。
仅仅因为类是不可变的,并不意味着可以使用" =="来检查相等性。例如,字符串是不可变的,并且在许多情况下," =="将不起作用,而.equals()会起作用。为了使它起作用,需要将其与扩展工厂结合使用,以确保"相等"值的实例不超过一个。
好的,我删除了它,但是如果我对类拥有完全的实例控制,则可以将它们与==进行比较,这意味着ziesemer编写了什么。
并发写入将如何发生?该示例未提供创建对象后回写对象的任何方法。
-Why is it a good practice?
因为您可以传递该类,并确保它永远不会被"恶意"代码修改。 Java字符串也一样,它们是不可变的。
-Could you name a situation where this approach can be used?
在许多团队合作的大型项目中,或者在设计框架或API时,它非常有用。在这种情况下,由于您不对代码的某些部分负责,因此您永远无法相信传递给代码的其他部分的对象不会被更改。如果需要确保不会修改对象,请使用不变性。
-What about constants or read only variables? Is not that very similar?
Java中没有,因为我们既没有const也没有只读。我们所拥有的只是final关键字,可确保除了第一次分配外,不会修改对象引用。但是,即使引用不能,也可以修改基础对象。不可变的类确保对象状态在创建后不会更改。
-In the article says, that this is not good for the performance of the application. But why?
因为每次需要修改对象时,都需要创建新实例。与字符串相同,您不能执行myString.append("42"),而需要执行myString = myString+"42",这将创建一个新的String对象。
除了关于性能的评论外,我几乎投票赞成,因为它们是错误的。是的,使用不可变的对象意味着您要分配更多的对象,但是在现代JVM实现中,这应该很快。
是的,分配并不是那么昂贵,但是new运算符不仅分配一个对象,它还调用构造函数。在上面的示例中,字符串串联涉及复制整个字符串的内容,这对于大型或频繁复制的字符串可能会变得昂贵。
分配不可变对象的新副本的成本部分(或完全!)由以下事实弥补:您不再需要制作防御性副本。我的经验是,有价值对象的防御性复制比修改要频繁得多,因此,如果对象是不可变的,则实际上您制作的副本较少。
对于价值对象,我完全同意-但Employee不是价值对象。
不变类的主要优点是线程安全。线程的大多数问题来自共享的可变状态。通过使对象不可变,特别是在多线程环境中,对它们进行推理就容易得多。
文章说:"创建不可变的对象会影响应用程序的性能。"我不确定为什么会这样说。这是完全错误的。不可变对象没有任何内在因素会影响应用程序的性能。
没事吗创建新对象并复制其整个状态,与更新单个字段一样快吗?
如果您使用的是hashTables,则拥有不可变的对象会很好,因为当对象的状态更改时(因为它们不可更改),您不需要重新计算hashCode。
文章说:
To make a class immutable you can define its all constructors private and then create a public static method to initialize and object and return it.
实际上,这是错误的。这两个概念并不真正相关。
例如。您可以将Employee类的构造函数声明为public,但它仍然是不可变的。
或者,您可以将可变对象作为参数传递给工厂方法,或者声明一个可变器方法
->尽管您使用的是工厂方法和私有构造函数,但Employee还是可变的。
在您给出的示例中,他将构造函数设为私有,从而直接从外部控制对象的创建。
含义:由于构造函数是私有的,因此您不能做
Employee e = new Employee("steve","jobs");
从此类之外。
这样,此类的程序员将针对该类的对象创建带入他的控件中。
当您编写非常庞大的服务器端类时,这是非常有益的,由于对象的大小,创建对象可能会占用大量内存。 现在如何保护您的客户,避免为您的班级创建更多对象?
上面的问题的答案很简单,只需将构造函数设为私有,就可以在需要使用静态方法时为类创建对象。
注意:静态方法可以通过使用类名直接访问。
注意:这种设计模式将在单例设计模式中大量使用,对于给定的类,该模式仅需要一个对象。