当前位置: 首页 > 面试题库 >

什么是Java中的类不变式?

公良理
2023-03-14
问题内容

我搜索了该主题,但是除了Wikipedia之外,我没有找到任何其他有用的文档或文章。

有人可以用简单的词向我解释这意味着什么,还是可以使我参考一些易于理解的好文档?


问题答案:

对于Java而言,这并不意味着什么。

类不变式只是一个属性,它始终为一个类的所有实例保存,无论其他代码做什么。

例如,

class X {
  final Y y = new Y();
}

X具有不变的类,即存在一个y属性,并且永远不存在,null并且它具有type值Y

class Counter {
  private int x;

  public int count() { return x++; }
}

无法保持两个重要的不变式

  1. count永远不会返回,因为可能下溢负值。
  2. 要求count严格单调增加。

修改后的类保留了这两个不变式。

class Counter {
  private int x;

  public synchronized int count() {
    if (x == Integer.MAX_VALUE) { throw new IllegalStateException(); }
    return x++;
  }
}

但无法保留count始终正常成功调用的不变式(缺少TCB-violations
†),因为count如果死锁线程拥有计数器的监视器,它可能会引发异常或阻塞。

每种带有类的语言都易于维护某些类不变式,而不能维护其他不变式。Java也不例外:

  1. Java类始终具有或不具有属性和方法,因此接口不变式易于维护。
  2. Java类可以保护其private字段,因此依赖私有数据的不变量易于维护。
  3. Java类可以是最终的,因此可以维护不依赖通过编写恶意子类来违反不变量的代码的不变量。
  4. Java允许null值以多种方式潜行,因此很难维护“具有真实值”的不变式。
  5. Java具有线程,这意味着不同步的类很难维护依赖于同时发生的线程中顺序操作的不变式。
  6. Java有一些异常,可以轻松维护诸如“返回带有属性p的结果或不返回结果”之类的不变量,而维护诸如“始终返回结果”之类的不变性则更加困难。

†- 外部性违反 TCB
是系统设计人员乐观地认为不会发生的事件。

通常,我们只相信基本硬件能够像在其上构建的高级语言的属性那样进行宣传,而我们不变式的论点并未考虑以下可能性:

  • 当程序运行时,使用调试钩子来更改局部变量的程序员以代码无法运行的方式运行。
  • 您的对等方不使用反射setAccessible修改private查找表。
  • Loki改变物理结构会导致处理器错误地比较两个数字。

对于某些系统,我们的TCB可能仅包括系统的一部分,因此我们可能不会假定

  • 管理员或特权守护程序不会终止我们的JVM进程,

但是我们可以假设

  • 我们可以检查点到可靠的事务性文件系统。

系统级别越高,通常其TCB越大,但是从TCB中获得的不可靠信息越多,不变量保持的可能性就越大,从长远来看,系统越可靠。



 类似资料:
  • 问题内容: 这可能是有史以来最愚蠢的问题,但我认为对于Java新手来说,这非常令人困惑。 有人可以澄清什么是不变的吗? 为什么是String一成不变的? 不可变对象的优点/缺点是什么? 为什么诸如StringBuilderString之类的可变对象优先于String,反之亦然? 一个很好的例子(在Java中)将不胜感激。 问题答案: 不可变是指一旦对象的构造函数完成执行,该实例将无法更改。 这很有

  • 问题内容: 我知道适用于一般不可变类的常见原因,即 不能改变为副作用 容易推断他们的状态 本质上是线程安全的 无需提供克隆/复制构造函数/工厂复制方法 实例缓存 无需防御副本。 但是,包装器类表示原始类型,并且原始类型是可变的。那么为什么包装器类不可变? 问题答案: 但是,包装器类表示原始类型,并且原始类型(String除外)是可变的。 首先,String不是原始类型。 其次,谈论原始类型是可变的

  • 问题内容: 我知道整数在Java中是不可变的。但是为什么要这样设计呢? 我找不到强制Integer不可变的用例。是否有类似String的技术原因? 字符串在网络连接,数据库URL等中用作参数。如果它是可变的,则很容易遭到破坏。 支持StringPool功能。 支持使用字符串作为参数的类加载机制。字符串可变会导致加载错误的类。 我知道有些包装很易变。 更新: 从对话中,并没有普遍的理由要求整数是不可

  • 问题内容: 有人问我为什么String是不可变的 我这样回答: 当我们在Java中像这样创建一个字符串时,;将在字符串pool(hello)中创建一个对象,并且s1将指向hello。现在再次执行该操作;将不会创建另一个对象,但是s2将指向该对象,hello 因为JVM将首先检查如果在字符串池中是否存在相同的对象, 如果不存在则仅创建一个新对象。 现在,如果假设Java允许串可变那么如果我们改变S1

  • 问题内容: 我正在阅读我的Deitel《 Java How to Program》一书,并遇到了“ 阴影 ”一词。如果允许阴影,那么Java类中有什么情况或目的? 例: 问题答案: 阴影的基本目的是使本地代码与周围的类分离。如果不可用,请考虑以下情况。 API中的Class Foo已发布。在代码中将其子类化,并在子类中使用名为bar的变量。然后,Foo发布更新,并在其类中添加一个称为Bar的受保护

  • 问题内容: 本地类的Java文档说: 此外,局部类可以访问局部变量。但是,局部类只能访问声明为final的局部变量。当局部类访问封闭块的局部变量或参数时,它将捕获该变量或参数。例如,PhoneNumber构造函数可以访问局部变量numberLength,因为它被声明为final。numberLength是捕获的变量。 捕获的变量是什么,其用途是什么,为什么需要它?请帮助我理解它的概念。 问题答案: