JavaSE--基础语法--继承和多态(第三期)

news2024/9/20 18:35:57

一.继承

1.1我们为什么需要继承?

首先,Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是 现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。

例如猫和狗:

public class Dog {
    String name;
    int age;
    float weight;

    public void eat(){
        System.out.println(name+"正在吃饭!");
    }
    public void sleep(){
        System.out.println(name+"正在睡觉!");
    }
    public void bark(){
        System.out.println(name+"正在汪汪汪!");
    }
}


public class Cat {
    String name;
    int age;
    float weight;

    public void eat(){
        System.out.println(name+"正在吃饭!");
    }
    public void sleep(){
        System.out.println(name+"正在睡觉!");
    }
    public void bark(){
        System.out.println(name+"正在喵喵喵!");
    }

}

我们可以看到上面的代码我们可以知道,猫和狗他们有一些共性代码例如eat和sleep,这时我们就可以想到它们都是动物我们是否有什么方法可以将使用一个类方法它们放在一起。确实我们这就就可以用到我们的继承方法,因为它们都是动物这时我们就可以定义一个Animal作为父类用于存放相同共性的代码,之后我们Dog和Cat就只需要继承Animal这时就达到了简便,从而实现代码的复用。

1.2继承的概念

继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。

继承关系如图所示:

 这里我们可以看出继承其实就是父类和子类的关系,当我们父类中有的代码时子类可以继承,子类只需要关心自已特有的特性。

继承的意义:实现代码的复用。

 1.3继承的语法

在Java中类之间的继承需要我们用到extends关键字

例如:上面的猫和狗我们就可以用继承关系进行eat和sleep方法的复用。

public class Animal {
    String name;
    int age;

    public void eat(){
        System.out.println(name+"正在吃饭!");
    }
    public void sleep() {
        System.out.println(name + "正在睡觉!");
    }
}

public class Cat extends Animal{
    public void bark(){
        System.out.println(name+"正在汪汪汪!");
    }

}
public class Dog extends Animal {
    public void bark(){
        System.out.println(name+"正在喵喵喵!!");
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.name="旺财";
        dog.age=10;

        dog.sleep();
        dog.eat();
        dog.bark();
    }
}

例如上面这一段代码,我们可以看到在测试方法里面我们可以直接通过dog.的形式来调用name,age,sleep,eat方法,但是我们的dog类方法中并没有这这些方法,这是它就是继承了父类Animal中的方法。

注意事项: 

1. 子类会将父类中的成员变量或者成员方法继承到子类中了

2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

1.4父类成员的访问

1.4.1子类访问父类的成员变量

1.当子类和父类不存在同名成员变量时

public class Base {
    int a;
    int b;

}
public class Derived extends Base{
    int c;
    public void method(){
        a=200;
        b=59;
        c=50;
    }

}

 其中a,b是从父类继承过来的,子类自己的。

2.当父类和子类存在同名变量时

public class Base {
    int a;
    int b;
    int c;

}
public class Derived extends Base {
    int a;
    char b;

    public void method(){
        int a=100;//此处的a访问子类的a还是继承父类的a?
        int b=200;//此处的b访问子类的b还是继承父类的b?
        int c=300;//此处的c子类中没有所以肯定是继承父类的c
    }
}

通过以上两个我们可以总结出:

在子类方法中或者通过子类对象访问成员时:

1.如果子类和父类同时拥有相同的成员变量时优先访问子类自己的。

2.如果访问成员变量子类没有,则继承父类的成员变量,如果父类也没有,则编译报错。

总结规律:采用就近原则,子类自己由就访问自己的,没有才去继承父类的。

1.4.2子类中访问父类的成员方法

1.成员方法名不同

public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
// methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
}
}

总结:子类中有的成员方法就访问自己的,如果没有在去访问父类的方法名,若两者都没有则编译报错。

2.成员方法名字相同

public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
public void methodA(int a) {
System.out.println("Derived中的method(int)方法");
}
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
}
}

总结:

1.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错。

2.通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;

这时我们可能会想如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?答案很明显我们就会引用super关键字。

1.5super关键字

我们平时在设计场景的时候我们通常会遇到父类和子类的成员变量名相同,那么我们如何来访问父类的相同变量名呢?这时我们就会用到我们的super关键字,super的作用:在子类方法中访问父类成员。

例如:

