在Java中销毁对象

我对垃圾收集器如何在Java中工作有一个大概的想法,但我推销对象的原因不是因为我关心释放内存,而是因为function。 我可以通过一个例子更好地解释:

说我正在制作一个涉及金钱的游戏。 当一个人从地上拿起一个Money对象时,我想调用该对象的addTo方法,该方法涉及向该人的钱包添加一个值。

 public class Money { /** The value of this object. */ private final int value; // constructor public Money(double x) { this.value = x; } // Should be called when a Person picks up this object. public void addTo(Person bob) { bob.addToWallet(this.value); } // MAIN METHOD public static void main(String[] args) { Person bob = new Person(); Money dollar = new Money(1.00); dollar.addTo(bob); // bob finds $1.00 } } 

找到dollar后,我不希望别人能够拿起它。 换句话说,我不希望自己或任何其他程序能够意外地调用该行:

 dollar.addTo(alice); 

因此,在Bob拿到钱之后,它的价值被添加到他的钱包中,并且不再需要该值的对象表示。 这不是我关心对象使用的内存,但我不希望该对象在程序中的其他地方被意外使用。 除了设置dollar = null;之外,我如何销毁dollar dollar = null;

你根本不应该考虑垃圾收集。 由JVM决定什么时候可以进行垃圾收集,99%的时间,如果你负责任地编程,你根本不需要考虑这个问题。

回答我这个问题……“该对象会在程序中的其他地方被意外使用”?

答案是,它不可能。 本地引用一直存在,直到主方法结束,不再存在。 除非您存储对它的引用,否则不能引用它。

随着程序的增长,暂时存储对它的引用可能是个好主意。 你需要用一个物体来代表你的“地面”概念。 由于您描述的function是基本的,因此您可以使用Collection任何内置实现,例如List

 List floorMoney = new LinkedList(); floorMoney.add(new Money(1.00)); 

当你将地板上的钱与一个Person你会把它从地板上移走。

 Money dollar = floorMoney.get(0); // how you access elements may vary floorMoney.remove(dollar); dollar.addTo(bob); 

现在,除非您在addTo实现中的某处存储对dollar的引用,否则Money对象将被放弃并稍后进行GC。

如果你真的想尽可能地模拟现实世界,你可以在美元中添加一个所有者属性,这样当你调用dollar.addTo()时,你将不得不改变所有者,美元只属于一个任何时候的人。

dollar=null将成功。 通过消除对该对象的引用,无法引用该对象。 垃圾收集器在这里不相关。

想一想:如果你不想让别人来你家,你就不必炸毁你的房子。 只是不要给他们地址。 幸运的是,对象无法跟踪其他物体,就像疯狂的前女友可以跟踪那些倾倒她的人……在他的工作之外停车,在任何时间发送文本,打电话给他的父母……

哦,对不起,回到你的问题。

如果地面是某种类型的Collection (例如List ),那么从地面集合中移除这些钱也会使其远离其他玩家或流氓有感知对象。

如果你的钱也是一个具有价值的Object ,在挑选第一个玩家后,你可以将其值设置为-1000,从而惩罚那些明显不属于他们的人。

作为@pstanton所说的扩展,保留自有对象的集合。 这是一种精心设计的方法,可用于防止错误代码破坏您的状态。

 public abstract class Possession { private Object _key = null; public synchronized Object acquire() { if (_key != null) { throw new IllegalStateException(); } _key = new Object(); } public synchronized void release(Object key) { if (_key != key) { throw new IllegalStateException(); } _key = null; } } public class Possessions implements Iterable { private final Map _possessions = new IdentityHashMap<...>(); public synchronized void add(Possession p) { if (!_possessions.containsKey(p)) { _possessions.put(p, p.acquire()); } } public synchronized void remove(Possession p) { Object key = _possessions.remove(p); if (key != null) { p.release(key); } } public Iterator iterator() { return Collections.unmodifiableSet(_possessions.keySet()).iterator(); } } public class Money extends Possession { ... } public class Person { private final Possessions _possessions = new Possessions(); public void add(Money money) { _possessions.add(money); } } 

您无法控制 Java垃圾收集-GC-并且现在可以强制Java虚拟机为您销毁对象。

您可以调用System.gc() [或Runtime.getRuntime().gc() ],但这只是建议JVM运行垃圾收集。 的确如此,JVM不应该为此而监听,并且不能保证GC的执行 。 不要依赖于此。

关于您的设计需求,将您的钱存入银行(或街道,如果您希望人们找到它们)并要求银行给您一个。 假设银行只有1,那么后续的调用将返回null。

在DOWNVOTE之后编辑

我决定从我的书中删除一些灰尘,并从SCJP学习指南第3章的强制垃圾收集部分引用几行。 我为纯粹的受虐狂而研究的一本书。

这里应该提到的第一件事是,与本节的标题相反,不能强制进行垃圾收集。

..it建议你永远不要在你的代码中调用System.gc() – 把它留给JVM。

..it只能向JVM建议它执行垃圾收集。 但是,无法保证JVM实际上会从内存中删除所有未使用的对象(即使运行垃圾收集)。

垃圾收集是一个比看起来更难的概念……

你只需要避免让“美元”可用于你不想去的地方。 请注意,这不是GC问题。 对于非GC环境(如C ++),如果某人看到指向已删除对象的指针,则会在尝试访问它时崩溃并刻录。

为什么你不能添加布尔“挑选”默认值= false? 或创建一个列表并从中删除?

虽然定义所有权(以收集Money ,钱包作为收集,或者所有者属性为金钱)可能在理论上是更清晰的解决方案,但我可以很容易地想象当这是过度杀伤时的情况。

例如,人们拿走他的钱可能完全无关紧要。 一旦获得金钱,游戏就不关心谁拥有这种Money实例

我建议只需添加isPicked属性:

 public class Money { private final int value; private boolean _isPicked = false; public Money(double x) { this.value = x; } public boolean isPicked(){ return _isPicked; } public void addTo(Person bob) { if( this.isPicked ) throw new Exception("some message for developers"); bob.addToWallet(this.value); _isPicked = true; } public static void main(String[] args) { Person bob = new Person(); Money dollar = new Money(1.00); dollar.addTo(bob); // bob finds $1.00 // dollar = null; // don't need this anymore Person alice = new Person(); dollar.addTo( alice ); // <------ RUNTIME EXCEPTION } } 

执行dollar = null也会导致exception,但它不太安全,因为您可能只是忘记这样做,或者意外地将引用复制到另一个变量