欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入
对分析本文的实例最重要的,用一句话说,就是“父类的构造方法调用发生在子类的变量初始化之前”。可以用下面的例子来证明:
// Petstore.javaclass Animal { Animal() { System.out.println(“Animal”); }}class Cat extends Animal { Cat() { System.out.println(“Cat”); }}class Store { Store() { System.out.println(“Store”); }}public class Petstore extends Store{ Cat cat = new Cat(); Petstore() { System.out.println(“Petstore”); } public static void main(String[] args) { new Petstore(); }} 运行这段代码,它的执行结果如下: Store Animal Cat Petstore 从结果中可以看出,在创建一个Petstore类的实例时,首先调用了它的父类Store的构造方法;然后试图创建并初始化变量cat;在创建cat时,首先调用了Cat类的父类Animal的构造方法;其后才是Cat的构造方法主体,最后才是Petstore类的构造方法的主体。 寻找程序产生例外的原因 现在回到本文开始提到的实例中来,当程序创建一个ChildDlg2的实例时,根据super(null, “Title”)语句,首先执行其父类BaseDlg的构造方法;在BaseDlg的构造方法中调用了createClientPanel()方法,这个方法是抽象方法并且被子类ChildDlg2实现了,因此,实际调用的方法是ChildDlg2中的createClientPanel()方法(因为Java里面采用“动态绑定”来绑定所有非final的方法);createClientPanel()方法使用了ChildDlg2类的实例变量jTextFieldName,而此时ChildDlg2的变量初始化过程尚未进行,jTextFieldName是null值!所以,ChildDlg2的构造过程掷出一个NullPointerException也就不足为奇了。 再来看ChildDlg1,它的jTextFieldName的初始化代码写在了createClientPanel()方法内部的开始处,这样它就能保证在使用之前得到正确的初始化,因此这段代码工作正常。 解决问题的两种方式 通过上面的分析过程可以看出,要排除故障,最简单的方法就是要求项目组成员在继承使用BaseDlg类,实现createClientPanel()方法时,凡方法内部要使用的变量必须首先正确初始化,就象ChildDlg1一样。然而,把类变量放在类方法内初始化是一种很不好的设计行为,它最适合的地方就是在变量定义块和构造方法中。 在本文的实例中,引发错误的实质并不在ChildDlg2上,而在其父类BaseDlg上,是它在自己的构造方法中不适当地调用了一个待实现的抽象方法。 从概念上讲,构造方法的职责是正确初始化类变量,让对象进入可用状态。而BaseDlg却赋给了构造方法额外的职责。 本文实例的更好的解决方法是修改BaseDlg类: public abstract class BaseDlg extends JDialog { public BaseDlg(Frame frame, String title) { super(frame, title, true); this.getContentPane().setLayout(new BorderLayout()); this.getContentPane().add(createHeadPanel(), BorderLayout.NORTH); this.getContentPane().add(createButtonPanel(), BorderLayout.SOUTH); } /** 创建对话框实例后,必须调用此方法来布局用户界面 */ public void initGUI() { this.getContentPane().add(createClientPanel(), BorderLayout.CENTER); } private JPanel createHeadPanel() { … // 创建对话框头部 } // 创建对话框客户区域,交给子类实现 protected abstract JPanel createClientPanel(); private JPanel createButtonPanel { … // 创建按钮区域 }} 新的BaseDlg类增加了一个initGUI()方法,程序员可以这样使用这个类: ChildDlg dlg = new ChildDlg();dlg.initGUI();dlg.setVisible(true); 总结 类的构造方法的基本目的是正确初始化类变量,不要赋予它过多的职责。 设计类构造方法的基本规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构造方法内唯一能安全调用的是基类中具有final属性的方法或者private方法(private方法会被编译器自动设置final属性)。final的方法因为不能被子类覆盖,所以不会产生问题。[1][2]
为了一些琐事吵架,然后冷战,疯狂思念对方,最后和好。