在paint()方法末尾调用frame.repaint()是否危险?为何未出现栈溢出?
关于Swing中paint()与repaint()调用的两个疑问解答
1. 在paint()方法末尾调用frame.repaint()是否存在风险?
当然有风险,而且是挺明显的性能和稳定性问题,主要体现在这几点:
- CPU资源被疯狂占用:每次
paint()执行完就立刻请求重绘,相当于让Event Dispatch Thread(EDT)(事件调度线程)一直循环处理绘制任务。如果你的绘制逻辑复杂一点,比如要画大量图形、加载图片,CPU使用率会直接拉满,界面轻则卡顿,重则直接失去响应。 - 事件队列被挤爆:Swing的所有UI事件(包括用户点击、键盘输入)都是在EDT里处理的。频繁的
repaint()请求会把事件队列塞满,其他正常的用户操作事件根本排不上队,用户点按钮、输入文字都没反应,体验极差。 - 可能出现绘制异常:如果你的
paint()里依赖某些动态状态(比如动画的帧计数),反复无限制的重绘可能导致状态逻辑混乱,比如动画帧跳变、视觉闪烁,或者一些绘制元素出现不可控的偏移。
2. 既然repaint()会调用paint(),那么在paint()方法末尾调用repaint()是否会引发无限递归?若确实如此,为何未出现栈溢出错误?
这问题问到点子上了!很多人会误以为这是递归,但其实根本不是栈上的无限递归,核心原因在于Swing的重绘机制:
repaint()并不是直接调用paint()!它只是向EDT发送一个重绘请求,这个请求会被加入到EDT的事件队列里,等待EDT空闲时去处理。- 当前的
paint()方法是在EDT处理某个重绘请求时执行的,当你在paint()末尾调用repaint(),只是把一个新的重绘请求丢进队列,当前的paint()会正常执行完毕,栈也会正常弹出。等EDT处理完当前的paint()后,会从队列里取出下一个重绘请求,再调用paint()——这是循环调用,不是栈递归,所以栈不会一直叠加,自然不会出现栈溢出。
不过虽然不会栈溢出,但这种做法会让EDT陷入无限处理重绘请求的死循环,其他UI事件完全得不到处理,界面相当于卡死了,本质上还是非常糟糕的写法。
内容的提问来源于stack exchange,提问作者ahab
相关产品推荐
相关产品推荐

