您的位置:首页 > 编程语言 > Java开发

Java拾遗补阙 ----- Super、This关键字使用总结

2017-09-29 01:45 369 查看

一、前言:

该总结因"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关键字呢?

    1、 当在方法内需要用到调用该方法的对象时,就用this。

    2、当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量是类成员

    3、在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性

三、super详解

1、super

super不是一个对象的引用,不能将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的区别

从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。(这个跟第五部分有矛盾,详见第五部分中的粗体部分)

    1、访问属性

        this访问本类中的属性,如果本类没有此属性则从父类中继续查找;super访问父类中的属性

    2、调用方法

        this访问本类中的方法;super直接访问父类中的方法

    3、调用构造器

        this调用本类构造器,必须放在构造器的首行;super调用父类构造器,必须放在子类构造器的首行

    4、特殊

        this表示当前对象;super无此概念

   5、两者都不能用于static中


五、详细说明

1、super

在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象。怎么去引用里面的父类对象呢?使用super来引用,this指的是当前对象的引用,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。

到此,整个内存分析就结束了,最终内存显示的结果如上面所示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: