一、前言:
该总结因"Java super关键字调用父类的方法疑惑"而成。
该疑惑为:
子类通过super.printA()调用父类的方法,而在父类的printA()中调用了printB(),这种情况下,调用的是子类的printB()方法。
public class SuperClass{
public void printA(){
System.out.print("SuperClass-printA\n");
System.out.println(this.getClass().getName());
printB(); //隐藏了this 可以写成this.printB()
}
public void printB(){
System.out.print("SuperClass-printB\n");
}
}
public class ChildClass extends SuperClass{
public void printA(){
System.out.print("ChildClass-printA\n");
System.out.println(this.getClass().getName());
super.printA();
}
public void printB(){
System.out.print("ChildClass-printB\n");
}
public static void main(String[] args)
{
ChildClass childClass = new ChildClass();
childClass.printA();
//SuperClass sc = new ChildClass();
//sc.printA();
}
}
运行结果为:
ChildClass-printA
com.wqc.test.ChildClass
SuperClass-printA
com.wqc.test.ChildClass
ChildClass-printB
从结果可以看出,执行super.printA()语句还是childClass对象。
疑惑原因:
困惑可能就是出在了对this关键字的理解上,此时的childClass对象的类型为ChildClass(此时this关键字指向类型为ChildClass的childClass对象),调用printA()方法首先输出ChildClass-printA,代码继续执行,遇到super.printA(),走到父类的printA()方法中,首先按照顺序执行到printB(),因为方法里有 一个隐藏的this指针,所以可以把printB()看作this.printB(),上面说过此时的this指向的是类型为ChildClass的childClass对象 所以去到ChildClass中去找printB()方法,如果有printB()就去执行,如果没有就父类中找(也就是SuperClass类中的printB()),如果SuperClass类中还没有printB()方法(当然SuperClass中一定会有,否则代码编译不过),就会继续向上找,直到Object类。 所以按照现在这样的情况,printB() 真正执行的代码是ChildClass类中的printB()方法,对应输出为ChildClass-printB。
二、this详解
1、this
this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。
2、作用
在Java中,this关键字的作用和其词义很接近.
1)、它在方法内部使用,即这个方法所属对象的引用;(这个可以解释前言中的疑惑)
2)、普通方法中,this总是指向调用该方法的对象;
3)、它在构造器内部使用,表示该构造器正在初始化的对象;
4)、构造方法中,this总是指向正要初始化的对象;
5)、this表示当前对象,this可以用来修饰属性、方法、构造器
6)、this理解为当前对象或当前正在创建的对象。比如:this.name,this.info();
3、注意
1)、this不能用于static方法,在static方法中不可以访问非static的成员。
2)、通过this调用其它构造方法,必须位于方法第一句。
4、什么时候使用this关键字呢?
- 当在方法内需要用到调用该方法的对象时,就用this。
- 当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量是类成员
- 在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
三、super详解
1、super
错误理解:super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。
2、作用
Java中的super关键字,在Java类中使用super关键字来调用父类中的指定操作:
1)、super可用于访问父类中定义的属性
2)、super可用于调用父类中定义的成员方法
3)、super可用于在子类构造方法中调用父类的构造器
3、注意
1)、尤其当子类出现同名成员时,可以用super进行区分
2)、super的追溯不仅限于直接父类,还可以是上上层父类
3)、super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
4)、super不能用于static方法,在static方法中不可以访问非static的成员。
4、什么时候使用super
1)、当子类与父类中有同名的属性时,可以通过"super.属性"显示的调用父类中声明的属性
2)、若想调用子类的同名的属性,可以通过"this.属性"
3)、当子类重写父类的方法以后,在子类中若想再显示的调用父类的被重写的方法,就需要使用"super.方法"
4)、super修饰构造器:通过在子类中使用"super(形参列表)"来显示的调用父类中指定的构造器:
在构造器内部,“super(形参列表)”必须要声明在首行
在构造器内部,“this(形参列表)”或“super(形参列表)”只能出现一个
当构造器中,不显示的调用“this(形参列表)”或“super(形参列表)”其中任何一个,默认调用的是父类空参的构造器
四、this和super的区别
1、访问属性
this访问本类中的属性,如果本类没有此属性则从父类中继续查找;super访问父类中的属性2、调用方法
this访问本类中的方法;super直接访问父类中的方法3、调用构造器
this调用本类构造器,必须放在构造器的首行;super调用父类构造器,必须放在子类构造器的首行4、特殊
this表示当前对象;super无此概念
5、两者都不能用于static中
五、详细说明
1、super
class Student {
public int age;
public void std(){ //声明Student类的方法std()
age = 15;
System.out.println("学生平均年龄为:"+age);
}
}
class ThisStudent extends Student{
public int age;
public void std(){
super.std(); //使用super作为父类对象的引用对象来调用父类对象里面的Std()方法
age = 18;
System.out.println("这个学生的年龄为:"+age);
System.out.println(super.age); //使用super作为父类对象的引用对象来调用父类对象中的age值
System.out.println(age);
}
}
public class TestDif {
public static void main(String[] args) {
ThisStudent a = new ThisStudent();
a.std();
}
}
执行结果:
学生平均年龄为:15
这个学生的年龄为:18
15
18
2、分析
ThisStudent a = new ThisStudent();
程序执行到这里时,首先在栈空间里面会产生一个变量a,a里面的值是什么这不好说,总而言之,通过这个值我们可以找到new出来的ThisStudent对象。由于子类ThisStudent是从父类Student继承下来的,所以当我们new一个子类对象的时候,这个子类对象里面会包含有一个父类对象,而这个父类对象拥有他自身的属性age。这个age成员变量在Student类里面声明的时候并没有对他进行初始化,所以系统默认给它初始化为0,成员变量(在类里面声明)在声明时可以不给它初始化,编译器会自动给这个成员变量初始化,但局部变量(在方法里面声明)在声明时一定要给它初始化,因为编译器不会自动给局部变量初始化,任何变量在使用之前必须对它进行初始化。
子类在继承父类age属性的同时,自己也单独定义了一个age属性,所以当我们new出一个子类对象的时候,这个对象会有两个age属性,一个是从父类继承下来的age,另一个是自己的age。在子类里定义的成员变量age在声明时也没有给它初始化,所以编译器默认给它初始化为0。因此,执行完第一句话以后,系统内存的布局如下图所示:
a.std();
当new一个对象出来的时候,这个对象会产生一个this的引用,这个this引用指向对象自身。如果new出来的对象是一个子类对象的话,那么这个子类对象里面还会有一个super引用,这个super指向当前对象里面的父对象。所以相当于程序里面有一个this,this指向对象自己,还有一个super,super指向当前对象里面的父对象。
这里调用重写之后的std()方法,方法体内的第一句话:“super.std();”是让这个子类对象里面的父对象自己调用自己的f()方法去改变自己age属性的值,父对象通过指向他的引用super来调用自己的std()方法,所以执行完这一句以后,父对象里面的age的值变成了15。接着执行“age=18;”这里的age是子类对象自己声明的value,不是从父类继承下来的那个age。所以这句话执行完毕后,子类对象自己本身的age值变成了18。此时的内存布局如下图所示:
方法体内的最后三句话都是执行打印age值的命令,前两句打印出来的是子类对象自己的那个age值,因此打印出来的结果为18,最后一句话打印的是这个子类对象里面的父类对象自己的age值,打印出来的结果为15。
到此,整个内存分析就结束了,最终内存显示的结果如上面所示。