您的位置 首页 java

我的Java Web之路15 – 抽象类和抽象方法

本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,原来已经分享在我的CSDN博客,现在分享在头条,希望能帮助更多码农和想成为码农的人。版权声明:本文为CSDN博主「普通的码农」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。原文链接:
 

目录

  1. 介绍
  2. Servlet 类图
  3. 为什么需要 抽象类
  4. 抽象方法
  5. 抽象的层次
  6. 总结

介绍

粗略的看了一下Tomcat提供的servlet-api.jar包中的Servlet有关的源码,这涉及到了 抽象类 这个基本概念,本篇文章我们就介绍一下它。

Servlet类图

根据我们看的那几个 接口 的源码,我们可以用 类图 (这方面涉及到一种叫做 统一建模语言 UML 的标准,暂且不深入讨论)来表示它们之间的关系。

我是用微软的 Visio 软件来画这种图(关于这个软件的使用也暂且不介绍),可能并不一定满足UML的标准,但核心的东西表示出来就行了:

是不是比源码清晰多了呢!而且还很形象,实线箭头表示 继承/扩展 (某个类),虚线箭头表示 实现 (某几个接口),memberName表示属性域或方法,不过我已经省略了,<<抽象类>>是我手动加的,为了区分普通类。

事实上,在软件研发过程中,通常都是先把 类图 和其他的图设计出来,然后再进行代码的编写实现,或者通过工具软件把这些设计图直接转换成代码。这些设计图就好比建筑工程里面的建筑蓝图。

我们现在是通过源码返推出设计图,这样有助于记忆和理解一个 软件的结构/架构、流程 等等。

为什么需要抽象类

讲过, 其实就是人脑中的 概念 在代码中的体现,那么 抽象类 当然就是指 抽象的概念 啊。

什么是抽象?下面是百度汉语给出的答案:

现实生活中有太多这样的例子,比如水果、动物、交通工具等等,这些概念都是不具体、太笼统、细节不明确的。

而我们的HttpServlet和GenericServlet也是如此。

HttpServlet是哪些细节不明确呢?当然就是需要用户去实现的业务细节啊,比如具体如何处理HTTP的GET、POST、PUT、DELETE等请求啊。

GET什么东西/资源,技术上是指网页、图像、文件、音频、视频、代码等等,业务上又可能是某种商品的细节啊、某篇新闻报道或小说啊、某部电影或某首流行音乐啊等等。

HttpServlet明确的是它是使用HTTP协议来与客户端软件进行通信 ,所以它提供了与HTTP各个方法对应的成员方法来处理请求。处理的请求是HTTP请求HttpServlet request ,返回的响应HTTP响应HttpServletResponse。

事实上,这只是一种约定而已,你完全可以在doGet方法中处理一些提交数据或更新数据或删除数据的业务逻辑。然而,这是极不提倡的,约定不就是需要大家一起遵守的吗!否则,你构建的代码将混乱不堪,给自己和别人维护代码带来极大的困难。 代码的可维护性很大程度上取决于约定的遵守程度。

所以,正是因为存在不具体,细节不明确的概念,才需要抽象类。从目标上来说,我们提供抽象类是为了给下游的软件研发人员实现进一步的细节,我们只是预留一个已经明确的约定而已。

抽象方法

GenericServlet也是一个抽象类,那它有哪些细节不明确呢?

查看它的源码可以看到它没有HttpServlet中doXXXX之类的方法,跟请求和响应相关的方法是下面一个很奇怪的方法,该方法只有 方法签名 ,而没有 方法体

@Override
public  abstract  void  service (ServletRequest req, ServletResponse res)
 throws ServletException, IOException;
 

这就是 抽象方法 ,也就是GenericServlet细节不明确的地方,它的参数是ServletRequest和ServletResponse,少了HTTP, 就是说它连使用何种通信协议都不明确。 这意味着这个抽象类没有约定要使用HTTP协议,你可以使用任何其他协议(事实上很少用到)。

那这个方法是什么意思呢?从方法名来看就是提供某种服务。OK,这就对了,不明确的就是提供何种服务啊,就需要用户去进一步实现这个方法啊。

抽象方法从语义上来说就是实现细节不明确的方法,从技术上来讲就只需加一个abstract关键字且不实现方法体即可。

从这个角度看,这个service方法也太抽象了吧,等于什么都没有提供嘛,我们也可以提供一个叫doSomething的抽象方法啊,那这还叫抽象吗?

别忘了service方法还有参数呢,ServletRequest和ServletResponse可是规定了很多特定的方法,当然它们也是抽象方法,不过至少从方法名上可以窥其一二。

不过,不得不说service这个方法名起得还是相当不错,语义上就是服务嘛,至于什么服务,就是用户需要实现的啊,可以是用户的注册登录、某个商品的搜索、订单的提交和支付、视频的观看等等。

抽象的层次

既然HttpServlet 继承/扩展 了GenericServlet,那HttpServlet包含的细节应该比GenericServlet更明确一些。

明确在哪呢?就是明确了它是使用 HTTP 协议的,因此它的service方法是这样的:

public void service(ServletRequest req, ServletResponse res)
 throws ServletException, IOException {
 HttpServletRequest request;
 HttpServletResponse response;
 try {
 request = (HttpServletRequest) req;//这里是重点
 response = (HttpServletResponse) res;//这里是重点
 } catch (ClassCastException e) {
 throw new ServletException(lStrings.getString("http.non_http"));
 }
 service(request, response);//这里是重点
}
 

首先,因为是继承/扩展GenericServlet,所以这个service方法 覆盖 了GenericServlet中的service方法,同时提供了方法体(就是实现了GenericServlet中的service方法)。

