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.
Ответы
Ответ 1
Конструктор 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
Ответ 2
Конструктор:
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;
}
вызывается. распечатывается "call to getSelectedFile", а затем "вызов getSelectedFile".
Ответ 3
Если вы посмотрите на трассировку стека, вы увидите, что конструктор JFileChooser
вызывает setup(FileSystemView view)
, который вызывает updateUI()
, который вызывает setUI()
в суперклассе JComponent, который вызывает installUI
на платформе- конкретный класс пользовательского интерфейса, этот класс затем вызывает installComponents
, который вызывает getSelectedFile
снова.
Цитата из Effective Java 2nd Edition:
Есть еще несколько ограничений, которые должен выполнять класс для разрешения наследования. Конструкторы не должны ссылаться на переопределяемые методы, прямо или косвенно. Если вы нарушите это правило, произойдет сбой программы. Конструктор суперкласса выполняется перед конструктором подкласса, поэтому метод переопределения в подклассе будет вызываться до запуска конструктора подкласса. Если метод переопределения зависит от любой инициализации, выполняемой конструктором подкласса, метод не будет вести себя так, как ожидалось.
Но, конечно, инструментарий 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)