考虑这个数据库模型:
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 一样吗?
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
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |