1.若是父類構造器調用了被子類重寫的方法,且經過子類構造函數建立子類對象,調用了這個父類構造器(不管顯示仍是隱式),就會致使父類在構造時實際上調用的是子類覆蓋的方法(你須要瞭解java繼承中的初始化機制)。java
例子:
- public abstract class Father {
- public Father() {
- display();
- }
-
- public void display() {
- System.out.println("Father's display");
- }
- }
- public class Son extends Father {
-
- public Son() {
- }
-
- public void display() {
- System.out.println("Son's display");
- }
-
- public static void main(String[] args) {
- new Son();
- }
-
- }
輸出爲:
Son's display
這種機制有優勢,不過有時也存在問題。
優勢:經過繼承相同的父類,初始化子類時,父類會調用不一樣子類的不一樣複寫方法,從而實現多態性。
舉一個Spring中的例子:
Spring中能夠經過爲每一個DAO注入一個已經用DataSource初始化的JdbcTemplate,來執行SQL語句。可是每一個DAO都經過編碼實現這個注入就產生了大量代碼冗餘,因而Spring提供了一個JdbcDaoSupport類,DAO只需繼承這個類,就會自動注入已初始化好的JdbcTemplate,那麼是如何作到的呢?查看源碼:
JdbcDaoSupport繼承了DaoSupport:
- public abstract class JdbcDaoSupport extends DaoSupport
DaoSupport實現了InitializingBean接口,該接口只有一個
void
afterPropertiesSet
()
throws
Exception;
方法,Spring會在初始化Bean的屬相後查看這個Bean是否實現了InitializingBean接口,若是繼承了就會自動調用afterPropertiesSet方法。
那麼看一下DaoSupport中的afterPropertiesSet是如何實現的:
- public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
- // Let abstract subclasses check their configuration.
- checkDaoConfig();
-
- // Let concrete implementations initialize themselves.
- try {
- initDao();
- }
- catch (Exception ex) {
- throw new BeanInitializationException("Initialization of DAO failed", ex);
- }
- }
他這裏調用了checkDaoConfig方法,此方法是抽象方法,真正運行時會去調用子類重寫過的該方法。
查看JdbcDaoSupport如何重寫checkDaoConfig():
- @Override
- protected void checkDaoConfig() {
- if (this.jdbcTemplate == null) {
- throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");
- }
- }
JdbcDaoSupport會檢查jdbcTemplate是否注入,沒注入會拋出異常!這就完成了注入檢測,經過子類實現具體檢測的過程!這也就是當你的DAO繼承了JdbcDaoSupport,可是在XML配置DAO時沒有配置DataSource屬性會拋出異常的緣由。
那麼JdbcTemplate是什麼時候注入的呢?觀察JdbcDaoSupport源碼,發現setDataSource()方法,框架根據XML配置初始化DAO時,會調用屬性的set方法注入,若是DAO沒有該set方法,則調用父類的。也就是調用JdbcDaoSupport的setDataSource方法,此時便建立了DAO執行SQL語句須要的jdbcTemplate。
- /**
- * Set the JDBC DataSource to be used by this DAO.
- */
- public final void setDataSource(DataSource dataSource) {
- if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
- this.jdbcTemplate = createJdbcTemplate(dataSource);
- initTemplateConfig();
- }
- }
缺點:若是在父類構造函數中調用被子類重寫的方法,會致使子類重寫的方法在子類構造器的全部代碼以前執行,從而致使子類重寫的方法訪問不到子類實例變量的值,由於此時這些變量尚未被初始化。