对this关键字和super关键字的总结

对java面向对象中的this和super关键字进行复习。

1、this关键字

this是一个关键字,翻译为:这个。this是一个引用,this变量中保存了内存地址指向自身,this存储在JVM内存java对象内部。

每创建100个java对象,每一个对象都有this,也就说有100个不同的thisthis可以实现出现在“实例方法中”当中,this指向当前正在执行这个动作的对象

1.1、this关键字的用法

  • 通过this.属性来表示当前类的实例变量
  • 通过this.(参数列表)来表示调用当前类的成员方法
  • 通过this(参数列表)表示通过当前的构造方法去调用另一个本类的构造方法

1.2、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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Customer
{
//姓名【堆内存的对象内部存储,所以访问改数据的时候,必须先创建对象,通过引用方式访问】
String name;//实例变量:必须采用“引用.”的方式访问
public void shopping(){
//当张三在购物的时候,输出:张三在购物
//当李四在购物的时候,输出:李四在购物
// 这里的this是谁?this是当前对象。
// c1调用shopping(),this是c1
// c2调用shopping(),this是c2
//System.out.println(this.name + "正在购物!");

// this. 是可以省略的。
// this. 省略的话,还是默认访问“当前对象”的name。
System.out.println(this.name + "在购物!");
}
// 静态方法
public static void doSome(){
// this代表的是当前对象,而静态方法的调用不需要对象。矛盾了。
// 错误: 无法从静态上下文中引用非静态 变量 this
//System.out.println(this);
}
}
-----------------------------------------------------------------------
class CustomerTest
{
public static void main(String[] args){
//创建Customer
Customer c1 = new Customer();
c1.name = "zhangsan";

//c1购物
c1.shopping();

//在创建Customer对象
Customer c2 = new Customer();
c2.name = "lishi";

//c2购物
c2.shopping();

Customer.doSome();
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class Test{
public static void main(String[] args){

Student s = new Student();
s.setNo(111);
s.setName("张三");
System.out.println("学号:" + s.getNo());
System.out.println("姓名:" + s.getName());

Student s2 = new Student(2222, "李四");
System.out.println("学号:" + s2.getNo());
System.out.println("姓名:" + s2.getName());

}
}

// 分析一下:以下代码哪里写的不好。
// 学生类
class Student{
//学号
private int no;

//姓名
private String name;

//构造方法无参
public Student(){

}

//构造方法有参
/*
public Student(int i, String s){
no = i;
name = s;
}
*/

// 上面的构造方法也增强以下可读性
public Student(int no, String name){
this.no = no;
this.name = name;
}

// setter and getter方法
/*
public void setNo(int i){
no = i;
}
*/
/*
public void setNo(int no){ // 就近原则。
no = no; //这两个no都是局部变量no,和实例变量no没关系。
}
*/
public void setNo(int no){
//no是局部变量
//this.no 是指的实例变量。
this.no = no; // this. 的作用是:区分局部变量和实例变量。
}
public int getNo(){
return no;
//return this.no;
}
/*
public void setName(String s){
name = s;
}
*/
/*
public void setName(String name){ // 就近原则
name = name; //这两个name都是局部变量name,和实例变量name没关系。
}
*/
public void setName(String name){
this.name = name;
}

/*
public String getName(){
return name;
}
*/

public String getName(){ // getName实际上获取的是“当前对象”的名字。
//return this.name; // 严格来说,这里是有一个 this. 的。只不过这个 this. 是可以省略的。
return name;
}
}

1.3、this调用顺序

见下面的super调用顺序总结。

2、super关键字

2.1、super关键字的用法

在Java中,super关键字有三种用法,分别是:

  • 通过super.属性来访问父类中的成员变量
1
2
3
4
5
6
7
8
9
10
class Person {
protected int name;
}
---------------------------------------------
class Student extends Person{
public void func(){
//通过super.属性名访问父类的成员变量
int age = super.age;
}
}
  • 通过super.(参数列表)来访问父类中的成员方法
1
2
3
4
5
6
7
8
9
class Person {
protected void func(){}
}
----------------------------------------------
class Student extends Person{
public void func(){
super.func(); //通过super.方法名调用父类的成员方法
}
}
  • 通过super(参数列表)来访问父类中的构造方法创建对象。
1
2
3
4
5
6
7
8
9
10
11
12
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
----------------------------------------------
class Student extends Person{
public Student(String name) {
super(name); //通过super(参数列表)调用父类的构造方法
}
}

注意:子类无法通过super关键字访问父类中private修饰的属性和方法,因为private修饰的属性和方法只能在当前类的内部进行访问在子类的构造器中通过super关键字调用父类的构造器时,super关键字必须出现在构造器的第一行且仅能出现一次

2.2、super调用的顺序

在正常情况下(不使用this或者super关键字):当我们再子类中调用某方法时,如果子类存在需要该方法时,直接调用即可。如果子类中不存在该方法,则向上寻找父类,如果父类中存在该方法,则调用父类中的方法若父类中不存在该方法,那么向上寻找父类的父类,直到找到Object类

注意:如果直到Object类依然没有找到该方法,那么会提示方法不存在;但如果向上找到了这个方法但没有权限访问(例如该方法被private修饰),则会产生错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test extends Test2{
public static void main(String[] args) {
Test test = new Test();
test.func1();
}
public void func1(){
System.out.println("子类中的func1()");
func2();
}
}
------------------------------------------------------------
class Test2{
public void func2(){
System.out.println("父类中的func2()");
}
}
------------------------------------------------------------
/*
结果:
子类中的func1()
父类中的func2()
*/

在上述代码中,子类中的func1()方法在调用func2()方法时,会先在子类中查找是否存在func2()方法,未找到时会在其父类中查找func2()方法。

如果我们使用this关键字进行调用func2()将改为 this.func2(); 此时语义未发生改变,依然会先在子类中查找,没有找到时会在其父类中查找。

当我们使用super关键字,改为 super.func2()这时语义就发生了变化,上面提到过,super.方法名()调用的是父类中的方法。所以这条语句就不会检测当前类中是否存在func2()方法,只会从父类开始向上进行检测。

如果我们有重复的方法该怎么进行调用(就近原则),当子类要访问上级类的某个成员,而子类的多个上级类都有该成员时,我们对super关键字的使用采用就近原则,也就是访问super关键字向上找到的第一个成员。(this和super同样遵守就近原则)

3、this和super的异同

3.1、相同点

  • 二者都能出现在实例方法或者构造方法中(不能出现在静态方法中);
  • 二者的语法都是this.this()super.super()
  • 大部分情况下可以省略,但是在区分局部变量或者实例变量的时候不可省略;
  • 只能出现在构造方法的第一行(二者不能共同出现)。

3.2、不同点

区别点 this super
访问属性 访问本类中的属性,如果本类没有属性则从父类中继续查找 访问父类中的属性
调用方法 访问本类中的方法,如果本类没有此方法则从父类继续查找 直接访问父类中的方法
调用构造器 调用本类构造器,必须放在构造器首行 调用父类构造器,必须放在子类构造器的首行
特殊 表示当前对象 子类访问父类对象

4、默认存在的super

当一个构造方法第一行既没有this()又没有super()的话,默认会有一个super();表示通过当前子类构造方法调用父类的无参数构造方法。所以必须保证父类的无参数构造方法是存在的。无论怎样折腾,父类的构造方法是一定会执行的(100%)。

5、二者不共存(构造函数冲突)

如果想用super继承父类构造的方法,但是没有放在第一行的话,那么在super之前的语句,肯定是为了满足自己想要完成某些行为的语句,但是又用了super继承父类的构造方法。那么以前所做的修改就都回到以前了,就是说又成了父类的构造方法了。

this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

6、为什么静态方法中不能使用this和super方法

静态方法是存储在静态区内的,静态区会随着类加载器一起加载到内存当中,这时候,只是加载到内存当中,但是并没有真正的去运行,此时也就没有产生实例化的对象。

然而this是表示当前对象的,super表示父类对象的,两个关键字和对象是关联起来的。这时候,连实例化的对象都没有产生,所以this和super也就不存在。


以下是理解:
首先,static叫静态方法,也叫类方法,它先于任何的对象出现。在程序最开始启动(JVM初始化)的时候,就会为static方法分配一块内存空间,成为静态区,属于这个类。而非static方法,必须在类实例化的时候,才会给分配内存空间,在实例化对象的时候JVM在堆区分配一个具体的对象,this指针指向这个对象。也就是说,this指针是指向堆区中的类的对象,而static域不属于this指向的范围所在,所以不能调用。

同时,在static修饰的方法中,不能使用super(),道理其实与上面差不多。

super代表子类对父类满参构造函数的初始化,也是需要产生对象才可以使用但是考虑到虚拟机加载顺序为先加载类,当被实例化才产生对象。所以如果并存则表示类和对象同时加载,显然是不合逻辑的。


对this关键字和super关键字的总结
http://wahoyu.xyz/2022/10/24/this&super/
作者
Wahoyu
发布于
2022年10月24日
许可协议