【转】JAVA基础:一个案例搞懂类、对象、重载、封装、继承、多态、覆盖、抽象和接口概念及区别(中篇)

本文介绍了面向对象编程中的继承、覆盖和多态概念。通过面积计算器的案例展示了如何使用继承减少代码重复,以及如何通过覆盖实现特定类的方法重写。最后,利用多态简化了对象调用的代码,提高了代码的可维护性和可扩展性。
摘要由CSDN通过智能技术生成

写在前面:

转载自:知乎  飞爱学习  作者:一个敲代码的汽车人

原文链接: https://zhuanlan.zhihu.com/p/139033198

这篇主要讲的是继承extends和覆盖override。

不管是继承、覆盖还是多态,甚至类、抽象类、接口,都是在体现一种工程化实现的设计思想。

如果能从做工程、多方协作的设计师的视角去看待这些概念,你会更容易理解为什么要创造出来这些概念,为什么用这些概念或方法就能达到工程代码的易扩展、易维护的目的。


1 继承(Inheritance)

1.1 案例

咱们稍微加一点难度。
现在要求设计一个面积计算器,计算的对象不仅仅是矩形,还包括平行四边形。输入仍然为高和宽(假定都是规范的数值输入),输出为面积。

1.2 代码

为了简化代码,前面已经实现过的内容(比如封装)就不再展开写了。

class Rectangle{                                  //定义矩形类
    public double height; 
    public double width;
    
    public Rectangle(double height,double width) {
        this.height=height;
        this.width=width;
    }
    
    public void calcuArea() {
        System.out.println("矩形的面积为:"+height*width);
    }
}

class Parallelogram{                               //定义平行四边形类
    public double height; 
    public double width;
    
    public Parallelogram(double height,double width) {
        this.height=height;
        this.width=width;
    }
    
    public void calcuArea() {
        System.out.println("平行四边形的面积为:"+height*width);
    }
}

public class Test {
    public static void main(String[] args) {
        Rectangle rec=new Rectangle(1, 2);
        rec.calcuArea();
        Parallelogram par=new Parallelogram(1, 3);
        par.calcuArea();
    }
}

1.3 代码分析

思考:以上代码写起来挺简单,但是有个问题,就是代码重复率太高!这才两个类,要是有成百上千个这种类,不仅写得累,后期维护也累。

有没有什么偷懒的办法呢?当然有啦,就是后面要介绍的继承。

1.4 改进版代码

class Quadrangle{                               //定义父类--四边形类
    public double height; 
    public double width;
    public String name;
    
    public Quadrangle(double height,double width,String name) {
        this.height=height;
        this.width=width;
        this.name=name;
    }
    
    public void calcuArea() {
        System.out.println(name+"面积为:"+height*width);
    }
}

class Rectangle extends Quadrangle{             //定义子类--矩形类
    public Rectangle(double height,double width,String name) {
        super(height,width,name);               //super指调用父类的构造方法
    }
}

class Parallelogram extends Quadrangle{         //定义子类--平行四边形类
    public Parallelogram(double height,double width,String name) {
        super(height,width,name);
    }
}

public class Test {
    public static void main(String[] args) {
        Rectangle rec=new Rectangle(1, 2,"矩形");
        rec.calcuArea();
        Parallelogram par=new Parallelogram(1, 3,"平行四边形");
        par.calcuArea();
    }
}

1.5 改进版代码分析

诶?咋一看,好像并没有省事哦?
那是因为我们这里只有两个子类,如果换成实现成百上千个这种子类,差距就会拉开了。咱们来分析一下这是如何实现的。

以上代码包含以下概念:
继承(Inheritance):即基于已有的类(称之为父类或超类)来创建新的类(称之为子类),顾名思义,子类将继承父类所有的成员变量及方法。继承一般应用于类与类较相似的情况下,比如本案例中,矩形类与平行四边形类的成员变量和方法高度相似,可以提取两者的共同代码,构造一个四边形类作为父类,从而避免了重复代码,也方便了后期功能的扩展及维护。

