首页 > 编程语言 >Java:继承和多态

Java:继承和多态

时间:2024-10-22 23:19:49浏览次数:3  
标签:Java 构造方法 继承 子类 多态 访问 父类 方法 我们

面向对象程序三大特性:封装、继承、多态。在前的一章我们已经了解了什么是封装,而今天我们就要开始了解后面的两个————继承和多态。

 一.继承

1.继承概念

继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。

用官方的话来讲就是:共性的抽取和代码的复用。

2.继承的语法

在Java中如果要表示类之间的继承关系,需要借助extends关键字

继承的格式:

  • 格式: public class 子类名 extends 父类名{}
  • 例如: public class Zi extends Fu {}
  • Fu:是父类,也被称为基类、超类
  • Zi: 是子类,也被称为派生类  

继承中子类的特点:

  • 子类可以有父类的内容,子类还可以有自己特有的内容。

那么让我们来举个例子,我们知道猫和狗都可以拥有名字和年龄,和吃饭的行为,对于这些共性的行为和属性,我们可以将它放入一个类当中,让这个类作为父类,而我们的猫类和狗类作为子类,同时着不相同的行为和属性,而这些我们会将他们放入自己的类当中,而共同拥有的这些行为和属性我们可以通过继承来获得他们 。

 3.父类成员访问

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

⨀子类和父类不存在同名成员变量

⨀子类和父类成员变量同名 

但是我们不能使用父类和子类中都没有的变量 

通过这三张图中的代码,我们可以发现在子类方法中或者通过子类对象访问成员时: 

  • 如果访问的成员变量子类中有,优先访问自己的成员变量
  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

总结下来就是:成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。 

(2)子类中访问父类的成员方法 

⨀成员方法名字不同

⨀成员方法名字相同 

 

通过上面的这两张图,我们可以总结出子类中访问父类的成员方法的方式:

成员方法没有同名时:

  • 在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时 再到父类中找,如果父类中也没有则报错

.成员方法有同名时:

  • 通过子类对象访问父类与子类中同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错
  • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错

 但是这时候我们就突然引出了一个问题:那就是在子类和父类方法有同名的时候,我们就想访问父类的方法我们该怎么办呢?这就要引出我们新的关键字super了。

4.super关键字

在Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。

注意:

只能在非静态方法中使用

在子类方法中,访问父类的成员变量和方法。 

5.子类构造方法

子类中所有的构造方法默认都会访问父类中无参的构造方法。

  1. 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
  2. 每一个子类构造方法的第一条语句默认都是: super()

注意:若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法

注意:super()必须放在子类构造方法的第一条语句

注意:如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的 父类构造方法调用,否则编译失败

注意:super(...)只能在子类构造方法中出现一次,并且不能和this()  同时出现 

在前面我们学习了实例代码块和静态代码块,在没有继承关系时的执行顺序。而现在我们了解了构造方法后,我们现在想知道到他们三个的执行顺序,那么我们就来看下面者段代码。

运行完之后我们发现他是按照红括号里的顺序执行的 

6.super和this的区别

相同点

  • 都是Java中的关键字
  • 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
  • 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

不同点

  • this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成 员的引用
  • 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  • 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造 方法中出现
  • 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有 

7.protected 关键字

在这里我们就又要了解一下访问限定符的范围了

我们发现只有protected和public是支持在不同包下进行访问的,但是protected必须是在子类当中,而public没有任何限制 

但是我们希望类要尽量做到 "封装", 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者. 因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用public.

8.继承方式

⨀Java支持单继承

⨀Java支持多层继承

 ⨀Java支持不同的类继承同一个类

⨀注意:Java不支持多继承

虽然上面的继承是可以的,但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系,当然如果我们之后想被继承了,我们就可以使用我们下面要讲的final关键字进行语法上的限制。

9.final 关键字

final关键可以用来修饰变量、成员方法以及类。

⨀修饰变量时:

