初始化
根据经验,请尝试在声明变量时对其进行初始化。
如果变量的值永远不会改变,请使用 final 关键字明确表示。这可以帮助您推断代码的正确性,虽然我不知道可以识别 final 关键字的编译器或 JVM 优化,但它们肯定是可能的。
当然,这条规则也有例外。例如,可以在 if-else 或 switch 中分配变量。在这种情况下,“空白”声明(没有初始化的声明)优于保证在读取虚拟值之前被覆盖的初始化。
/* DON'T DO THIS! */
Color color = null;
switch(colorCode) {
case RED: color = new Color("crimson"); break;
case GREEN: color = new Color("lime"); break;
case BLUE: color = new Color("azure"); break;
}
color.fill(widget);
如果出现无法识别的颜色代码,您现在有一个NullPointerException。最好不要分配无意义的null。编译器会在调用color.fill() 时产生错误,因为它会检测到您可能没有初始化color。
为了在这种情况下回答您的问题,我必须查看相关代码。如果解决方案在 run() 方法中对其进行了初始化,则它必须被用作临时存储,或者作为“返回”任务结果的一种方式。
如果集合用作临时存储,并且在方法之外无法访问,则应将其声明为局部变量,而不是实例变量,并且很可能应在方法中声明的位置对其进行初始化。
并发问题
对于初级编程课程,您的讲师可能不会试图让您面对并发编程的复杂性——尽管如果是这样的话,我不确定您为什么使用Thread。但是,随着 CPU 设计的当前趋势,任何学习编程的人都需要牢牢掌握并发性。我会在这里尝试更深入地研究。
从线程的run 方法返回结果有点棘手。这个方法就是Runnable接口,没有什么能阻止多个线程执行单个实例的run方法。由此产生的并发问题是 Java 5 中引入的Callable 接口背后的动机的一部分。它很像Runnable,但可以以线程安全的方式返回结果,如果任务可以则抛出Exception不会被执行。
有点题外话,但如果你好奇,可以考虑以下示例:
class Oops extends Thread { /* Note that thread implements "Runnable" */
private int counter = 0;
private Collection<Integer> state = ...;
public void run() {
state.add(counter);
counter++;
}
public static void main(String... argv) throws Exception {
Oops oops = new Oops();
oops.start();
Thread t2 = new Thread(oops); /* Now pass the same Runnable to a new Thread. */
t2.start(); /* Execute the "run" method of the same instance again. */
...
}
}
在main 方法结束时,您几乎不知道Collection 的“状态”是什么。两个线程同时处理它,我们还没有指定集合是否可以安全地并发使用。如果我们在线程内部初始化它,至少我们可以说最终state会包含一个元素,但我们不能说它是0还是1。