java继承中谈“super”


三、父类成员访问

在继承体系中,子类将父类中的方法和字段继承下来了,那在子类中能否直接访问父类中继承下来的成员呢?

1.子类中访问父类的成员变量

先从简单的开始(1.基类和子类没有重复的变量):
写一个Base类:

public class Base {
    int a=1;
    int b=2;
}

再写一个Derived类:

public class Derived extends Base{
    int c;
    //Derived类在没有新增a和b成员之前
    public void method(){
        //a和b访问的就是从基类中继承下来的a和b
        a=10;
        b=20;
        c=30;  //c访问的就是自己当中的成员
    }
}

2.子类和基类成员变量同名
Base类不变,Derived类:

public class Derived extends Base{
    int c;

    //子类和基类成员变量同名了
    int a;   //名字和类型均相同
    byte b;  //名字一样,类型不同

    //Derived增加了a和b成员之后

    public void method2(){
        //a和b访问的就是从基类中继承下来的a和b
        a=10;
        b=20;
        c=30;  //c访问的就是自己当中的成员
    }
}

问题1:现在Derived有将基类中的a和b继承下来么?
问题2:如果继承下来,下面的a和b访问的是子类的还是基类的?

我们可以创建一个子类对象下断点调试看看(如下图):
在这里插入图片描述
在这里插入图片描述
所以问题一:已经继承下来了。
调试运行完成结果为图中的右图:
在这里插入图片描述
所以问题二:a和b访问的是子类的。
对图中的最后一句话进行解释:
在这里插入图片描述
那么如果在子类方法中,想要访问从基类继承下来的同名的成员呢?应该怎样操作:
在基类中写一个set方法:

public class Base4 {
    int a=1;
    int b=2;

    void setA(int a){
      this.a=a;
    }
}

在这里插入图片描述
这个super.a这种语法就是告诉编译器,去找父类中的成员进行访问。
总结:
1.在子类方法中或者通过子类对象访问成员时:
①如果访问的成员变量子类中有,优先访问自己的成员变量。
②如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
③如果访问的成员变量与父类成员变量同名,则优先访问自己的,即:子类将父类同名成员隐藏了。
2.成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。 比如:你和你父亲各自有一款相同的手机,平时使用时你肯定优先用自己的,如果自己手机没电了,你才会考虑使用你父亲的。

2.子类访问父类的成员方法

1.成员方法名字不同:
这里和成员变量名字不同是一样的,这里不再作过多阐述。
2.成员方法名字相同:
基类:

//验证子类和父类具有同名的方法
public class Base5 {
    public void methodA(){
        System.out.println("Base--->methodA()");
    }

    public void methodB(){
        System.out.println("Base--->methodB()");
    }
}

子类:

public class Derived5 extends Base5{
    //与基类中methodA()参数列表不同
    public void methodA(int a){
        System.out.println("Derived--->methodA()");
    }

    //与基类中methodB()原型一致
    public void methodB(){
        System.out.println("Derived--->methodB()");
    }

    void testMethod(){
        //关系:构成了方法的重载
        methodA(10);    //子类自己新增加的methodA
        methodA();         //从基类中继承的methodA
        super.methodA();

        //后续所看到的重写---多态
        methodB();        //默认调用自己的
        super.methodB();  //加了super关键字之后,告诉编译器调用Base类中的methodB
    }

    public static void main(String[] args) {
        Derived5 d=new Derived5();
        d.methodA(100);
        d.methodB();
        d.methodA();

        d.testMethod();
    }
}

运行结果为:
在这里插入图片描述
说明:
1.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
2.通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法中传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写-后面讲),则只能访问到子类的,父类的无法通过派生类对象直接访问到。

四、super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。
在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。
注意事项:
1.只能在非静态方法中使用
在这里插入图片描述
在这里插入图片描述

2.在子类方法中,访问父类的成员变量和方法
super的其他用法在后面会介绍。

五、子类构造方法

父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法(从严谨性方面来讲这句话是有问题的,后面会进行解释)。
代码如下(示例):
父类:

