是否可以确保 JVM 中不存在两个相同的对象?

Fai*_*sal 2 java

考虑这个数据库模型:

Book
isbn primary key
title
Run Code Online (Sandbox Code Playgroud)

在 RDBMS 中,数据库确保上述模型不存在两个相同的行。

类似地,在 Java 中考虑这个对象模型:

Book
- isbn: int
- title: String
+ Book(isbn)
Run Code Online (Sandbox Code Playgroud)

假设我们正在创建一个 Book 对象:

Book b = new Book(123456);
Run Code Online (Sandbox Code Playgroud)

后来,在代码的其他部分,我们再次创建一个相同的 Book 对象:

Book c = new Book(123456);
Run Code Online (Sandbox Code Playgroud)

Java 能否确保 JVM 堆中不存在两个相同的对象?就像 RDBMS 一样吗?

Joa*_*uer 6

Java 中没有内置机制可以自动为您执行此操作。你可以为此构建一些东西,但可能不应该。如果您这样做,那么可能不会像您在问题中所显示的那样。

首先:我们假设这些对象是不可变的,因此问题被简化为“不允许构造两个具有相同属性的对象”。这不是必要的限制,但这样我就可以演示这种方法的问题。

第一个问题是它要求您在一个中心位置跟踪程序中的每个实例。通过在构造对象时填充一个集合,Book可以很容易地做到这一点。

然而,这基本上会在您的程序中造成大量内存泄漏,因为如果没有其他东西挂在该Book对象上,该集合仍然会引用它,从而防止它被垃圾收集。

WeakReference您可以通过使用对象来保留您的对象来解决该问题Book

接下来,如果您想避免重复,那么您几乎肯定需要一种方法来获取 a 的“原始”实例(Book如果您无法创建新实例)。如果你只是使用构造函数,你就不能这样做,因为构造函数不能“返回另一个对象”,它总是会创建并返回一个新对象。

所以new Book(12345)你不需要类似的东西BookFactory.getOrCreateBook(12345)。然后,该工厂可以根据需要获取Book具有给定 id 的现有对象创建一个新对象。

使内存泄漏问题更容易处理(并且还可能允许多个并行会话,每个会话都有自己的一唯一Book对象)的一种方法是使BookFactorybe a BookSession:即您实例化一个,它会跟踪书籍。现在这BookSession是所有书籍的“根”,如果它不再被引用,它(以及它创建的所有书籍)可能会被垃圾收集。

所有这些甚至都没有涉及线程安全,对于不可变对象来说,线程安全可以相当容易地解决,但如果您想允许修改同时仍保持唯一性,则可能会变得相当复杂。

一个简单的BookSession可能看起来有点像这样(请注意,我使用 for recordbook 只是为了简化此示例代码,这将使构造函数可见。在“真实”代码中,我将使用等效的普通类,其中构造函数不是其他人可以访问):

record Book(int isbn, String title) {}

class BookSession {
    private final ConcurrentHashMap<Integer, Book> books = new ConcurrentHashMap<>();

    public Optional<Book> get(int isbn) {
        return Optional.ofNullable(books.get(isbn));
    }

    public Book getOrCreate(int isbn, String title) {
        return books.computeIfAbsent(isbn, (i) -> new Book(i, title));
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松地将其他方法添加到会话中(例如findByTitle或类似的方法)。

如果你只想要一个,你甚至可以某个地方,如果你愿意的话(但那时你已经重新创建了内存泄漏)BookSessionpublic static final BookSession BOOKS