java中的继承(三)

    在java中的修饰符有public、private、protected等,其中public表示对外可以访问,private表示只可以在类里面访问,protected表示虽然不可以在类外访问,但是可以在子类中访问。在设计模式中的模板方法就是按照这种设计思路来的,这也是protected的一种常见的使用场景。

    在子类重写父类的方法时,不允许降低父类方法的可见性。比如,如果父类方法的访问修饰符是protected,如果子类重写这个方法,那么重写的这个方法的修饰符就不能使用private。当然子类可以提高方法的可见性。之所以这样设计是因为在继承反映的是”is-a“的关系,子类对象要支持父类所有对外的行为,可见性降低会减少子类对外的行为。

    还记的模板设计模式吗?在模板设计模式中我们定义了一个固定的算法骨架,在这个骨架里面按照顺序调用方法其中有几个方法用protected修饰为了让子类重写。其中这个定义固定算法骨架的方法我们使用的修饰符就是final。被final修饰的方法是不允许被子类重写的,这样就可以防止子类重写篡改核心骨架。

    java的继承一方面可以实现代码的复用和动态绑定等强大的功能但是它的破坏性也是很强的。java的继承可能会破坏封装。因为子类可以通过重写修改父类的方法的访问修饰符,把父类的原本不对外公开的方法对外公开,其次由于动态绑定子类需要了解父类方法的一些实现细节才能做出正确的拓展。在这里需要给大家介绍一个对象ServletContext,他是servlet上下文对象,多个servlet共用一个ServletContext对象。下面我们定义一个servlet:

public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(this.getServletConfig());
    }

}

当我们访问这个servlet时按照代码中所写会在控制台打印ServleContext对象:org.apache.catalina.core.StandardWrapperFacade@1ff0a94。这时,我们在重写一个方法:

public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println(config.getServletContext());
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(this.getServletConfig());
    }

}

init(ServletConfig config)方法。这个方法会在servlet初始化的时候被调用,这时控制台上回打印出之前的那个servletContext对象,可是这时如果我们访问这个servlet对象时控制台的输出是:

很明显,this.getServletConfig()这个方法返回了null,查看源码发现getServletConfig()方法返回的是一个类成员变量config。

在从init方法的父类的实现方法中发现这个config变量的赋值操作是在init方法中进行的

分析到这里相信我们已经发现了问题所在,由于我们重写了父类的init方法,导致原来父类中的初始化操作失败,所以这时我们只需要修改代码为:

public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    

    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println(config.getServletContext());
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(this.getServletConfig().getServletContext());
    }

}

我们需要在原来的init方法中通过super关键字调用一下父类的init方法即可。可是这时对于子类而言仅仅知道父类能做什么是不够的,我们还需要了解父类是如何实现的,这时子类就与父类有了依赖的关系,这也是继承的一个弊端。

原文地址:https://www.cnblogs.com/suyang-java/p/10505161.html