public class Fu4 {
    int a;
    int b;
    
    public Fu4(){
        System.out.println("Fu4()");
    }
}

子类:

public class Zi4 extends Fu4{
    int c;
    int d;
    public Zi4(){
        System.out.println("Zi4()");
    }

    public static void main(String[] args) {
        Zi4 zi=new Zi4();
    }
}

在这里插入图片描述
在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象是一个父类对象,先要将从父类继承下来的成员初始化完整,然后再初始化子类自己新添加的成员。
注意:
1.若父类显式定义无参或者默认的构造方法,此时子类的构造方法可以不用定义,如果需要再定义,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法。
在这里插入图片描述
假如我们有需求例如下图:
在这里插入图片描述
可以根据需求进行自定义。
在这里插入图片描述
原因是子类构造器中隐含了一个super。super会告诉编译器,去调用基类的构造方法,将子类对象中从基类中继承下来的部分先构造完成。
注意:不能直接调用Fu():
在这里插入图片描述
如果用户自己没有显式提供super(),则编译器会自动添加。
运行过程分析:
在这里插入图片描述
2.如果父类构造方法是带有参数的,此时编译器不会(不是不会生成,是没有能力生成了)再给子类生成默认的构造方法,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
首先我们再创建一个包,里面建一个父类和一个子类:
父类:

public class Fu5 {
    int a;
    public Fu5(int a){
        this.a=a;
    }
}

子类:

public class Zi5 {
}

编译错误:
在这里插入图片描述
原因是:父类已经显式定义带有参数的构造方法了,在父类中无参的构造方法编译器就不会生成,此时super()就需要传一个参数进去,但是编译器不知道传什么。 此时,就需要用户自己显示来定义子类的构造方法,并且在构造方法中显式调用基类无参的构造函数。
Zi(参数列表)
{
1.super(参数);
2.初始化自己的成员;
}
父类:

public class Fu5 {
    int a;

    //Fu类没有无参的构造方法
    public Fu5(int a){
        this.a=a;
        System.out.println("Fu");
    }
}

子类:

public class Zi5 extends Fu5{

    /*
    一个类如果没有显式定义构造方法,则编译器会生成
    Zi()
    {
         super();   //父类已经显式定义带有参数的构造方法了,在父类中无参的构造方法编译器就不会生成
    }
    在Zi()类构造方法中,没有办法调用基类无参的构造方法---则此时子类无参的构造方法就没有办法生成了,只能用户自己显示提供
     */

    Zi5(int data){
        //1.调用基类构造方法,完成从基类继承下来成员的构造
        super(100);
        //2.再构造自己的成员变量
    }
}

3.在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数第一条语句。
super和this很像,this只能是构造方法的第一条语句,super也是一样:
在这里插入图片描述
在这里插入图片描述
4.super(…)只能在子类构造方法中出现一次,并且不能和this同时出现。
在这里插入图片描述
类似于人的出生只能出生一次。
在这里插入图片描述
在这里插入图片描述

总结:
在这里插入图片描述
在这里插入图片描述
下面这种方法是比较合理的:
Zi类:

public class Zi7 extends Fu7{
    int d;

    public Zi7(int a,int b,int d){
        super(a,b);    //注意:super必须是构造方法中的第一条语句
        this.d=d;
        System.out.println("Zi()");
    }

    public static void main(String[] args) {
        Zi7 zi=new Zi7(10,20,30);
    }
}

六、super和this

super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那么它们有什么区别呢?

1.相同点

1.都是java中的关键字
2.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
在这里插入图片描述
3.在构造方法中使用,必须是构造方法中的第一条语句,并且不能同时存在

2.不同点

1.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
在这里插入图片描述
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3.this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
证明方法1:
在这里插入图片描述
证明方法2:
在这里插入图片描述
4.成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this来访问的;在子类中如果通过super访问父类成员,编译之后在字节码层面super实际上是不存在的(通过字节码文件可以验证)
在这里插入图片描述
5.在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造方法中出现
6.构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dhdhdhdhg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值