推薦閱讀:
正文今天首先來看個問題,用原生servlet實現的介面,大家看下控制檯輸出結果是什麼?
web.xml如下:
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.smallsoup.servlet.SonServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/rest/v3/access/*</url-pattern> </servlet-mapping></web-app>
SonServlet.java如下:
package com.smallsoup.servlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** @program: myServlet* @description: SonServlet* @author: smallsoup* @create: 2018-08-01 20:46**/public class SonServlet extends ParentServlet{ @Override public void handleGet(HttpServletRequest req, HttpServletResponse resp) { System.out.println("I am SonServlet handleGet"); }}
ParentServlet.java如下:
package com.smallsoup.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @program: myServlet* @description: ParentServlet* @author: smallsoup* @create: 2018-08-01 20:47**/public class ParentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("I am ParentServlet doGet"); this.handleGet(req, resp); } public void handleGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("I am ParentServlet handleGet"); super.doGet(req, resp); }}
啟動tomcat,用postman發請求:
GET:http://localhost:8080/rest/v3/access/1212
控制檯會輸出什麼呢?答案是:
I am ParentServlet doGet
I am SonServlet handleGet
我相信很多小夥伴應該會答錯,以為會輸出:
I am ParentServlet doGetI am ParentServlet handleGet
或者別的答案。小編今天遇到這個問題也懵逼了,基礎掌握不紮實,還得回過頭來補補。
首先根據url匹配到web.xml中定義的name為myServlet的servlet,所以會到SonServlet中去處理,但是SonServlet沒有重寫HttpServlet的doGet()方法,它的父類ParentServlet重寫了,所以請求會到ParentServlet的doGet()方法,但是這裡的doGet方法中的this.handleGet中的this指的是什麼呢?我們通過debug看到this其實是SonServlet的例項。
由此看來,this.handleGet會去呼叫SonServlet的方法,這就解釋了控制檯的輸出。
這個問題,主要包含兩個知識點:
1、servlet處理請求的流程;
2、this關鍵字指什麼?
下面這篇對this關鍵字講的非常好,出自:
https://www.cnblogs.com/zheting/p/7751752.html
Java中this關鍵字使用小結:
當一個物件建立後,Java虛擬機器(JVM)就會給這個物件分配一個引用自身的指標,這個指標的名字就是 this。
因此,this只能在類中的非靜態方法中使用,靜態方法和靜態的程式碼塊中絕對不能出現this,並且this只和特定的物件關聯,而不和類關聯,同一個類的不同物件有不同的this。
1、使用this來區分當前物件
Java中為解決變數的命名衝突和不確定性問題,引入關鍵字this代表其所在方法的當前物件的引用:
構造方法中指該構造器所建立的新物件;方法中指呼叫該方法的物件;在類本身的方法或構造器中引用該類的例項變數(全域性變數)和方法。this只能用在構造器或者方法中,用於獲得呼叫當前的構造器方法的物件引用。可以和任何的物件引用一樣來處理這個this物件。
說明:
當例項變數和區域性變數重名,JAVA平臺會按照先區域性變數、後例項變數的順序尋找。即,方法中使用到的變數的尋找規律是先找區域性變數,再找例項變數。如果沒用找到,將會有一個編譯錯誤而無法通過編譯。
如果使用this.a,則不會在方法(區域性變數)中尋找變數a,而是直接去例項變數中去尋找,如果尋找不到,則會有一個編譯錯誤。
在一個方法內,如果沒有出現區域性變數和例項變數重名的情況下,是否使用this關鍵字是沒有區別的。
在同一個類中,Java普通方法的互相呼叫可以省略this+點號,而直接使用方法名+引數。因為Java編譯器會幫我們加上。
2、 在構造器中使用this來呼叫物件本身的其他構造器
在構造器中使用this([args_list]);可以呼叫物件本身的其他的構造器。直接使用this()加上類構造器所需要的引數。就可以呼叫類本身的其他構造器了。如果類中有多個其他構造器定義,系統將自動根據this()中的引數個數和型別來找出類中相匹配的構造器。
注意: 在構造器中可以通過this()方式來呼叫其他的構造器。但在一個構造器中最多隻能呼叫一個其他的構造器。並且,對其他構造器的呼叫動作必須放在構造器的起始處(也就是構造器的首行),否則編譯的時候將會出現錯誤,另外不能在構造器以外的地方以這種方式呼叫構造器。
3、 this關鍵字還有一個重大的作用就是返回類的引用。如在程式碼中,可以使用return this來返回某個類的引用。此時,這個this關鍵字就代表類的名稱。
例1、把this作為引數傳遞
當你要把自己作為引數傳遞給別的物件時,也可以用this。如:
package com.smallsoup.servlet;/*** @program: myServlet* @description: A* @author: smallsoup* @create: 2018-08-01 22:58**/public class A { public A(){ new B(this).print(); } public void print(){ System.out.println("From A!"); } public static void main(String[] args) { new A(); }}class B{ A a; public B(A a){ this.a = a; } public void print(){ a.print(); System.out.println("From B!"); }}
執行結果:
From A!From B!
在這個例子中,物件A的建構函式中,用new B(this)把物件A自己作為引數傳遞給了物件B的建構函式。
例2、注意匿名類和內部類中的中的this
有時候,我們會用到一些內部類和匿名類,如事件處理。當在匿名類中出現this時,這個this則指的是匿名類或內部類本身。這時如果我們要使用外部類的方法和變數的話,則應該加上外部類的類名。如下面這個例子:
package com.smallsoup.servlet;/*** @program: myServlet* @description: C* @author: smallsoup* @create: 2018-08-01 23:00**/public class C { int i = 1; public C(){ Thread thread = new Thread(){ @Override public void run(){ for(;;){//表示是死迴圈 C.this.run();//呼叫外部方法run() try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } };//注意這裡有分號; thread.start(); } public void run(){ System.out.println("i = " + i); i++; } public static void main(String[] args) throws Exception { new C(); }}
執行結果:每一秒產生一個數:1,2,3 ……
在上面這個例子中, thread 是一個匿名類物件,在它的定義中,它的 run 函式裡用到了外部類的 run 函式。這時由於函式同名,直接呼叫就不行了。這時有兩種辦法,一種就是把外部的 run 函式換一個名字,但這種辦法對於一個開發到中途的應用來說是不可取的。那麼就可以用這個例子中的辦法用外部類的類名加上 this 引用來說明要呼叫的是外部類的方法 run。
例3 、this關鍵字最大的作用是,讓類的一個方法,訪問該類的另一個方法或者屬性。
先看一個不好的例子:
package com.smallsoup.servlet;/*** @program: myServlet* @description: Baby* @author: smallsoup* @create: 2018-08-01 23:03**/public class Baby { public void wakeUp(){ System.out.println("寶寶醒啦"); } public void eat(){ Baby baby = new Baby(); baby.wakeUp(); System.out.println("吃東西"); }}
這樣不符合邏輯。這就相當於本物件的eat方法,需要呼叫另一個物件的wakeUp方法。
我們看這個例子:
public class Baby { public void wakeUp() { System.out.println("寶寶醒啦"); } public void eat() { this.wakeUp(); System.out.println("吃東西"); }}
這樣就符合邏輯了。自己的eat方法,還需要自己的一個wakeUp方法。
java允許同一個物件的方法直接呼叫該物件的屬性或者方法,所以this可以省略。
注意:java中為什麼在static中不能使用this關鍵字?Static方法是類方法,先於任何的例項(物件)存在。即Static方法在類載入時就已經存在了,但是物件是在建立時才在記憶體中生成。而this指代的是當前的物件在方法中定義使用的this關鍵字,它的值是當前物件的引用。也就是說你只能用它來呼叫屬於當前物件的方法或者使用this處理方法中成員變數和區域性變數重名的情況,而且,更為重要的是this和super都無法出現在static 修飾的方法中,static 修飾的方法是屬於類的,該方法的呼叫者可能是一個類,而不是物件。如果使用的是類來呼叫而不是物件,則 this就無法指向合適的物件.所以static 修飾的方法中不能使用this