public class Base {
    int a;
    int b;
    public void methodA(){
        System.out.println("Base中的methodA().......");
    }
    public void methodB(){
        System.out.println("Base中的methodB().......");
    }
}
public class Derived extends Base{
    int a;//与父类的成员变量名相同且类型相同
    char b;//与父类的成员变量名相同但类型不同

    //与父类中的methodA构成了重载
    public void methodA(int a){
        System.out.println("Derived中的methodA().....");
    }

    //与父类中的methodB构成了重写
    public void methodB(){
        System.out.println("Derived中的methodB.......");
    }

    public void methodC(){
        a=100;//等价于 this.a=a;
        b=200;//等价于 this.b=b;
        //之前我们讲过this是对当前类中成员变量的直接引用

        //这里如果我们要访问父类的a和b,需要借助super关键字
        //super是指子类从父类继承下来的部分
        super.a=300;
        super.b=400;

        //父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
        methodA();
        methodA(10);

        // 如果在子类中要访问重写的基类方法,则需要借助super关键字
        methodB();
        super.methodB();//调用父类的methodB
    }
}

总结:在子类中调用父类的成员变量和方法名只需要用上super变量就可以了。

上面我们提到了this和super关键字我们来区分以下它们。具体如下:

 1.6初始化

说起子类构造方法我们其实可以在这个板块里面把父类和子类的静态,实例,构造这三个的执行顺序全部总结出来。我们来看以下一段代码:

public class Animal {
    static{
        System.out.println("static::Animal().......");
    }

    private final String name;
    private final int age;


    {
        System.out.println("实例代码块Animal().....");
    }
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("Animal().......");
    }

}
public class Dog extends Animal{
    static{
        System.out.println("static::Dog()....");
    }
    {
        System.out.println("实例代码块Dog().......");
    }

    public Dog(String name, int age) {
        super(name, age);
        System.out.println("Dog().......");
    }
}

以上是父类和子类的静态,实例,构造代码,那么我们接下来就可以通过运行结果来获得它们的执行顺序是怎样的?

通过上图我们可以看出代码执行顺序:

 从而我们可以得到以下结论:

注意:第二次实例化子类对象时子类和父类的静态方法将不再执行

 1.7protect关键字

我们在前面学习了类和对象中我们可以知道在实现封装的时候,Java中引入了限定修饰符,主要限定:类或者类中成员能否在类外或者其他包中被访问。

protect的定义如下:

那么在不同包中的子类使用具体是如何的呢?我们用下面的具体实例来说明

 

上面这一段代码就很好的说明了protect在不同包中子类的使用。 

1.8继承方式 

Java中继承的方式多种多样下面我们来举出几个具体的例子:

在Java中我们一般采用前三种继承方式,多继承一般不会被使用。我们又是想要限制继承,这时我们就要用到关键字final。

1.9final关键字

1.修饰变量

被final修饰的变量不能被修改。

2.修饰类

此类将无法被继承。 

3.修饰方法

此方法不能被重写。

1.10继承和组合

和继承相似组合也是一种表达类之间的关系,也可以起到代码复用的效果,给我们带来简便,但在组合中并没用想继承中的extend之类的关键词,仅仅时将一个类的实例作为另一个类的字段。

继承表示对象是is-a的关系,例如: 狗是动物,猫是动物

组合表示对象时has-a的关系,例如:汽车

// 轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
// 奔驰是汽车
class Benz extend Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}

组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用 组合。

二.多态

2.1多态的概念

多态的概念简单的来说就是当不同的对象去完成相同的事的时候会产生不同的状态。

例如:

从上面两个例子我们可以看出:同一件事情发生在不同的对象身上就会产生不同的结果。 

2.2多态的实现条件

首先在Java中多态实现的必要条件:

1. 必须在继承体系下

2. 子类必须要对父类中方法进行重写

3. 通过父类的引用调用重写的方法

public class Animal {
    String name;
    int age;

    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println(name+"正在吃饭");
    }

}
public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        super.eat();
        System.out.println(name+"吃骨头");
    }
}
public class Cat extends Animal{
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        super.eat();
        System.out.println(name+"正在吃鱼");
    }
}
public class TestAnimal {
    public void eat(Animal a){
        a.eat();
    }

    public static void main(String[] args) {
        Dog dog=new Dog("旺财",1);
        Cat cat=new Cat("元宝",2);
        dog.eat();
        cat.eat();
    }
}

当不同的对象进行相同的行为会产生 不同的结果这就是多态。

2.3重写

重写:就是覆盖,重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程 进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写。

