Java Bean Class的缺点是什么?

Nee*_*ain 7 java

我从Effective Java一书中读到了下面提到的这两个陈述

1

不幸的是,JavaBeans模式本身就存在严重的缺点.因为构造是跨多个调用分开的,所以JavaBean可能在其构造的中途处于不一致状态.类只能通过检查构造函数参数的有效性来强制执行一致性.尝试在对象处于不一致状态时使用该对象可能会导致与包含该错误的代码相差甚远的失败,因此难以调试.

第2

相关的缺点是JavaBeans模式排除了使类不可变的可能性(第15项),并且需要程序员的额外努力来确保线程安全.可以通过在物体构造完成时手动"冷冻"物体并且不允许其在冷冻之前使用来减少这些缺点,但是这种变型是笨重的并且在实践中很少使用.此外,它可能会在运行时导致错误,因为编译器无法确保程序员在使用它之前调用对象上的冻结方法.

,我无法理解这两个陈述究竟要传达的内容,你们能帮助我理解上述陈述吗?

更新

我在这篇文章中已经阅读了答案(不是全部),大多数社区成员都建议我使用,Constructor Pattern但在同一本书中这些内容已经说过了

静态工厂和构造函数共享一个限制:它们不能很好地扩展到大量可选参数.考虑表示包装食品上出现的营养成分标签的类别的情况.这些标签有一些必需的字段 - 份量,每个容器的份量和每份的卡路里 - 以及超过20个可选字段 - 总脂肪,饱和脂肪,反式脂肪,胆固醇,钠等.大多数产品只有少数这些可选字段具有非零值.

对于这个场景,我们使用telescoping构造函数模式

伸缩构造函数模式有效,但是当有很多参数时很难编写客户端代码,并且仍然难以阅读它.读者会想知道所有这些值是什么意思,必须仔细计算参数才能找到.相同类型参数的长序列可能会导致细微的错误.如果客户端意外地反转了两个这样的参数,编译器就不会抱怨,但程序在运行时会出错

这就是为什么suugested使用JavaBeans结束constructor pattern

Ale*_*exR 14

让我们看看最简单的Java Bean:

class Person {
   private String firstName;
   private String lastName;
   public String getFirstName() {return firstName;}
   public void setFirstName(String firstName) {this.firstName = firstName;}
   public String getLastName() {return lastName;}
   public void setLastName(String lastName) {this.lastName = lastName;}
}
Run Code Online (Sandbox Code Playgroud)

以下代码创建Person一个启动它的实例:

Person president = new Person();
p.setFirstName("George");
p.setLastName("Bush");
Run Code Online (Sandbox Code Playgroud)

如你看到的:

  1. 初始化确实分为3行.这意味着当完成所有3行并且在此之前处于不一致状态时,对象处于恒定状态.
    1. 该对象确实是可变的:它调用的值可以通过调用setter来改变.

为什么这么糟糕?因为我们的类Person不是线程安全的,因此我们不能在多线程环境中直接使用它而不考虑同步化.

这是一个例子.几年前,巴拉克奥巴马成为美国总统.我们怎样才能在代码中表达这一点?

p.setFirstName("Barak");
p.setLastName("Obama");
Run Code Online (Sandbox Code Playgroud)

在mutlti线程envronent中,当president对象setFristName()已经完成并且setLastName()尚未调用时,该对象处于错误状态,因为该对象包含明显错误的"Barak Bush".

解决办法是什么?让我们`人不可变:

class Person {
   private final String firstName;
   private final String lastName;
   Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
   }

   public String getFirstName() {return firstName;}
   public String getLastName() {return lastName;}
}
Run Code Online (Sandbox Code Playgroud)

如您所见,无法更改存储在对象中的名字或姓氏.字段a final和没有setter.所以,我们的例子如下:

人总统=新人("乔治","布什"); //选举.....总统=新人("巴拉克","奥巴马");

由于Person是不可变的,我们无法重用旧的instanceo Person并更改其属性.我们必须改为创建新实例.如果presidentvolatitle引用赋值是原子,因此代码是线程安全的.

更新

构造函数的问题在于它们不灵活.我们的例子只有2个参数.但是当课程Person大概有20个或更多的领域时,想想现实世界.在这种情况下,创建这样的对象非常冗长.

此外,一些字段可以是可选的.在这种情况下,您可能希望创建具有不同数量参数的多个重载构造函数.为了避免重复的assignemnt代码,通常的技术是使用所谓的伸缩构造函数,即构造函数调用其他构造函数时的模式.这种模式很好,但有些过于冗长,难以修改.

这只意味着没有理想的解决方案.每种解决方案都有自己的优点和缺点.

BTW结合了不可变对象的优点和对象创建和初始化的灵活性的解决方案是构建器模式.

我不会写出更好地理解这些问题所需的所有示例.但我希望我的回答对你有所帮助.现在您有了一个起点,可以使用其他资源来学习这个问题.祝好运.