经过final修饰的变量,会变为常量,所以我们不能修改他的值

 ⨀修饰类时:

经过final修饰的类是不能被继承的

  ⨀修饰方法时:

关于重写我们会在下文进行讲解

这是一个正常的重写:

能够正常的执行小明正在跑步,但是如果我们这时给父类中的方法加上final关键字,我们就无法对父类中的run()方法进行重写了

10.继承与组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码复用的效果。

继承表示对象之间是is-a的关系 例如:洗衣机,空调,冰箱是家电

组合表示对象之间是has-a的关系 例如:手机有cpu,芯片,电池

那么接下来我们就以手机为例写一个组合的例子:

二.多态

1.多态的概念和实现

 ⨀多态的概念:字面意义上来将就是一种事物多种形态

具体点去说就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

 ⨀多态的实现:

首先我们先来了解一下实现多态的一些条件:

  • 必须在继承体系下
  • 子类必须要对父类中方法进行重写
  • 通过父类的引用调用重写的方法

接下来我们来看一个多态的实例:

现在你们一定很好奇这些由蓝色和黄色框起来的内容到底是什么,其实黄色框起来的就是我们上文所说的重写,而蓝色的是我们在多态中要使用的向上转型。那么下面就让我们来了解一下他们吧。

2.重写

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写

重写的条件:

  • 方法名相同
  • 参数列表相同
  • 返回值相同
  • 但是对于子类重写父类的方法时被重写的方法返回值类型可以不同
  • 访问权限不能比父类中被重写的方法的访问权限更低
  • 父类被static、private修饰的方法、构造方法都不能被重写
  • 重写的方法, 可以使用 @Override这是用来建议是否合法的

对于重写介绍完之后我们还要了解一个东西静态绑定和动态绑定 

⨀静态绑定

也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表就是重载。

⨀动态绑定

也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法。典型代表就是重写。

接下就用我们上面的多态例子来简单介绍一下动态绑定

3.向上转型和向下转型

⨀向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。

我们可以简单理解为子变父。

向上转型定义有三种方式

第一种:直接赋值

语法格式:父类类型 对象名 = new 子类类型()

 Animal animal = new Cat("咪咪","黄色");

第二种 :方法传参

这就是我们上面, 所用的方式

第三种:方法返回 

向上转型的优点:让代码实现更简单灵活。

向上转型的缺陷:不能调用到子类特有的方法。 

 ⨀向下转型

当我们将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

我们可以简单理解为父变子

我们看上面这张图使用向下转型后发生了错误,并且编译器告诉我们不安全,这其实是因为我们虽然进行了两次向上转型,但是我们是一个同名的变量进行的转型后赋值,所以我们最后的到的Dog类,而下面的代码是想要进行向下转型,但是现在我们进行转型是两个子类之间进行转换想要把狗变成猫这显然是不可以的,但如果我们是Animal的dog类还原成子类的Dog这肯定是可以的

虽然我们你正确输出dog类的结果但是我们依然存在cat类,这时如果我们不想掉,我们该怎么解决呢?这时我们又要引入一个关键字instanceof了

我们发现利用这个关键字与Dog和Cat两类型进行比较,他所返回的是boolean类型,如果为真就会执行下面的语句,这些就能成功解决这个问题了,所以进行向下转型后我们最好要用instanceof关键字进行比较之后再进行使用 

4.多态的优缺点

1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else

我们先来看一段代码

class Shape {
    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("●");
    }
}

class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

public class Test1 {
    public static void main(String[] args) {
            Rect rect = new Rect();
            Cycle cycle = new Cycle();
            Flower flower = new Flower();
            String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
            for (String shape : shapes) {
                if (shape.equals("cycle")) {
                    cycle.draw();
                } else if (shape.equals("rect")) {
                    rect.draw();
                } else if (shape.equals("flower")) {
                    flower.draw();
                }
            }
    }
}

