目录
一、super用法介绍
在Java中,super是一个关键字,主要用于访问父类的成员(方法、构造函数、变量)。它在继承的上下文中非常重要,以下是super的常见用法和场景
1. 访问父类的成员变量
如果子类和父类中存在同名的成员变量,子类可以通过super
关键字来访问父类的成员变量。
class Parent {
int num = 10;
}
class Child extends Parent {
int num = 20;
void display() {
System.out.println(num); // 输出子类的num,结果为20
System.out.println(super.num); // 输出父类的num,结果为10
}
}
2. 调用父类的方法
如果子类和父类中存在同名的方法,子类可以通过super
关键字来调用父类的方法。
class Parent {
void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
void show() {
System.out.println("Child show");
}
void display() {
show(); // 调用子类的show方法,输出Child show
super.show(); // 调用父类的show方法,输出Parent show
}
}
3. 调用父类的构造方法
在子类的构造方法中,若没有显式调用父类的构造方法,则会隐式调用,相当于在子类构造函数的第一行加上super(),但这样的方式编译器只能调用父类无参的构造函数,若想调用含参的,就需要在子类的构造方法中,通过super(参数)
来显式调用父类的构造方法。
public class People
{
protected String name;
public People(String name)
{
this.name = name;
}
}
class Student extends People
{
private String id;
public Student(String name, String id)
{
super(name);
this.id = id;
}
public String toString()
{
return "姓名:"+name+" 学号:"+id;
}
public static void main(String[] args)
{
Student s=new Student("小明","2310");
System.out.println(s);
}
}
输出
二、super本质理解
说到super的本质,现在学术界主要有两种观点:第一种,super是对象引用,表示父类对象;第二种,super是一种关键字,与父类对象没有直接关系,只是能够实现一些特定功能。
下面我将带着你逐渐揭开super的真面目
我们先来看几个真实的对象引用的例子,以下s变量是真实学生对象的引用
public class People
{
public String name;
public People(String name)
{
this.name = name;
}
}
class Student extends People
{
private String id;
public Student(String name, String id)
{
super(name);
this.id = id;
}
public String toString()
{
return "姓名:"+name+" 学号:"+id;
}
public String getId()
{
return id;
}
public static void main(String[] args)
{
Student s=new Student("小明","2310");//此处s是学生对象的引用,即“管理者”
System.out.println(s.name);//成员变量,输出小明
System.out.println(s.getId());//成员函数,输出2310
System.out.println(s);//输出姓名:小明 学号:2310
}
}
运行结果
乍一看,上面几个例子中,super不也是这样吗,super.num,super.show(),甚至在显式调用父类构造函数的时候写的也是super(name),这不就跟People(name)一样吗,所以super是父类对象引用
先说结论,super不是父类对象的引用,而只是一个关键字,其中存储了指向父类部分的地址,使之能够完成与父类相关的操作
我们来看证据
(1)我们知道,在Java中,任何对象都是Object类的子类,那么根据向上造型(上转型),任何对象应该都可以“赋值”给Object,即由Object父类管理,如上一个例子,就可以这样写
Object s=new Student("小明","2310");
或这样
Student s=new Student("小明","2310");
Object o=s;
经过测试,这都是没问题的,只是这样一来s只能使用Object父类的东西(这里牵扯到上转型的知识点,与我们的问题无关,不再赘述)
那么,如果super是父类对象的引用, 它也应该可以这样写
public Student(String name, String id)
{
super(name);
Object o=super;
this.id = id;
}
但是实际上这是不行的,编译都过不去
报错
编译器必须让你加个“.” ,也就是super.name这样的形式才行,可见,super并不是父类对象的引用(2)这部分与(1)类似,这次我们通过toString方法来区别对象引用和super关键字
我们知道,Object类中有一个toString函数,用来描述对象的信息,则对于Object类的子类,若不重写toString函数,使用System.out.println时输出的会是 类名@哈希码 这样的形式,重写后则会根据你的想法输出对象相关的信息
先看真实对象引用,这里我重写了toString函数
public class People
{
public String name;
public People(String name)
{
this.name = name;
}
}
class Student extends People
{
private String id;
public Student(String name, String id)
{
super(name);
this.id = id;
}
public String toString()
{
return "姓名:"+name+" 学号:"+id;
}
public static void main(String[] args)
{
Student s=new Student("小明","2310");//此处s是学生对象的引用,即“管理者”
System.out.println(s);//输出姓名:小明 学号:2310
}
}
那么,super是不是也能通过println函数输出呢?我们来试一下
public Student(String name, String id)
{
super(name);
System.out.println(super);
this.id = id;
}
可见,也是不行的,这再次证明了,super不是父类对象的引用
(3) 在结论中我说,super中存储了指向父类部分的地址,使之能够完成与父类相关的操作,这如何证明?
如果你学过c语言,拿指针的概念来理解super会很合适,我们来看例子
还是上面那个People和Student的例子,我在构造函数中加了一个断点,我们来看调试
可见,super和this的首地址是一样的
可见,super.name和this.name地址相同,是一个唯一的name
由此,我们可以做一些合理的推断
Student对象在内存中的情况如下
可见,super可以理解为一个指针,其中存储了子类中父类部分的地址(不是对象,子类对象中没有父类对象),因此,通过这个地址,super就可以直接访问到父类的成员变量和方法,上述的例子也就不难理解了,然而,作为一个地址的“存储器”,super不能作为对象赋值和输出也情有可原了