Java的继承到底是怎么回事?看这篇让你明明白白

3552 篇文章 114 订阅

一. 引言

在学习面向对象后,我们就可以使用类来描述对象共有的特征(属性)和行为举止(方法),如果我们用类来描述猫、狗和企鹅,可以进行如下编码:

public class Cat {
    
    private String name;//名字
    
    private int age;//年龄
    
    private String strain; //品种
    //省略getter和setter方法
}

public class Dog {
    
    private String name;//名字
    
    private int age;//年龄
    
    private String strain; //品种
    //省略getter和setter方法
}

public class Penguin{
    
    private String name;//名字
    
    private int age;//年龄
    
    private String sex; //性别
    //省略getter和setter方法
}
复制代码

你会发现,在上面的3个类中存在大量的重复代码,那么该如何优化呢?这里我们可以使用继承来优化

二. 继承

1. 什么是继承

当我们编写的多个类中存在相同的属性或方法时,可以将这些类中相同的属性和方法抽取到一个新的类中,然后再让这些类继承于这个新类,就可以重用新类中的属性和方法,这些类称为子类,这个新类称为父类,这就是Java中的继承。

2. 如何使用继承

我们将上面的案例抽取出一个父类如下:

public class Animal {
    
    String name;//名字
    
    int age;//年龄
}
复制代码

接下来只需要继承于这个父类即可。在Java中,继承使用extends关键字 来表示,上面的三个类修改如下:

public class Cat extends Animal{//猫类继承于动物类
    
    private String strain; //品种
     //省略getter和setter方法
}

public class Dog extends Animal{//狗类继承于动物类
    
    private String strain; //品种
     //省略getter和setter方法
}

public class Penguin extends Animal{//企鹅类继承于动物类
    
    private String sex; //性别
     //省略getter和setter方法
}
复制代码

接下来我们可以编写一个测试案例。

public class AnimalTest {

    public static void main(String[] args) {
        Cat c = new Cat();
        c.setName("苗苗");
        c.setAge(1);
        c.setStrain("咖啡猫");
    }
}
复制代码

我们发现,在 Cat 类中并没有 setNamesetAge 方法,但却可以直接使用这些方法,说明这两个方法都是从父类中继承过来的,其他两个类也一样。

3. 子类能够继承父类的哪些属性和方法

3.1 API文档

官方文档中有这样的描述:

A subclass inherits all of the public and protected members of its parent, no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the parent.

解释说明:

不论子类与父类是否在同一个包中,父类中使用 public 或者 protected 修饰的属性和方法,都能够被子类继承。如果子类和父类在同一个包中,那么子类还能继承受包保护的属性和方法(受包保护指的是没有使用访问修饰符的属性和方法)。

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.

解释说明:

子类不会继承父类中定义的私有成员。但如果父类有提供使用 public 或者 protected 修饰的访问该字段的方法,这些方法也能在子类中被使用。

为了验证这些文档中的说法是否正确,接下来我们通过几个案例来进行验证。

3.2 案例一

public class Animal {
    
    private String name;//修改访问修饰符为private
    
    int age;//年龄
    //省略getter和setter方法
}

public class Cat extends Animal {

    private String strain;

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    public void show(){ //添加一个show方法,打印name和age属性
        System.out.println(name + age);
    }
}
复制代码

此时编译也出错,name 属性不能访问,但 age 属性可以。

该案例说明了父类中私有的属性不能被继承,但受包保护的属性可以被继承

3.3 案例二

package com.qf.oop; //给父类添加包名

public class Animal {
    
    private String name;//修改访问修饰符为private
    
    int age;//年龄
    //省略getter和setter方法
}

package com.qf.oop.sub; //给子类添加包名

import com.qf.oop.Animal;

public class Cat extends Animal {

    private String strain;

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    public void show(){
        System.out.println(name + age);
    }
}
复制代码

此时编译也出错,nameage 属性都不能访问。

