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();
- }
- }
缺点:若是在父类构造函数中调用被子类重写的方法,会致使子类重写的方法在子类构造器的全部代码以前执行,从而致使子类重写的方法访问不到子类实例变量的值,由于此时这些变量尚未被初始化。