以最简单的形式,以下是设计:
class Session {
Timer t = new Timer();
// ...
};
每当分配Session
时,我会在其中启动一个计时器;计时器将在10-20分钟后过期。现在,假设Session
在计时器到期之前被销毁;那么这是我必须停止计时器的场景。我不知道在Session
被销毁时是否有总是被调用的最后一个方法。
在Java中是否存在某种类似的C ++析构函数,当cancel()
被销毁时,它可以帮助我Session
计时器? (无需等待GC)
编辑:请不要重新加入C ++。我想要一些相同的东西。 Session
是一个电话会话,当与其连接的所有用户都断开连接时会被销毁。现在,没有最后调用的Session
方法,也没有任何异常。
答案 0 :(得分:7)
不,Java中没有这样的东西。最接近的是写一个终结器,但不能保证它会运行。
会话如何被破坏?如果有一个被调用的方法,那么将代码放在那里。
如何创建会话?如果您在try / catch块中执行此操作,则可以清除finally块中的所有内容。
我会编写一个close()方法来处理它并在finally块中调用它。
答案 1 :(得分:2)
与C ++不同,您无法控制Java中的对象释放 - 也就是说,GC会在其认为合适的时候收集对象,只是保证它不会收集通过给定时间范围内的任何引用可以访问的任何内容。
有Object.finalize()
,它作为最后机会挂钩进行清理,但运行时并不能保证它完全被调用。
我会重新考虑这个设计,并尝试采用更明确的方式来清理你的计时器。
答案 2 :(得分:2)
在Java中,变量不像C ++中那样直接表示对象 - Java中的变量是对象的引用。因此,对象不能超出范围 - 只有引用对象的变量才会超出范围。
您可以覆盖finalize(),它是类Object中的一个方法,当垃圾收集器即将永久丢弃一个对象时,它会被垃圾收集器调用,但这与析构函数不完全相同。请参阅Object类中的finalize()方法的API文档。
答案 3 :(得分:1)
首先,finalize
不等同于析构函数。不要试图将其作为一个整体使用;它不会起作用。 Java没有为此构建任何内容,但在某些圈子中,有一个约定使用void dispose()
来实现此目的;而不是delete ptr;
,你写ptr.dispose()
。 (当然,内存稍后会被回收。)在这种情况下,最好定义finalize
,如果在调用dispose
之前回收对象,则会产生某种内部错误。
答案 4 :(得分:1)
大多数情况下,您可以构建问题,因此您不需要析构函数。
为每个会话创建一个计时器相对较重。最好向ScheduledExecutorService提交延迟任务。通过使其重量轻,不需要取消它。 (事实上,cancel()不会删除它,它被标记为不运行)
在这种情况下,您可以使用单例ScheduledExecutorService来实现简单。
class Session {
private static final ScheduledExecutorService timer =
Executors.newSingleThreadScheduledExecutor();
private Future<Void> timeoutFuture = null;
// call every time you want the timeout to start from now.
public void resetTimer() {
if(timeoutFuture != null) timeoutFuture.cancel(false);
timeoutFuture = timer.schedule(new Callable<Void>() {
public Void call() {
sessionTimedOut();
return null;
}
}, TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
void sessionTimedOut() {
// what to do when the session times out.
}
}
这里是昂贵的组件,ScheduledExecutorService只创建一次并在应用程序的生命周期中存在。清除会话(并且任务已过期)时,将丢弃Future。
答案 5 :(得分:0)
java中没有类似的方法。当对象被垃圾收集器销毁时,可以调用顶层finalize()
中定义的Object
方法,但这不是您可以依赖的行为。
您可以做的最好的事情就是将对象设置为null(删除引用),这将使其为垃圾收集做好准备。
答案 6 :(得分:0)
您可以使用CDI(上下文依赖注入) - 例如Weld或在某些JEE服务器(TomEE,JBoss等)上运行程序。下面是一个使用数据库的例子。
在您的课程中使用适当的 @ ... Scoped (ApplicationScoped,SessionScoped等)注释,例如:。
@ApplicationScoped
public class MyDatabaseFactory implements Serializable {}
使用 @PostConstruct 注释的方法,在构造函数中执行您想要执行的操作:
@PostConstruct
private void initializeObjectsOrConnections() {
// ...
}
使用 @Inject 注释将对象注入其他位置(如果您愿意):
public class MyApplication {
@Inject
MyDatabaseFactory databaseFactory;
// ...
}
清理,销毁对象并断开与数据库的连接 - 您希望在C ++的析构函数中使用MyDatabaseFactory类的 @PreDestroy 注释的方法,例如:
@PreDestroy
private void destroyObjectsOrCloseConnections() {
// ...
}
使用非常简单,我们在Java中使用了与C ++相同的析构函数。