java中this和super关键字
初学java的同学都知道,this代表当前对象自身,可以看作是当前对象的虚拟地址(尽管不是,当前可以这么理解).后面再做详细讲解.
很多初学java的同学都习惯性的直译super关键字,认为super关键字就是指向当前对象的父类对象的引用.此种想法是错误的,我在JAVA创建子类对象不会实力化父类对象一文中已经提及,当我们实例化子类对象的时候根本就不会创建其父类对象,父类对象都不存在,何来super保存父类对象的虚拟地址一说?
闲言少叙,我们通过下面的例子来看this和super都保存了什么信息:
package test2;
public class Person {
public Person() {
System.out.println("Person's this: " + this);
//此处如上图所示,不能直接打印super关键字,需要调用下suepr的toString()
System.out.println("Person's super: " + super.toString());
}
}
package test2;
public class Teacher extends Person{
public Teacher() {
System.out.println("Teacher's this:" + this);
System.out.println("Teacher's super: " + super.toString());
}
}
package test2;
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher();
System.out.println("Teacher对象地址: " + t);
}
}
我运行了测试类的结果:
通过上面的运行,我们能看到,this和super中存储的都是当前对象在堆中的虚拟地址.下面,我来用图说明下,this和super的具体用法和区别,为了更清晰的认识在创建对象的时候this和super都做了什么,我把代码进行些许改动.
package test2;
public class Person {
private String name = "无名";
public Person() {
super();
System.out.println("Person's this: " + this);
//此处如上图所示,不能直接打印super关键字,需要调用下suepr的toString()
System.out.println("Person's super: " + super.toString());
this.name = "王飞";
}
}
package test2;
public class Teacher extends Person{
double salary = 8000;
public Teacher() {
super();//这里显示调用当前类的父类的无参构造方法,不写的化,虚拟机自动添加。
System.out.println("Teacher's this: " + this);
System.out.println("Teacher's super: " + super.toString());
}
}
进入debug模式
按F5,启动类加载器
F6:跳过加载器加载过程,回到测试类构造方法
F5:进入Teacher类无参构造方法
F5:执行super(),调用Teacher类的父类的无参构造
Person类无参构造第一行也是调用super()(父类的无参数构造方法),这写和不写都一样,不写的话虚拟机会默认调用父类的无参数构造方法.
F5:执行super(),调用Person类的父类无参构造.
Object类是所有类的"鼻祖"类,只有默认的无参构造,此处不做绘图处理.F5:返回到Person类,初始化成员变量值(还没进入构造方法的初始值)
在此时,给堆中对象赋值就是根据this关键字进行的赋值(注意,name的值是在this下),也就是说一旦我们new Teacher();
new关键字调用构造方法的时候,就会在堆中创建好对象,并隐式的把对象的首地址值赋予了this关键字.
F5:打印this中保存的值
下面我用堆栈结构演示:
F6:步进,打印super存储的信息
我们会看到在Person类中this关键字和super关键字存储的虚拟地址是同一个虚拟之地,都指向同一个对象
从而把对象中name属性,由null替换成字符串对象"无名".
此处大家可能不太好理解,其本质是现有对象地址,然后根据对象的地址找到堆中的对象,再执行
private String name = “无名”;
这行代码的赋值.如下图:
那么问题来了,既然都是保存同一个地址,那java为什么会分别定义this(当前对象的引用),super(父类"域"的引用)呢?
在此,我暂且认为super关键字的作用为"域"的标识,是用来标注在堆对象中,哪些属性(成员变量)是从哪个父类(可能还有爷爷类)继承过来的.
为了演示这种效果,我需要在Person类中增加一个可以被子类可见的属性.重构代码
package test2;
public class Person {
private String name = "无名";
protected int age;
public Person() {
super();
System.out.println("Person's this: " + this);
//此处如上图所示,不能直接打印super关键字,需要调用下suepr的toString()
System.out.println("Person's super: " + super.toString());
this.name = "王飞";
this.age = 37;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package test2;
public class Teacher extends Person{
double salary = 8000;
int age;
public Teacher() {
super();//这里显示调用当前类的父类的无参构造方法,不写的化,虚拟机自动添加。
System.out.println("Teacher's this : " + this);
System.out.println("Teacher's super: " + super.toString());
this.age = 39;
super.age = 38;
}
}
package test2;
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher();
System.out.println("Teacher对象地址: " + t);
System.out.println("当父类中有age属性,子类中也有age属性,用当前对象引用看到的是子类的age属性:" + t.age);
}
}
运行结果:
断点还是在第五行,重新启动debug模式:
F5
F6
F5
F5 调用Teacher类中的super(),即Person类的无参构造方法
F5
F5
F6
F6
F6
this保存堆对象的地址,会通过对象找到堆中对象的name属性进行重新赋值
F6
在此,估计大家会问,你怎确定的上面的age而不是给下面age赋值.这也就是为什么又重构代码的原因.因为虚拟机在加载类的时候,会把当前类有继承关系的类全部加载,并在对象中开辟继承来的属性空间和自身类的属性空间,用"域"做标识
F5
F6
F6
F6
F6
当前执行:this.age = 38; 此时this保存的还是对象地址,但为了大家看的更清晰,我把箭头直接指向了this的"域"
F6
当前执行:super.age = 100; 此时this保存的还是对象地址,但为了大家看的更清晰,我把箭头直接指向了super的"域"
F6
F6
时间比较紧,没办法继续文字说明了,但相信图画的还算清晰,