根据这段代码虽然我们成功的打印的图形,但是我们发现我们是用了很多的if..else if语句这就增加了我们的圈复杂度,这时我们就可以使用多态来解决这个问题

class Shape {
    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("●");
    }
}

class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
                new Rect(), new Flower()};
        for (Shape shape : shapes) {
            shape.draw();
        }

    }
}

这样我们发现我们成功的解决了圈复杂度的问题,并且也能成功打印 

2. 可扩展能力更强 

我们发现如果我们想要增加一个图像或者减少一个图形只需要我们去再数组里加上或减去即可,如果我们使用第一种方法我们不仅要在数组里加还要再写一个else if语句。

3.多态缺陷

当然多太肯定不是完美的他肯定还是有所缺陷的 :他会导致代码的运行效率降低。


好了今天的分享就到这里了我们下一篇见!

标签:Java,构造方法,继承,子类,多态,访问,父类,方法,我们
From: https://blog.csdn.net/zm3rttqs9f/article/details/143080981

相关文章

  • Java毕设项目案例实战II 基于移动平台的远程在线诊疗系统(开发文档+数据库+源码)
    目录一、前言二、技术介绍三、系统实现四、论文参考五、核心代码六、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言在当今数字化时代,医疗行业正经历着前所未......
  • java基于springboot的点餐系统(源码+vue+部署文档+前后端分离等)
    收藏关注不迷路!!......
  • java基于springboot的个性化课程推荐系统(源码+vue+部署文档+前后端分离等)
    收藏关注不迷路!!......
  • Java 的奇妙探险:揭秘 @Retention 和 @Target 的魔法奥秘
    前言在Java的魔法世界里,隐藏着两个神秘的符号,它们的名字是@Retention和@Target。这对“魔法师”的法杖不仅能让你施展代码的魅力,还能决定注解的生命旅程和使用场所。想象一下,如何用这些注解为你的代码注入灵魂,创造出更为高效和优雅的程序。今天,就让我们一同踏上这场充满......
  • Java安全-反射
    反射​反射是一种强大的机制,它允许程序在运行时访问、检查和修改它自己的结构,比如类、接口、字段(属性)和方法。反射提供了一种动态性,使得Java程序可以在运行时处理对象和类。所以说通过反射,我们可以使java这类静态语言附上动态的特征。几个反射中重要的方法:获取类的⽅法:......
  • Java调用第三方接口、http请求详解,一文学会
    系列文章目录提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章Python机器学习入门之pandas的使用提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录系列文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据......
  • 精通Java并发锁机制:24种锁技巧+业务锁匹配方案
    在Java并发编程中,锁是确保线程安全、协调多线程访问共享资源的关键机制。从基本的synchronized同步关键字到高级的ReentrantLock、读写锁ReadWriteLock、无锁设计如AtomicInteger,再到复杂的同步辅助工具如CountDownLatch、CyclicBarrier和Semaphore,每种锁都针对......
  • 黑马程序员Java进阶学习(三)
    异常Java的异常体系异常的基本处理异常的作用异常是什么?异常是代码在编译或者执行的过程中可能出现的错误。异常的代表是谁?分为几类?Exception,分为两类:编译时异常、运行时异常。编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。运行时异常:继承自Runtim......
  • 【java】抽象类和接口(了解,进阶,到全部掌握)
    各位看官早安午安晚安呀如果您觉得这篇文章对您有帮助的话欢迎您一键三连,小编尽全力做到更好欢迎您分享给更多人哦大家好我们今天来学习Java面向对象的的抽象类和接口,我们大家庭已经来啦~一:抽象类1.1:抽象类概念在面向对象的概念中,所有的对象都是通过类来描绘的,但是......
  • java的三大程序结构
    JAVA的三大程序结构一:顺序结构程序走上执行到下。二:选择结构if单选择结构if(布尔表达式){//如果布尔表达式的值为ture则执行{}里的语句块}publicclassIfDemo01{publicstaticvoidmain(String[]args){//接收键盘输入Scannerscanner=newSca......