1. 程式人生 > >java父類呼叫被子類重寫的方法

java父類呼叫被子類重寫的方法


1.如果父類構造器呼叫了被子類重寫的方法,且通過子類建構函式建立子類物件,呼叫了這個父類構造器(無論顯示還是隱式),就會導致父類在構造時實際上呼叫的是子類覆蓋的方法(你需要了解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();
		}
	}


缺點:如果在父類建構函式中呼叫被子類重寫的方法,會導致子類重寫的方法在子類構造器的所有程式碼之前執行,從而導致子類重寫的方法訪問不到子類例項變數的值,因為此時這些變數還沒有被初始化。