java 方法重写和构造器

464 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

方法重写

概念

方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

使用场景与案例

发生在子父类之间的关系。 子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方 法。

例如:我们定义了一个动物类代码如下:

public class Animal  {
    public void run(){
        System.out.println("动物跑的很快!");
    }
    public void cry(){
        System.out.println("动物都可以叫~~~");
    }
}

然后定义一个猫类,猫可能认为父类cry()方法不能满足自己的需求

代码如下:

public class Cat extends Animal {
    public void cry(){
        System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
    }
}
​
public class Test {
    public static void main(String[] args) {
        // 创建子类对象
        Cat ddm = new Cat();
        // 调用父类继承而来的方法
        ddm.run();
        // 调用子类重写的方法
        ddm.cry();
    }
}

@Override重写注解

  • @Override:注解,重写注解校验!

  • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。

  • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!

    加上后的子类代码形式如下:

    public class Cat extends Animal {
         // 声明不变,重新实现
        // 方法名称与父类全部一样,只是方法体中的功能重写写了!
        @Override
        public void cry(){
            System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
        }
    }
    

注意事项

  1. 方法重写是发生在子父类之间的关系。
  2. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
  3. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。

继承后的特点—构造器

引入

当类之间产生了关系,其中各类中的构造器,又产生了哪些影响呢? 首先我们要回忆两个事情,构造器的定义格式和作用。

  1. 构造器的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  2. 构造器的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子

继承后子类构造器特点:子类所有构造器的第一行都会先调用父类的无参构造器,再执行自己

案例演示

在前面学习对象的创建流程中,在使用new关键字创建对象的时候,先在堆中给对象分配内存空间,接着给非静态的成员变量进行默认初始化,开始调用对应的构造函数。而在执行构造函数中有隐式的三步:

1、super(); 它是在调用自己的父类空参数的构造函数。

2、非静态成员变量显示赋值。

3、构造代码块运行。

4、本构造函数中的代码运行。

 

需求:

根据以下代码画图说明子父类构造函数的特点。

1)创建一个子类Zi和一个父类Fu,让这个子类来继承这个Fu类;

2)在Fu类中定义一个成员变量x并赋值为3,然后创建Fu类构造代码块,在这个Fu类代码块中打印x的值;

3)在Fu类中定义一个一般函数show,也打印x的值;

4)在Fu类中定义一个无参构造函数,随便打印一句话;

5)在Zi类中定义一个成员变量y并赋值为10,同样定义一个构造代码块,分别写两个打印语句打印x和y的值;

6)在子类中复写父类中show函数,并写两句语句,分别打印x和y的值;

7)在子类中定义一个构造函数,使用super()调用父类无参构造函数,在子类构造函数中打印一句话,调用show函数;

8)定义一个测试类ConstructorDemo ,在这个类中创建Zi类的对象;

 

//继承中的构造函数细节
class Fu
{
    int x = 3;
    {
        System.out.println("Fu  构造代码块  x="+x);
    }
    Fu()
    {
        //super();
        System.out.println("Fu的构造函数执行");
    }
    void show()
    {
        System.out.println("Fu show x="+x);
    }
}
class Zi extends Fu
{
    int y = 10;
    {
        System.out.println("Zi 的构造代码块  x="+x);
        System.out.println("Zi 的构造代码块  y="+y);
    }
    Zi()
    {
        super();
        System.out.println("Zi的构造函数执行。。。。。");
        show();
    }
    void show()
    {
        System.out.println("Zi show x="+x);
        System.out.println("Zi show y="+y);
    }
}
​
class ConstructorDemo 
{
    public static void main(String[] args) 
    {
        Zi z = new Zi();
    }
}

注意:

1、在每个类中都调用父类中的构造函数,是因为子类继承了父类,子类要使用父类中的成员变量,所以调用父类中的构造函数是为了父类中的成员变量显示初始化使用的;

2、上述代码中由于父类的构造函数是Zi类对象来调用的,所以Fu类中的构造函数中this变量记录着Zi类对象的内存地址名;

上述代码的内存执行过程如下所示:

继承中构造方法图解.jpg

小结

  • 子类构造器执行的时候,都会在第一行默认先调用父类无参数构造器一次。
  • 子类构造器的第一行都隐含了一个super() 去调用父类无参数构造器,super() 可以省略不写。