注意: 1.被重写的方法返回值类型可以不同,但是必须是具有父子关系的。

            2.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected

            3.父类被static、private修饰的方法、构造方法都不能被重写。

            4.重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.

重写和重载的区别:

 2.4静态绑定和动态绑定

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

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。

动态绑定具体如下:

2.5向上转型和向下转型

2.5.1向上转型:

向上转型顾名思义在我们的继承中就是由子类向父类进行向上转型。也就是创造一个子类对象把它当作父类对象来使用。

语法格式:

使用场景具体的有三种:

1.直接赋值

2.方法传参

3.方法返回

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

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

 2.5.2向下转型

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

但我们需要注意的是向下转型存在安全隐患

public static void main(String[] args) {
        Cat cat=new Cat("元宝",10);
        Dog dog=new Dog("旺财",20);

        //向上转型
        Animal animal=cat;
        animal.eat();
        animal=dog;
        animal.eat();

        //向下转型
        cat=(Cat)animal;//此时animal指向的是dog但这里向下转型为cat运行时会抛出异常
        cat.mew();

        dog=(Dog)animal;
        dog.bark();//这里animal指向的就是dog故这里不会报错
    }

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了 instanceof ,如果该表达式为true,则可以安全转换。

public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("元宝",2);
Dog dog = new Dog("小七", 1);
// 向上转型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal;
dog.bark();
}
}
}

2.6多态的优缺点

使用多态能够大大降低代码的“圈复杂度”,避免大量使用if-else.

圈复杂度:圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如 果有很多的条件分支或者循环语句, 就认为理解起来更复杂.

例如:

