2013-04-04 57 views
10

我有以下代码,在同一个java文件中。Java执行流 - 重写的方法首先比构造函数执行

import javax.swing.SwingUtilities; 
import java.io.File; 

public class MainClass2{ 
    public static void main(String[] args){ 
     SwingUtilities.invokeLater(new Runnable(){ 
      public void run() { 
       javax.swing.JFileChooser jfc = new MyFileChooser(); 
        File file = jfc.getSelectedFile(); 
      } 

     }); 
    } 
} 

class MyFileChooser extends javax.swing.JFileChooser{ 
    public MyFileChooser(){ 
     System.out.println("constructor call"); 
    } 
    @Override 
    public java.io.File getSelectedFile(){ 
     System.out.println("call to getSelectedFile"); 
     return null; 
    } 
} 

当我运行它,输出给了我

call to getSelectedFile

constructor call

call to getSelectedFile

不应该输出是

constructor call

call to getSelectedFile

我用java 5.

+0

哪里是你的电话到getSelectedFile? – 2013-04-04 07:36:19

+0

我的不好,实际上在我的原始代码中,即时通讯调用MyFileChooser实例化后的常用方式。但正如你所看到的那样,即使我没有对'getSelectedFile'进行明确的调用。我会更新我的代码。 – Bnrdo 2013-04-04 07:41:52

+0

你永远不需要调用'getSelectedFile',这是在你选择文件时调用'JFileChooser' – 2013-04-04 07:45:09

回答

8

MyFileChooser的构造等同于:

public MyFileChooser() { 
    super(); // *** 
    System.out.println("constructor call"); 
} 

getSelectedFile()第一呼叫由MyFileChooser制成的基类的构造,这在System.out.println("constructor call")之前隐含地在上面标记为***的点处被调用。

这里是堆栈跟踪:

MyFileChooser.getSelectedFile() line: 16  
AquaFileChooserUI.installComponents(JFileChooser) line: 1436  
AquaFileChooserUI.installUI(JComponent) line: 122 
MyFileChooser(JComponent).setUI(ComponentUI) line: 670 
MyFileChooser(JFileChooser).updateUI() line: 1798 
MyFileChooser(JFileChooser).setup(FileSystemView) line: 360 
MyFileChooser(JFileChooser).<init>(File, FileSystemView) line: 333 
MyFileChooser(JFileChooser).<init>() line: 286 
MyFileChooser.<init>() line: 11 
+0

为了避免这样的问题,构造函数调用的所有方法都应该是最终的,以便它们不能被子类覆盖。 – MatsT 2013-04-04 09:28:27

1

构造:

public MyFileChooser(){ 
    System.out.println("constructor call"); 
} 

似乎什么都没有做基类的构造函数。但是,对javax.swing.JFileChooser()有一个超级调用,它会调用getSelectedFile();。所以,你的构造其实是这样的:

public MyFileChooser(){ 
    super(); 
    System.out.println("constructor call"); 
} 

因为JFC是MyFileChooser,这种方法的目的:

@Override 
public java.io.File getSelectedFile(){ 
    System.out.println("call to getSelectedFile"); 
    return null; 
} 

是被调用。 “调用getSelectedFile”被打印出来,然后是“调用getSelectedFile”。

1

如果你看一下堆栈跟踪,你会看到JFileChooser构造函数调用setup(FileSystemView view)这就要求updateUI(),这在JComponent的超类,它要求在特定平台上的UI类installUI调用setUI(),这个类然后调用installComponents ,它再次调用getSelectedFile

从有效的Java第二版引述:

有类必须服从允许继承了几个限制。构造函数不得直接或间接地调用可重写的方法。如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此子类中的重写方法将在子类构造函数运行之前被调用。如果覆盖方法依赖于子类构造函数执行的任何初始化,则该方法将不会按预期运行。

但当然,Swing工具包并不总是遵循这个建议;-)

完整堆栈跟踪:

at MyFileChooser.getSelectedFile(MainClass2.java:27) 
    at com.apple.laf.AquaFileChooserUI.installComponents(AquaFileChooserUI.java:1436) 
    at com.apple.laf.AquaFileChooserUI.installUI(AquaFileChooserUI.java:122) 
    at javax.swing.JComponent.setUI(JComponent.java:670) 
    at javax.swing.JFileChooser.updateUI(JFileChooser.java:1798) 
    at javax.swing.JFileChooser.setup(JFileChooser.java:360) 
    at javax.swing.JFileChooser.<init>(JFileChooser.java:333)