我們知道,如果我們自定義一個servlet繼承HttpServlet,並且重寫HttpServlet中的doGet或doPost方法,那么從瀏覽器發送過來的request請求將調用HttpServlet中的service方法,在service方法中,根據獲得的method是get還是post,將調用我們自定義servlet中的doGet或doPost方法,這里用到了多態技術,因為基類HttpServlet中service方法調用的不是基類的doGet或doPost方法,而是子類的doGet或doPost方法。
我們舉一個例子來說明:
1 public class HelloServlet extends HttpServlet{
2 @Override 3 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 4 System.out.println("請求已收到。。。"); 5 } 6 }
這是正常情況下的程序。但是這里的多態是怎么實現的呢,我們並沒有看見子類對象賦給基類的代碼。這里我們不得不提java中很重要的概念:反射。反射不是這里研究的重點,所以不過多闡述。
那利用反射是怎樣實現上面代碼的多態呢?
我們看下面這個例子:
1 public class HelloServlet extends HttpServlet{
2 public HelloServlet(int i) { 3 } 4 5 @Override 6 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 7 System.out.println("請求已收到。。。"); 8 } 9 }
這個代碼只是在第一個代碼的基礎上加上了一個HelloServlet的帶參構造器,當我們運行上述代碼時,會報如下錯誤:
HTTP Status 500 – Internal Server Error
--------------------------------------------------------------------------------
Type Exception Report
Message Error instantiating servlet class [com.itheima.a_hello.HelloServlet]
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
javax.servlet.ServletException: Error instantiating servlet class [com.itheima.a_hello.HelloServlet]
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:407)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.base/java.lang.Thread.run(Thread.java:844)
Root Cause
java.lang.NoSuchMethodException: com.itheima.a_hello.HelloServlet.<init>()
java.base/java.lang.Class.getConstructor0(Class.java:3322)
java.base/java.lang.Class.getConstructor(Class.java:2108)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:407)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.base/java.lang.Thread.run(Thread.java:844)
Note The full stack trace of the root cause is available in the server logs.
在Message中我們看到,原理時實例化servlet類時出錯,那為什么會這樣呢?
原來,我們沒有看到的子類對象賦給基類的代碼正式反射幫我們做的。還記得我們在創建HelloServlet時會在web.xml中需要配置HelloServlet的全限定名嗎,這正是給反射用的,通過這個類名,反射技術可以得到該類的Class對象,並由Class對象的newInstance方法創建該類的實例,並把該子類對象賦給了基類,由此產生多態。
Class對象的newInstance方法需要根據無參構造器去創建實例,由於我們用帶參構造器覆蓋了無參構造器,所以,HelloServlet沒有了無參構造器,因此不能創建實例,報出以上錯誤信息。