java对象类型中的正确封装是什么
java中下面两个类的正确封装是什么?我在许多代码中看到了这两种方法(主要是第一种方法)。但第二种方法似乎是正确的java对象类型中的正确封装是什么,java,oop,object,encapsulation,Java,Oop,Object,Encapsulation,java中下面两个类的正确封装是什么?我在许多代码中看到了这两种方法(主要是第一种方法)。但第二种方法似乎是正确的 import java.util.Date; public class SomeClass { private Date date; public Date getDate() { return date; } public void setDate(Date date) { this.date
import java.util.Date;
public class SomeClass
{
private Date date;
public Date getDate()
{
return date;
}
public void setDate(Date date)
{
this.date = date;
}
}
或
这取决于使用
SomeClass
的代码。如果您计划将此类包含在第三方使用的库中,或者您的代码交互/运行您不控制的代码,那么您绝对需要后一种形式的封装
即使您不处于如此困难的境地,也值得避免设计错误,即可变的Date
类,并返回一个新的Date
实例。这就是我知道的所有IDE都将此标记为警告的原因
虽然这对我来说通常会导致这样的结果:
public class MyClass {
private Date myDate;
public Date getDate() {
return myDate != null ? new Date(myDate.getTime()) : null;
}
public void setDate(Date date) {
myDate = (date != null ? new Date(date.getTime()) : null);
}
}
因此,我想使用第二种变体或切换到Jodas。第一种!
第二个将使用clone()
为每个getDate()
调用创建一个新对象,根据您的应用程序,它可能会导致尴尬。(即,如果要使用aSomeDate.getDate().aMethod()调用Date
方法)
了解我蹩脚英语的一个小例子;)
不是针对此特定示例,而是针对一般方法:
为用户提供一种将返回的对象强制转换到他想要的任何其他兼容类中的方法总是很好的
因此,第一个方法看起来不错,因为如果您返回的对象已被其他类扩展,那么很容易将其转换为该类型,而不是提供固定类型的对象
因此,通过这种方式,您可以提供一个更通用的对象,或者我可以说抽象
并鼓励多态性这是一个安全/封装偏好的问题,最基本的封装是您发布的第一种方法,第二种是更高级的封装方式这还可以通过克隆类来保护传递给类的对象。
考虑以下几点:
public class SomeData {
private final Point value;
public SomeData (final Point value) {
this.value = value;
}
public Point getValue( ) {
return value;
}
}
现在,上面的代码片段看起来是不可变的(与您的示例类似),但是其中有一个漏洞。查看下面的代码片段
public final static void main(final String[] args) {
Point position = new Point(25, 3);
SomeData data = new SomeData(position);
position.x = 22;
System.out.println(data.getValue( ));
}
因为我们只传递位置变量的引用,所以我们仍然能够改变它的状态。克隆它将有助于保护可变位置:
如果我们将声明更改为:
public SomeData (final Point value) {
this.value = value;
}
到(类似于您的克隆)
当我们再次调用main方法时:
Point position = new Point(25, 3);
SomeData data = new SomeData(position);
position.x = 22;
无论我们对位置做了什么,数据
对象都将保持不变。我希望你明白为什么那里有克隆。克隆不是一个好主意,因为-
通常,clone
方法,创建同一类的新实例,并将所有字段复制到新实例,并返回它=shallow copyObject
类提供了一个克隆方法,并支持浅拷贝
。它将“Object”作为类型返回,您需要显式地回溯到原始对象
当您实现深度复制时,要小心,因为您可能会陷入循环依赖关系中
克隆不适用于实例化
和初始化
。不应将其视为创建新对象。因为克隆对象的构造函数可能永远不会在过程中被调用
4.还有一个缺点(以及其他许多缺点:):克隆阻止使用final
田地
五,。在单例模式中,如果超类
实现了public clone()
方法,以防止子类使用该类的clone()
方法获取副本,则覆盖它并抛出CloneNotSupportedException
因此,方法1比方法2更好
这取决于您获得/设置的字段类型是否为,也就是说,它们在构建之后是否可以修改
整个Getter/Setter范式背后的要点是,实例的私有/受保护字段不能被任何外部类任意修改(或获得访问权)
因此,在第一个示例中,类可以获取对私有日期字段的引用,然后(因为Date
不是不可变的)使用Date的setTime(long)
方法修改日期,有效地绕过SomeClass
Setter方法(以及它可能产生的任何副作用,例如执行验证、更新GUI元素等)。
在第二个示例中,这无法实现,因为外部类只会获取实际日期的克隆,因此在此之后所做的任何修改都不会影响SomeClass
的原始私有日期字段
底线:
这完全取决于您的私有/受保护字段的类型以及您试图实现/阻止的内容
要记住的要点:
- 不会总是返回对象的深度克隆(特别是对于复杂对象,其字段引用其他可变对象等)。因此必须小心使用(并了解其内部工作原理)
- 基本数据类型和字符串是不可变的,因此在获取/设置这些类型的字段时不需要
clone()
ing
封装的两个基本特征是:
- 保护实例变量(使用访问修饰符,通常是私有的)
- 公开访问器方法,并强制调用代码使用这些方法,而不是直接访问实例变量
当可变对象被传递到类中的constructor
和set
方法或get
方法之外时,创建可变对象始终是一种很好的做法。如果不这样做,则调用方很容易中断
public SomeData (final Point value) {
this.value = value;
}
public SomeBetterData(final Point value) {
this.value = new Point(value);
}
Point position = new Point(25, 3);
SomeData data = new SomeData(position);
position.x = 22;
import java.util.Date;
public class SomeClass
{
private Date date;
public Date getDate()
{
return new Date(date.getTime());
}
public void setDate(Date date)
{
this.date = new Date(date.getTime());
}
}
public class Period {
private Date first, second;
public Period(Date first, Date second) {
if(first.compareTo(second) > 0)
throw new IllegalArgumentException("first > second");
this.first = first;
this.second = second;
}
public Date getFirst() {
return first;
}
public Date getSecond() {
return second;
}
}
Date d1 = new Date(), d2 = new Date();
Period p = new Period(d1, d2) // no exception
d1.setYear(98); // broken period precondition
this.first = new Date(first.getTime());
return new Date(first.getTime()); // here you may use clone....