注意:

  • JAVA中类只能单继承(即只能继承一个父类),但不仅限于一个层次,Object是所有类的终极父类,是万物之源。
  • 子类继承父类时需要用到关键字“extends”。
  • 子类能继承父类所有的成员变量和方法(除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
  • 若父类定义了构造函数,子类需要用super调用父类的构造方法,且必须位于子类构造方法中的第一行。
  • 子类继承了父类,不代表只能用父类的东西,还可以定义自己的成员变量及方法,甚至于可以改写父类的方法(即后文讲到的覆盖)。

2 覆盖(Override)和多态(Polymorphism)

2.1 案例

咱们稍微再加一点难度。
现在要求设计一个面积计算器,计算的对象不仅仅是矩形和平行四边形,还包括梯形。矩形和平行四边形的输入为高和宽,梯形的输入为高、上底长和下底长(假定都是规范的数值输入),输出都为面积。
规定:矩形和平行四边形的面积计算公式为宽x高;梯形的面积计算公式为(上底+下底)x 高/2

2.2 代码

思考:我们仍然可以采用继承来实现,但是梯形的面积计算方法与矩形和平行四边形不同,如何以最简洁的方法实现代码?

具体代码如下:

class Quadrangle{                               //定义父类--四边形类
    public double height; 
    public double width;
    public String name;
    
    public Quadrangle(double height,double width,String name) {
        this.height=height;
        this.width=width;
        this.name=name;
    }
    
    public void calcuArea() {
        System.out.println(name+"面积为:"+height*width);
    }
}

class Rectangle extends Quadrangle{             //定义子类--矩形类
    public Rectangle(double height,double width,String name) {
        super(height,width,name);               
    }
}

class Parallelogram extends Quadrangle{         //定义子类--平行四边形类
    public Parallelogram(double height,double width,String name) {
        super(height,width,name);
    }
}

class Trapezoid extends Quadrangle{             //定义子类--梯形类
    public double width_up;                     //自定义成员变量--上底宽
    public Trapezoid(double height,double width_up,double width_down,String name) {
        super(height,width_down, name);
        this.width_up=width_up;
    }
    @Override
    public void calcuArea() {                   //覆盖父类的面积计算方法
        System.out.println(name+"面积为:"+height*(width_up+width)/2);
    }
}
public class Test {
    public static void main(String[] args) {
        Rectangle rec=new Rectangle(1, 2,"矩形");
        rec.calcuArea();
        Parallelogram par=new Parallelogram(1, 3,"平行四边形");
        par.calcuArea();
        Trapezoid tra=new Trapezoid(1, 1, 2,"梯形");
        tra.calcuArea();
    }
}

2.3 代码分析

以上代码用到了以下概念:
覆盖(Override):指在继承中,父类的有些方法在子类中不适用,子类重新定义的手段。在本案例中,梯形类对calcuArea方法实现了覆盖。

注意:

  • 若子类中被“覆盖”方法的参数类型不同,返回类型不一致,这不是覆盖,而是重载。覆盖要求参数类型必须一样,且返回类型必须兼容。总之,子类对象得保证能够执行父类的一切。
  • 不能降低覆盖方法的存取权限,如public变成private。
  • 若不希望父类的某个方法被子类覆盖,可以用final修饰该方法。甚至可以扩展到将类用final修饰,则其中所有的方法均不可覆盖,但不影响成员变量的赋值。

思考:Test类中的语句块有点啰嗦,同样是初始化加调用面积计算方法,三个对象实现了三次,那如果有成百上千个类岂不是要累死,这能否优化呢?

2.4 改进版代码

对Test类进行优化可以得到如下代码:

public class Test {
    public static void main(String[] args) {
        //创建对象
        ArrayList<Quadrangle> quadrangles = new ArrayList<Quadrangle>();
        quadrangles.add(new Rectangle(1, 2,"矩形"));
        quadrangles.add(new Parallelogram(1, 3,"平行四边形"));
        quadrangles.add(new Trapezoid(1, 1, 2,"梯形"));
        //循环执行各个对象的面积计算方法
        for (Quadrangle qua : quadrangles) {
            qua.calcuArea();
        }
    }
}

2.5 改进版代码分析

以上代码用到了以下概念:
多态(Polymorphism):指一个对象变量(如代码中的qua)可以指示多种实际类型的现象。由于矩形类、平行四边形类和梯形类都是继承于四边形父类,所以其方法名一致,可以通过一个父类的对象变量来实现子类的自动匹配,从而简化了代码。

多态的优缺点

  • 优点:可以提高可维护性(多态前提所保证的),提高代码的可扩展性
  • 缺点:无法直接访问子类特有的成员。

思考:这个缺点怎么解决呢?比如在上述代码中,无法通过qua.width_up获取只有梯形的才有的成员变量。
解决方法:可以通过instanceof判断对象变量的实际类型以及对象类型转换实现相应的操作,代码如下:

public class Test {
    public static void main(String[] args) {
        //创建对象
        ArrayList<Quadrangle> quadrangles = new ArrayList<Quadrangle>();
        quadrangles.add(new Rectangle(1, 2,"矩形"));
        quadrangles.add(new Parallelogram(1, 3,"平行四边形"));
        quadrangles.add(new Trapezoid(1, 1, 2,"梯形"));
        //循环执行各个对象的面积计算方法
        for (Quadrangle qua : quadrangles) {    
            if (qua instanceof Trapezoid) {         //判断对象类型
                Trapezoid tra=(Trapezoid)qua;       //对象类型转换
                //输出梯形类的上底宽
                System.out.println("梯形的上底宽为:"+tra.width_up);
            }
            qua.calcuArea();
        }
    }
}

3 参考文献

[1]《Head First Java(第二版·中文版)》

[2]《Java核心技术·卷 I(原书第11版)》

[3] 菜鸟教程:https://www.runoob.com/java/j...

[4] 速学堂:https://www.sxt.cn/Java_jQuer...

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值