java中this和super关键字

初学java的同学都知道,this代表当前对象自身,可以看作是当前对象的虚拟地址(尽管不是,当前可以这么理解).后面再做详细讲解.

很多初学java的同学都习惯性的直译super关键字,认为super关键字就是指向当前对象的父类对象的引用.此种想法是错误的,我在JAVA创建子类对象不会实力化父类对象一文中已经提及,当我们实例化子类对象的时候根本就不会创建其父类对象,父类对象都不存在,何来super保存父类对象的虚拟地址一说?

闲言少叙,我们通过下面的例子来看this和super都保存了什么信息:

java default关键字 AOP java关键字this super_java default关键字 AOP

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);
	}
}

我运行了测试类的结果:

java default关键字 AOP java关键字this super_编程语言_02

通过上面的运行,我们能看到,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());
	}
}

java default关键字 AOP java关键字this super_java default关键字 AOP_03


进入debug模式

按F5,启动类加载器

java default关键字 AOP java关键字this super_编程语言_04


F6:跳过加载器加载过程,回到测试类构造方法

java default关键字 AOP java关键字this super_java default关键字 AOP_05

F5:进入Teacher类无参构造方法

java default关键字 AOP java关键字this super_编程语言_06

java default关键字 AOP java关键字this super_java default关键字 AOP_07

F5:执行super(),调用Teacher类的父类的无参构造

java default关键字 AOP java关键字this super_编程语言_08


Person类无参构造第一行也是调用super()(父类的无参数构造方法),这写和不写都一样,不写的话虚拟机会默认调用父类的无参数构造方法.

java default关键字 AOP java关键字this super_System_09

F5:执行super(),调用Person类的父类无参构造.

java default关键字 AOP java关键字this super_System_10


Object类是所有类的"鼻祖"类,只有默认的无参构造,此处不做绘图处理.F5:返回到Person类,初始化成员变量值(还没进入构造方法的初始值)

java default关键字 AOP java关键字this super_java_11


在此时,给堆中对象赋值就是根据this关键字进行的赋值(注意,name的值是在this下),也就是说一旦我们new Teacher();

new关键字调用构造方法的时候,就会在堆中创建好对象,并隐式的把对象的首地址值赋予了this关键字.

F5:打印this中保存的值

java default关键字 AOP java关键字this super_System_12


下面我用堆栈结构演示:

java default关键字 AOP java关键字this super_java default关键字 AOP_13


F6:步进,打印super存储的信息

java default关键字 AOP java关键字this super_java_14


我们会看到在Person类中this关键字和super关键字存储的虚拟地址是同一个虚拟之地,都指向同一个对象

java default关键字 AOP java关键字this super_System_15


从而把对象中name属性,由null替换成字符串对象"无名".

此处大家可能不太好理解,其本质是现有对象地址,然后根据对象的地址找到堆中的对象,再执行

private String name = “无名”;

这行代码的赋值.如下图:

java default关键字 AOP java关键字this super_父类_16

那么问题来了,既然都是保存同一个地址,那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);
	}
}

运行结果:

java default关键字 AOP java关键字this super_java_17

断点还是在第五行,重新启动debug模式:

java default关键字 AOP java关键字this super_java default关键字 AOP_18


F5

java default关键字 AOP java关键字this super_编程语言_19


F6

java default关键字 AOP java关键字this super_java_20


F5

java default关键字 AOP java关键字this super_编程语言_21


F5 调用Teacher类中的super(),即Person类的无参构造方法

java default关键字 AOP java关键字this super_System_22


F5

java default关键字 AOP java关键字this super_java_23

F5

java default关键字 AOP java关键字this super_java default关键字 AOP_24


F6

java default关键字 AOP java关键字this super_java default关键字 AOP_25


F6

java default关键字 AOP java关键字this super_java default关键字 AOP_26


F6

java default关键字 AOP java关键字this super_System_27


java default关键字 AOP java关键字this super_java default关键字 AOP_28


this保存堆对象的地址,会通过对象找到堆中对象的name属性进行重新赋值

java default关键字 AOP java关键字this super_编程语言_29


F6

java default关键字 AOP java关键字this super_java default关键字 AOP_30


java default关键字 AOP java关键字this super_编程语言_31


在此,估计大家会问,你怎确定的上面的age而不是给下面age赋值.这也就是为什么又重构代码的原因.因为虚拟机在加载类的时候,会把当前类有继承关系的类全部加载,并在对象中开辟继承来的属性空间和自身类的属性空间,用"域"做标识

java default关键字 AOP java关键字this super_java_32


F5

java default关键字 AOP java关键字this super_java default关键字 AOP_33

F6

java default关键字 AOP java关键字this super_编程语言_34


F6

java default关键字 AOP java关键字this super_编程语言_35


F6

java default关键字 AOP java关键字this super_System_36


F6

java default关键字 AOP java关键字this super_java default关键字 AOP_37


当前执行:this.age = 38; 此时this保存的还是对象地址,但为了大家看的更清晰,我把箭头直接指向了this的"域"

java default关键字 AOP java关键字this super_java_38

F6

java default关键字 AOP java关键字this super_父类_39


当前执行:super.age = 100; 此时this保存的还是对象地址,但为了大家看的更清晰,我把箭头直接指向了super的"域"

java default关键字 AOP java关键字this super_System_40


java default关键字 AOP java关键字this super_java default关键字 AOP_41


F6

F6

java default关键字 AOP java关键字this super_父类_42


java default关键字 AOP java关键字this super_java default关键字 AOP_43

时间比较紧,没办法继续文字说明了,但相信图画的还算清晰,