public class Shape {
    public void draw(){
        System.out.println("画图形!");
    }
}
public class Cycle extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("●");
    }
}
public class Rect extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("♦");
    }
}
public class Flower extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("❀");
    }
}
public class TestShape {
    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循环语句这时代码就比较繁琐,那当我们使用多态会是什么效果呢?

public class Shape {
    public void draw(){
        System.out.println("画图形!");
    }
}
public class Cycle extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("●");
    }
}
public class Rect extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("♦");
    }
}
public class Flower extends Shape{
    @Override
    public void draw() {
        super.draw();
        System.out.println("❀");
    }
}
public class TestShape {
    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.使用多态可扩展性强

简而言之就是当我们要增加一种新的形状的时候,改动代码的成本比较低,例如:

class Triangle extends Shape {
@Override
public void draw() {
System.out.println("△");

我们只需要在上面代码的基础上新增加一个类就可以了,不需要去改动其他地方。

但是多态除了它的优点也有缺点

1. 属性没有多态性 当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

2. 构造方法没有多态性

好了以上就是关于继承和多态的全部内容,我们下期见! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1948490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开发AI自动直播工具需要了解的源代码!

随着人工智能技术的快速发展,AI自动直播工具成为了现代直播领域的一大创新,这些工具利用先进的算法和机器学习模型,能够自动化地生成、编辑和播出直播内容,极大地提高了直播的效率和质量。 然而,要开发一款功能强大的…

10 个顶级的PPT生成AI工具盘点,一文把所有好用软件尽收囊中!

你是否希望在工作中制作 PPT 演示文稿,与他人分享你的洞见,或是发表演讲?然而,使用传统的 PPT 制作方式既耗时又费力,步入 AI 时代后,人们寻求更智能、更简便的 PPT 演示文稿制作方法。 目前市场上出现了一…

谷粒商城实战笔记-65-商品服务-API-品牌管理-表单校验自定义校验器

文章目录 1,el-form品牌logo图片自定义显示2,重新导入和注册element-ui组件3,修改brand-add-or-update.vue控件的表单校验规则firstLetter 校验规则sort 校验规则 1,el-form品牌logo图片自定义显示 为了在品牌列表中自定义显示品…

本地部署VMware ESXi服务实现无公网IP远程访问管理服务器

文章目录 前言1. 下载安装ESXi2. 安装Cpolar工具3. 配置ESXi公网地址4. 远程访问ESXi5. 固定ESXi公网地址 前言 在虚拟化技术日益成熟的今天,VMware ESXi以其卓越的性能和稳定性,成为了众多企业构建虚拟化环境的首选。然而,随着远程办公和跨…

Codeforces Round 955 (Div. 2, with prizes from NEAR!) B. Collatz Conjecture(数学)

这道题考察的主要是通过数学对过程进行优化,而不是通过数学而得到结论(让人摸不着头脑)。 我们不需要把k次直接一次次的加,这样时间复杂度太大,那么我们现在探讨一次要加多少。 我们想要实现加一个数n,满足…

事务、函数和索引

什么是事务? 事务(Transaction),就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内 的所有SQL都将被取消执行。 特点 一个事务中如果有一个数据库操作失败,那么整个事务…

Linux网络-pingtelnet

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注我,我尽量把自己会的都分享给大家,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux服务器作为一个常用的网络服务器,主要的作用就是向客户端提供网络…

vue3前端开发-小兔鲜项目-关于详情页图片渲染的一些技术

vue3前端开发-小兔鲜项目-关于详情页图片渲染的一些技术!经过前面几天的努力,我们现在已经可以正常渲染产品详情了。是时候汇总一下,基础的技术知识点了。 1:单页面组件内的抽离,是一种很重要的思想。当我们遇到了&…

leetcode日记(49)旋转链表

其实不难,就是根据kk%len判断需要旋转的位置,再将后半段接在前半段前面就行。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : …

World of Warcraft [CLASSIC] Timebadge

游戏币【每个服务器实时金价不一样,本例子是5000-6000金】 1枚【魔兽世界时光徽章】 30天游戏时间。 5760金币游戏币,策划如何消耗游戏里面的金币总量,以及如何留住那些非人民币玩家呢 30天加上去了 World of Warcraft [CLASSIC] [魔兽世界…

VitePress Index.md 的设置:开发者指南

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

使用vcpkg

概述 vcpkg 是 Microsoft 和 C 社区维护的免费开放源代码 C/C 包管理器。 它于 2016 年推出,可帮助开发人员将项目迁移到较新版本的 Visual Studio。 vcpkg 已演变成 Windows、macOS 和 Linux 上开发人员使用的跨平台工具。 vcpkg 拥有大量开放源代码库注册表和企业…

Linux-安装VMware-01

一、认识linux Linux 是一个开源的类 Unix 操作系统,由林纳斯托瓦兹(Linus Torvalds)于1991年首次发布。Linux 是许多计算机硬件的底层操作系统,特别是服务器、嵌入式系统和个人电脑。它支持多种架构,包括 x86、x64、A…

linux编译gcc源码详解

linux编译gcc源码详解 一、下载依赖包二、安装依赖2.1 安装m42.2 编译GMP2.3 编译MPFR2.4. 编译MPC2.5 设置环境变量三、gcc编译3.1 下载gcc3.2 编译gcc源码3.3 环境变量的配置一、下载依赖包 https://gcc.gnu.org/pub/gcc/infrastructure 命令下载依赖库,注意gcc编译时的依…

stm32cubeIDE与stm32cubeMX库文件安装路径自定义设置

1、 stm32cubeMX库文件安装路径自定义设置 打开stm32cubeMX软件,依次点击标题栏【Help】→【Updater Settings】 点击图中的【Browser】,选择自己想要存放库文件的文件夹,选择完成后点击【OK】即可设置成功。 PS:这里的路径建议…

python实现接缝雕刻算法

python实现接缝雕刻算法 接缝雕刻算法步骤详解Python实现详细解释优缺点应用领域接缝雕刻算法(Seam Carving Algorithm)是一种内容感知的图像缩放技术,可以智能地改变图像的尺寸,而不会明显影响其重要内容。它通过动态规划的方式寻找图像中的“接缝”,即在图像中从上到下或…

hadoop完全分布模式搭建

本次搭建是基于伪分布式进行的,所以配置之前需要搭建好伪分布式 我使用的ubuntu版本见下 虚拟机之前安装过在此不在记录 伪分布式的搭建过程在之前的第一次实验报告上有详细的记录 修改主机名

《Java初阶数据结构》----7.<优先级队列PriorityQueue>

前言 大家好,我目前在学习java。之前也学了一段时间,但是没有发布博客。时间过的真的很快。我会利用好这个暑假,来复习之前学过的内容,并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区…

【算法】插入排序 与 希尔排序 概念+图解+代码【Python C C++】

1.插入排序 1.1概念 插入排序(InsertionSort),一般也被称为直接插入排序。 对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个元素插入到已经排好序的有序表中,从而构造出一个新的…

EXCEL自动公式计算始终为0

如果你的数据单元格的左上角存在绿色的三角小箭头,那么就会造成这种问题: 你的数字是以文本形式存入的单元格 解决办法: 选中数据列,数据->分列 直接选择完成 此时就可以进行公式计算了