我正在查看一些视图代码,并注意到以下内容:
public class FooPanel extends JPanel {
private JCheckBox checkBox = null;
private JComboBox comboBox = null;
...
protected void initView() {
checkBox = new JCheckBox();
comboBox = new JComboBox();
}
}
是否存在与将字段初始化为null相关的任何问题,即使它们是null而你没有声明它?
我看到的一件事是,如果按如下方式初始化为null,则会创建2个对象实例:
public class FooPanel extends JPanel {
private JPanel innerPanel = null;
...
protected void initView() {
add.(getInnerPanel());
}
private JPanel getInnerPanel() {
if(innerPanel == null) {
innerPanel = new JPanel();
}
return innerPanel;
}
}
在这种情况下,我得到2个内部面板。那么,惯例是什么?你应该为面板中的每个小部件编写一个getWidget()吗?我认为这些是由Eclipse中的WindowsBuilder插件生成的。
谢谢!
答案 0 :(得分:0)
是否将字段分配给null
或保持不变是没有区别的。这两种情况编译成完全相同的东西(参见字节码答案的底部)。
至于正在创建的2个实例:我不知道如何创建两个实例。如果尚未设置innerPanel
且null
,则构建并存储JPanel
。如果确实存在,则返回时不触及任何内容。无论你做什么都是你的选择。
字节码:
$ cat TestNull.java
public class TestNull {
public Object obj = null;
}
$ cat TestBlank.java
public class TestBlank {
public Object obj;
}
$ javap -c TestNull
Compiled from "TestNull.java"
public class TestNull {
public java.lang.Object obj;
public TestNull();
Code:
0: aload_0
1: invokespecial #1 // super()
4: aload_0
5: aconst_null
6: putfield #2 // obj = null
9: return
}
$ javap -c TestBlank
Compiled from "TestBlank.java"
public class TestBlank {
public java.lang.Object obj;
public TestBlank();
Code:
0: aload_0
1: invokespecial #1 // super()
4: aload_0
5: aconst_null
6: putfield #2 // obj = null
9: return
}
答案 1 :(得分:0)
null
:在声明期间(或者在声明之外的其他时间),将值null
分配给成员并不会产生任何问题。这种类型的赋值的一个优点是它使代码显式化,并使得您的意图对于稍后阅读代码的任何人都非常清楚。如果在声明期间没有为成员变量赋值,则不清楚作者是将该值设为null
还是忘记编写赋值代码。
innerPanel
个实例初始分配给null
,然后在需要时分配值(在代码中完成),不会导致创建2个实例。从代码中确切地说明您的getInnerPanel
方法是如何使用的,但可能运行到多线程场景中,其中2个线程可能正在调用{{ 1}}大约在同一时间。
在这种情况下,您最终可能会有多个getInnerPanel
成员分配(以及重复构造)。第一个线程可以检查innerPanel
的值,发现它当前是innerPanel
(从而输入null
条件中的关联代码),然后运行{{ 1}}构造和赋值代码。如果不同的线程运行相同的代码,大约在同一时间,第二个线程可能执行相同的if
值检查,发现该值仍然是JPanel
(因为{{1}中的代码条件尚未执行构造和赋值),并输入相同的构造和赋值代码,导致多个构造出现。
您可以在innerPanel
方法中设置null
访问if
,以防止这种情况发生:
innerPanel
synchronized
第二个选项是更好的选项,因为它减少了getInnerPanel
代码的范围,但不是很大。如果您希望多次调用private synchronized JPanel getInnerPanel() {
if(innerPanel == null) {
innerPanel = new JPanel();
}
return innerPanel;
}
方法,则所显示的选项都不是最佳选项,因为调用该方法的每个线程都会因同步和private JPanel getInnerPanel() {
synchronized(innerPanel) {
if(innerPanel == null) {
innerPanel = new JPanel();
}
}
return innerPanel;
}
检查而变慢
在您的示例中,关键权衡以synchronized
成员为中心。如果您知道将始终创建getInnerPanel
或几乎总是创建,则最好删除延迟初始化(在需要时构造null
的地方),只需构造{ {1}} innerPanel
声明和分配的一部分,这也将消除innerPanel
方法中JPanel
检查的必要性。预先JPanel
构造还消除了对innerPanel
检查的任何需求,null
方法可以简单地返回对getInnerPanel
成员的引用。
另一方面,如果你确实有一些可能构建或不构造的面板,根据需要,你实现的延迟初始化非常有意义。
对每个面板使用get方法是有意义的,如果您觉得它使代码更清晰,更容易使用,特别是如果它是您计划在整个项目中实现的模式。也就是说,应检查相关的延迟初始化和相关的JPanel
代码块(基于上述权衡考虑因素),并以最符合您认为需要面板的方式实现和构造。