该案例说明了在不同的包中,子类不能继承父类中的私有属性和受包保护的属性。

3.4 案例三

package com.qf.oop;

public class Animal {

    public String name; //修改访问修饰符为public

    protected int age; //修改访问修饰符为protected
    
    //省略getter和setter方法
}


package com.qf.oop.sub;

import com.qf.oop.Animal;

public class Cat extends Animal {

    private String strain;

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    public void show(){
        System.out.println(name + age);
    }
}
复制代码

此时编译正常。

该案例说明了在不同的包中,子类可以继承父类中公开的属性和受保护的属性 方法的继承与属性的继承规则一致。

三. 方法重写

1. 什么是方法重写

方法重写指的是在具有继承关系的子类中,如果存在一个成员方法,与父类中的成员方法有相同的签名和返回值类型,那么,这个方法就重写了父类中的成员方法,称为方法重写。

方法签名指的是方法名、参数类型和参数个数。

相同的方法签名指的是方法名、参数类型、参数个数和参数出现的位置均相同。

2. 为什么要使用方法重写

我们知道方法表示的是行为举止,子类继承父类,当然也可以继承父类的行为举止。但我们经常会遇到子类和父类做一件事情的方式不一样,也就是说,子类中的方法实现和父类中的方法实现存在差别。例如:动物会吃食物,但不同的动物吃的食物也不一样。 

public class Animal{
    
    public void eat(){
        System.out.println("动物吃食物");
    }
}

public class Cat extends Animal{
   
   public void eat(){
       System.out.println("猫吃鱼");
   }
}
复制代码

子类 Cat 中的 eat 方法与父类 Animal 中的 eat 方法具有相同的签名和返回值类型,这样的方法我们称其为重写了父类中eat 方法。

为了方便查看重写的方法,Java 提供了 @Override 注解来标识。这个注解仅仅就是一个标识,写与不写都不影响。

public class Cat extends Animal{

   @Override //重写的方法的一个标识
   public void eat(){
       System.out.println("猫吃鱼");
   }
}
复制代码

3. 如何使用方法重写

现有这样一个场景:几何图形都有面积和周长,不同的几何图形,面积和周长的算法也不一样。矩形有长和宽,通过长和宽能够计算矩形的面积和周长;圆有半径,通过半径可以计算圆的面积和周长。请使用继承相关的知识完成程序设计。

package com.qf.oop.shape;

/**
 * 几何图形
 */
public class Shape {
    /**
     * 计算周长
     * @return
     */
    public double calculatePerimeter(){
        return 0;
    }

    /**
     * 计算面积
     * @return
     */
    public double calculateArea(){
        return 0;
    }
}

package com.qf.oop.shape;

/**
 * 矩形
 */
public class Rectangle extends Shape {

    private int width;

    private int length;

    public Rectangle(int width, int length) {
        this.width = width;
        this.length = length;
    }

    @Override
    public double calculatePerimeter() {//重写计算周长的方法
        return (width + length) * 2;
    }

    @Override
    public double calculateArea() {//重写计算面积的方法
        return width * length;
    }
}

package com.qf.oop.shape;

/**
 * 圆
 */
public class Circle extends Shape{

    private int radius;

    public Circle(int radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {//重写计算面积的方法
        return Math.PI * radius * radius;
    }

    @Override
    public double calculatePerimeter() {//重写计算周长的方法
        return 2 * Math.PI * radius;
    }
}

package com.qf.oop.shape;

public class ShapeTest {

    public static void main(String[] args) {
        Shape s1 = new Rectangle(10, 9);
        System.out.println(s1.calculatePerimeter());
        System.out.println(s1.calculateArea());


        Shape s2 = new Circle(5);
        System.out.println(s2.calculatePerimeter());
        System.out.println(s2.calculateArea());
    }
}
复制代码

重写方法时访问修饰符的级别不能降低。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值