这个问题你能答对吗?

首先,还是给大家说声抱歉,由于微信限制,前两天抽奖的好友请求还没有全部通过验证,这两天都会通过并拉大家进抽奖群的,还请大家海涵。

如果还没看到抽奖活动的小伙伴,请点击下面的:

传送门

今天首先来看个问题,用原生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 doGet
I 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代表其所在方法的当前对象的引用:

1) 构造方法中指该构造器所创建的新对象;

2) 方法中指调用该方法的对象

3) 在类本身的方法或构造器中引用该类的实例变量(全局变量)和方法。

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

本文由“壹伴编辑器”提供技术支持

最后我为大家收集了些学习资料,如果你准备入IT坑,励志成为优秀的程序猿,那么这些资源很适合你,包括java、go、python、springcloud、elk、嵌入式 、大数据、面试资料、前端 等资源。同时我们组建了一个技术交流群,里面有很多大佬,会不定时分享技术文章,如果你想来一起学习提高,可以公众号后台回复【2】,免费邀请加技术交流群互相学习提高,会不定期分享编程IT相关资源。

原文发布于微信公众号 - 我的小碗汤(mysmallsoup)

原文发表时间:2018-08-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端杂谈

让你的JS代码更具可读性

28710
来自专栏码洞

看完Java的动态代理技术——Pythoner笑了

Java的动态代理常用来包装原始方法调用,用于增强或改写现有方法的逻辑,它在Java技术领域被广为使用,在阿里的Sofa RPC框架序列化中你能看到它的身影,H...

1013
来自专栏Linyb极客之路

JVM 方法内联

调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。

2004
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试系列12

一、排序都有哪几种方法?请列举。用JAVA实现一个快速排序。 排序的方法有: 插入排序(直接插入排序、希尔排序), 交换排序(冒泡排序、快速排序), 选择排序...

3186
来自专栏程序员互动联盟

聊聊C语言-编程世界的容器

上一篇聊聊C语言-存储世界的奥秘,我们介绍了计算机的整个存储体系设计,了解了我们的数据在计算机中是怎么被存储的。然而在我们的编程中我们的代码也是按照这个结构被计...

4057
来自专栏郭耀华‘s Blog

Java命名规范

Java命名规范 定义规范的目的是为了使项目的代码样式统一,使程序有良好的可读性。 包的命名  (全部小写,由域名定义) Java包的名字都是由小写单词组...

65910
来自专栏C语言及其他语言

数组越界为什么没有出错

数组越界 在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不...

39810
来自专栏测试开发架构之路

C++之类和对象的特性

简介:C++并不是一个纯粹的面向对象的语言,而是一种基于过程和面向对象的混合型的语言。 凡是以类对象为基本构成单位的程序称为基于对象的程序,再加上抽象、封装、...

3446
来自专栏Java与Android技术栈

Scala学习笔记(二)

目前,Scala 在国外比较火,Twitter 已经将自己全部的代码从 Ruby 转到了Scala。而且还有 Spark、Kafka、akka 这样的开源项目及...

943
来自专栏chafezhou

小说python中的迭代器(Iterator)

902

扫码关注云+社区

领取腾讯云代金券