需求:在子类中某一个方法中,去调用父类被覆盖的方法
此时,就需要使用到super关键字
什么是super:
this:当前对象,谁调用this所在的方法,this就是哪一个对象;
super:当前对象的父类对象;
class Bird
{
public void fly(){
System.out.println("fly");
}
}
class Penguin extends Bird
{
public void fly(){
System.out.println("Can't fly");
}
public void say(){
System.out.println("I am singing");
super.fly(); //调用父类中的fly方法
}
}
子类初始化过程:创建对象子类对象的过程。
(1)在创建子类对象之前,会首先创建父类对象。
class Person
{
private String name;
private int age;
Person(){
System.out.println("父类构造器");
}
}
class Student extends Person
{
private int studentID;
Student(){
//这里会存在一个隐式的super(),表示调用了父类构造器
System.out.println("子类构造器");
}
}
class SubClassInitDemo
{
public static void main(String[] args)
{
Student s = new Student(); //创建一个学生对象
}
}
---------- 运行java ----------
父类构造器
子类构造器
输出完成 (耗时 0 秒) - 正常终止
注: 子类Student是无法继承到父类Person中私有的name和age字段的,但是有的书籍记载:private成员是可以继承的,只不是不能呗外界访问,我们承认该观点,但是因为访问不到,我们可以简单地认为private成员不能被子类继承。
因此,在创建字类对象的时执行属性是:先进入子类构造器,然后在构造器中会先调用父类构造器(创建父类对象),再执行子类构造器代码。
调用子类构造器之前,在子类构造器中会先调用父类构造器,默认调用的是父类的无参数构造器。
class Person
{
private String name;
private int age;
Person(String name){
System.out.println("父类构造器");
}
}
class Student extends Person
{
private int studentID;
//这里会报错,因为在子类构造器中会先调用父类构造器,默认调用的是父类的无参数构造器
// 因为父类中,不存在无参数构造器
Student(){
System.out.println("子类构造器");
}
}
---------- 编译java ----------
SubClassInitDemo.java:13: 错误: 无法将类 Person中的构造器 Person应用到给定类型;
Student(){
^
需要: String
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
1 个错误
(2)如果父类不存在可以被子类访问的构造器,则不能存在子类。
也是就说父类中的所有构造器都被私有化了,就不存在继承它的子类了;
class Person
{
private String name;
private int age;
private Person(){ //构造器被私有化
System.out.println("父类构造器1");
}
}
class Student extends Person
{
private int studentID;
Student(){ // 编译报错,因为父类构造器被私有化了
System.out.println("子类构造器");
}
}
---------- 编译java ----------
SubClassInitDemo.java:14: 错误: Person() 在 Person 中是 private 访问控制
Student(){
^
1 个错误
输出完成 (耗时 0 秒) - 正常终止
(3)如果父类没有提供无参数构造器,此时子类必须显示通过super语句去调用父类带参数的构造器。
class Person
{
private String name;
private int age;
Person(String name){
this.name = name;
System.out.println("父类构造器");
}
}
class Student extends Person
{
private int studentID;
Student(){
super("Liming");
System.out.println("子类构造器");
}
}
在子类中如何获取父类的私有(private修饰)成员变量,代码如下:
class Person
{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
class Student extends Person
{
private int studentID;
Student(String name, int age, int studentID){
//调用父类构造器这句话必须作为子类构造器的第一句话
super(name,age);
this.studentID = studentID;
}
public int getStudentID(){
return studentID;
}
public void print(){
//其实,下面的super关键字可以不写,因为子类中没有getName和getName方法,
// 在子类中找不到的话,会自动去父类中寻找这两个方法
System.out.println("name="+super.getName()+",age="+super.getName()+",studentID="+this.getStudentID());
}
}
class SubClassInitDemo
{
public static void main(String[] args) {
Student s = new Student("Liming",18,10475); //创建一个学生对象
s.print();
}
}
---------- 运行java ----------
name=Liming,age=18,studentID=10475
输出完成 (耗时 0 秒) - 正常终止
super关键字的使用场景:
(1)可以使用super解决子类隐藏了父类的字段情况,如下代码。但是,该情况我们一般不讨论,因为破坏了封装。
class Person{
public String name = "will";
}
class Student extends Person{
public int name = 18; //隐藏了父类中的name字段
public void printName(){
System.out.println(name); // 18
Sytem.out.println(super.name);// will
}
}
(2)在子类方法中,调用父类被覆盖的方法,如引出super的例子代码,此时必须使用super。
(3)在子类构造中,调用父类构造器,此时必须使用super语句:super(实参)。如上上述子类初始化过程的(3)代码。
什么是“隐藏现象 ” :
所谓“隐藏”就是“遮蔽”的意思
(1)满足继承的访问权限下,隐藏父类静态方法:若子类定义的静态方法的签名和父类中的静态方法的签名相同,那么此时就是隐藏父类的方法。注意:仅仅是静态方法。
如下代码:在父类和子类中都存在了一个static修饰的dowork方法,这里的dowork是隐藏的概念,而不是覆盖 !在多态中会再次提到。
class Person{
public String name = "will";
public static void dowork(){}
}
class Student extends Person{
public int name = 18; //隐藏了父类中的name字段
public void printName(){
System.out.println(name); // 18
Sytsem.out.println(super.name);// will
}
public static void dowork(){}
}
(2)满足继承的访问权限下,隐藏父类字段:若子类中定义的字段和父类中的字段名相同(不管类型),此时就是隐藏父类字段,此时只能通过super关键字访问被隐藏的字段。
代码示例如super关键字的使用场景(1)中的代码。
(3)隐藏本类字段:若同类中某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段。
如下代码中,父类中存在String 类型name字段,子类中int类型的name字段,在字段的printName方法中又存在了一个boolean类型的局部变量name,那么根据程序的就近原则打印出来的接错依次是false,18,will
class Person{
public String name = "will";
}
class Student extends Person{
public int name = 18; //隐藏了父类中的name字段
public void printName(){
boolean name = false;
System.out.println(name); // false
System.out.println(this.name); // 18
System.out.println(super.name);// will
}
}
class SuperDemo2{
public static void main(String[] agrs){
Student s = new Student();
s.printName();
}
}
---------- 运行java ----------
false
18
will
输出完成 (耗时 0 秒) - 正常终止
注意:
static不能与super和this关键字共存,这是因为super和this都是对象级别的,而static修饰的是类级别