Fork me on GitHub

二.多态提高

大家先来看看一段代码然后来猜测一下输出的值是什么

大神请绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package yp.JavaSE.Review_07;

/**
* @author RickYinPeng
* @ClassName Test_04
* @Description
* @date 2019/1/18/14:06
*/
public class Test_04 {
public static void main(String[] args) {
HttpServlet httpServlet = new MyHttpServlet();
httpServlet.service();
}
}
class HttpServlet {

public void service(){
System.out.println("调用HttpServlet的service方法");
doGet();
}
public void doGet(){
System.out.println("调用HttpServlet的doGet方法");
}
}
class MyHttpServlet extends HttpServlet{
@Override
public void doGet() {
System.out.println("调用MyHttpServlet的doGet方法");
}
}

输出结果:

1
2
调用HttpServlet的service方法
调用MyHttpServlet的doGet方法

我们来分析一波

按照之前的逻辑,我们肯定会认为结果如下:

1
2
调用HttpServlet的service方法
调用HttpServlet的doGet方法

那么这是为什么呢?

其实大家可以参考一下Java编程思想中的第84页的this关键字,这里我不在叙述了,后面会讲。

当我们在方法执行之时,比如有相同的两个对象调用相同的方法,那么编译器是如何知道具体是哪个对象呢?

这个时候为了能区分到底是哪个对象调用的方法,编译器帮我们偷偷的将当前调用这个方法的对象引用传了进去,这样就可以区分了。如下

1
2
3
4
5
User u1 = new User(1,"张三");
User u2 = new User(2,"李四");

u1.login(1);
u2.login(2);

两个对象u1和u2同时调用了login对象,那编译器是如何知道到底哪个对象调用了方法呢?于是编译器做了以下手脚

1
2
3
4
5
User u1 = new User(1,"张三");
User u2 = new User(2,"李四");

User.login(u1,1);
User.login(u2,2);

编译器帮我们将这个对象的引用偷偷传了进去,这样就可以识别了,Java为我们专门设置了这一代表谁调用方法的关键字—–this,那我们来看看我们之前的那个代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package yp.JavaSE.Review_07;

/**
* @author RickYinPeng
* @ClassName Test_04
* @Description
* @date 2019/1/18/14:06
*/
public class Test_04 {
public static void main(String[] args) {
HttpServlet httpServlet = new MyHttpServlet();
httpServlet.service();
}
}
class HttpServlet {

public void service(){
System.out.println("调用HttpServlet的service方法");
doGet();
}
public void doGet(){
System.out.println("调用HttpServlet的doGet方法");
}
}
class MyHttpServlet extends HttpServlet{
@Override
public void doGet() {
System.out.println("调用MyHttpServlet的doGet方法");
}
}

我们可以将其转换为如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package yp.JavaSE.Review_07;

/**
* @author RickYinPeng
* @ClassName Test_04
* @Description
* @date 2019/1/18/14:06
*/
public class Test_04 {
public static void main(String[] args) {
HttpServlet httpServlet = new MyHttpServlet();
httpServlet.service(this);
}
}
class HttpServlet {

public void service(this){
System.out.println("调用HttpServlet的service方法");
//这个this是编译器帮我们加上去的,我们可以反编译看看
this.doGet();
}
public void doGet(){
System.out.println("调用HttpServlet的doGet方法");
}
}
class MyHttpServlet extends HttpServlet{
@Override
public void doGet() {
System.out.println("调用MyHttpServlet的doGet方法");
}
}

分析:

  1. 首先我们自己的MyHttpServlet调用service方法
  2. 由于我们自己写的类MyHttpServlet没有重写service方法,所以它就去调用我们父类HttpServlet的service方法
  3. 并且在调用的时候编译器将this(就是MyHttpServlet对象的引用)偷偷传入进去
  4. 当我们执行父类的service方法时,父类的service方法又调用doGet()方法
  5. 编译器给我们加了this,而当前this代表的是MyHttpServlet对象的引用,所以执行的doGet()方法也是MyHttpServlet的doGet()方法。