最主要的逻辑是将ServletRequest req, ServletResponse res 强制转换 成了HttpServletRequest和HttpServletResponse类型,因为只要是进入到HttpServlet的这个service方法,那么可以明确的就是使用了HTTP协议,因此可以进行这种转换

(当然,这段强制转换代码被 异常处理 包围着,就是以防万一转换的不是HTTP协议的Servlet请求和响应,那么就抛出 异常 ,关于异常,我们以后再讨论)。

转换之后,又调用了HttpServlet中 重载 的另外一个service方法,该方法的参数就是HttpServletRequest和HttpServletResponse类型了(关于方法覆盖和重载,可以看看我的 )。我们再来看看这个service方法:

protected void service(HttpServletRequest req, HttpServletResponse resp)
 throws ServletException, IOException {
 String method = req.getMethod();//这里是重点
 if (method.equals(METHOD_GET)) {//这里是重点
 long lastModified = getLastModified(req);
 if (lastModified == -1) {
 // servlet doesn't support if-modified-since, no reason
 // to go through further expensive logic
 doGet(req, resp);//这里是重点
 } else {
 long ifModifiedSince;
 try {
 ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
 } catch (IllegalArgumentException iae) {
 // Invalid date header - proceed as if none was set
 ifModifiedSince = -1;
 }
 if (ifModifiedSince < (lastModified / 1000 * 1000)) {
 // If the servlet mod time is later, call doGet()
 // Round down to the nearest second for a proper compare
 // A ifModifiedSince of -1 will always be less
 maybeSetLastModified(resp, lastModified);
 doGet(req, resp);//这里是重点
 } else {
 resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 }
 }
 } else if (method.equals(METHOD_HEAD)) {//这里是重点
 long lastModified = getLastModified(req);
 maybeSetLastModified(resp, lastModified);
 doHead(req, resp);//这里是重点
 } else if (method.equals(METHOD_POST)) {//这里是重点
 doPost(req, resp);//这里是重点
 } else if (method.equals(METHOD_PUT)) {//这里是重点
 doPut(req, resp);//这里是重点
 } else if (method.equals(METHOD_DELETE)) {//这里是重点
 doDelete(req, resp);//这里是重点
 } else if (method.equals(METHOD_OPTIONS)) {//这里是重点
 doOptions(req,resp);//这里是重点
 } else if (method.equals(METHOD_TRACE)) {//这里是重点
 doTrace(req,resp);//这里是重点
 } else {
 //
 // Note that this means NO servlet supports whatever
 // method was requested, anywhere on this server.
 //
 String errMsg = lStrings.getString("http.method_not_implemented");
 Object[] errArgs = new Object[1];
 errArgs[0] = method;
 errMsg = MessageFormat.format(errMsg, errArgs);
 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
 }
}
 

我们只看重点语句,可以看到,这就是从HTTP请求中拿到HTTP方法后,判断是GET、HEAD、POST等等之中的哪一个之后,再调用相应的doXXXX方法。

因此,HttpServlet这个抽象类比GenericServlet这个抽象类更加明确一些,明确的是使用HTTP协议来处理请求和响应。

但还是不够明确,就是如何处理HTTP协议中的GET、POST之类的请求,这些请求中执行什么业务逻辑,是用户登录还是订单提交支付,还是看电影听音乐等等。这就是我们开发HelloWorld这个Servlet的目的。

可见,抽象具有层次性,这也符合我们的思维习惯。 我们设计类的时候,也应该尽量遵从这种思维习惯,尽量把相关的部分抽象出来形成一个层次。基于这一点,我们甚至可以在继承/扩展HttpServlet的时候,继续设计一个抽象类,再往下扩展时又设计一个抽象类。。。直到你判断业务该是设计 具体类 的时候。

总结

好,以后我们遇到 abstract 关键字就不会陌生了,就是 抽象类 抽象方法 的意思;我们也可以设计自己的抽象类和抽象方法了。

  • 类图 是一个很好的设计、交流的工具,我们也可以尝试为源码画画类图,这样有助于记忆和理解;
  • 因为存在不具体、细节不明确的概念,所以需要抽象类,这就符合人的思维层次性;
  • 抽象类的目标为了给下游的软件研发人员去实现进一步的细节(即给别人继承/扩展之用的),我们只是根据已经明确的细节预留一个约定而已;
  • 抽象是具有层次性的,因而也就形成了 继承/扩展层次
  • 分层的思想很重要 ,也可以称之为分层模式、分层范式,大的有架构上的分层,小的有方法上的分层;
  • Java中抽象类使用 abstract 关键字定义
  • Java中抽象方法使用 abstract 关键字定义且不能实现方法体,因为一丁点细节都不知道啊,唯一知道的就是它的参数和返回值;
  • 拥有抽象方法的类必须定义为抽象类;
  • 但抽象类不必有抽象方法,它可以有具体细节,因为可以提供具体的但很粗糙的默认方法实现 ,HttpServlet就是这样,但这样的方法往往是需要被覆盖的,它们通常使用 protected 关键字;
  • 抽象类不能生成对象/实例,但可以声明一个抽象类的引用类型的变量,该变量可以指向一个具体类的对象/实例;
  • 具体类 能直接使用new关键字生成对象/实例,语义上就是细节很明确的,不过具体类也可以被继承/扩展,它的方法也能被覆盖。
  • 所谓语义,就是在语言上的含义,它通常是分层的依据,说直接一点就是类名、方法名一定要在一定程度上符合或表达或反映各个层次的细节,不该放在该层的就不要放到该层,这又体现了单一职责原则。

文章来源:智云一二三科技

文章标题:我的Java Web之路15 – 抽象类和抽象方法

文章地址:https://www.zhihuclub.com/173127.shtml

关于作者: 智云科技

热门文章

网站地图