Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
java对象类型中的正确封装是什么_Java_Oop_Object_Encapsulation - Fatal编程技术网

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

java中下面两个类的正确封装是什么?我在许多代码中看到了这两种方法(主要是第一种方法)。但第二种方法似乎是正确的

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 copy
    Object
    类提供了一个克隆方法,并支持
    浅拷贝
    。它将“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....