IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java 类和对象——继承、多态 -> 正文阅读

[Java知识库]Java 类和对象——继承、多态

一、继承

有些类在设计上是有关联的,比如我们定义了一个动物类:

class Animal {
    public String name;
    public int age;
    public String sex;
    
    public void eat() {
        System.out.println("动物在eat");
    }
}

在定义一个猫类:

class Cat {
    public String name;
    public int age;
    public String sex;

    public void eat() {
        System.out.println("猫在eat");
    }
    
    public void catSound() {
        System.out.println("猫发出的声音是喵喵");
    }
}

? ? 我们发现:这两个类有很多的部分是重叠的。那么,能不能降低重叠的部分,把Animal类中的直接拿过来在Cat类中使用呢?面向对象思想中提出了继承的概念。

1、继承的概念

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

2、定义继承

Java中要表示类的继承关系,需要extends(扩展)关键字。Cat如何继承Animal:

? ? 类只有两种访问修饰限定符:public和什么都不加的默认权限访问。一个Java文件中只能只能有一个public类。由于main方法所在的类有一个public,所以其他的类都是默认的访问修饰限定符。这里的Cat是子类(或者派生类),Animal是父类(或基类、超类)。中间的extends表示扩展、继承。

那么上面的两个类就可以写为:

class Animal {
    public String name;
    public int age;
    public String sex;

    public void eat() {
        System.out.println("动物在eat");
    }
}

class Cat extends Animal {

    //Cat类继承了Animal类的属性和方法。

    public void catSound() {
        System.out.println("猫发出的声音是喵喵");
    }
}

现在我们以Base类(基类)和Derived类(派生类)来举例。

class Base {
    public int data1 = 10;
    public int data2 = 20;

    public void baseMethod() {
        System.out.println("父类方法");
    }
}

class Derived extends Base {
    public int data3 = 30;
    public int data4 = 40;

    public void derivedMethod() {
        System.out.println("子类方法");
    }

}

访问子类和父类的属性或方法:

发现:实例化子类对象,红色的框中出现子类继承过来父类的一些属性。?

3、super关键字

? ? 现在在子类中写一个derivedBaseSameNameAttribute()方法表示出现子类和父类中变量名同名的情况,在Base类中加一个data5=51;Derived类中加一个data5=50,然后在这个方法中访问。

打印的是子类中的data5。要访问父类的data5,需要加super关键字。super表示父类对象的引用。

? ? super和this关键字的使用差不多。super和this一样有三种使用方法:super.data,表示调用父类的属性、super.func()表示调用父类的方法、super()表示调用父类中的构造方法。这里的super.data5就表示调用父类的属性。

如果是成员方法名相同:在Base类和Derived类中加一个derivedBaseSameNameMethod()方法。

? ? 打印的是子类的derivedBaseSameNameMethod()方法。在main方法中要打印父类的同名方法,加一个super是不行的(同名属性也不行)。要么在子类中写一个方法来间接调用、要么将父类的derivedBaseSameNameMethod()方法和子类的方法形成重构、要么实例化父类对象访问。

这里的参数没有意义,只是为了调用父类的构造方法。但是最好不要这样写没有意义的参数。

总结:

(1)在子类中,如果想要明确来访问父类中的成员时,需要super关键字就可以了。父类和子类中不重名的时候也可以使用this。

(2)重名的时候,编译器采用就近原则。先去子类中找,找不到就去父类(不能从父类到子类)。还找不到就报错。

(3)通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法构成重载,根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法相同(方法名、参数列表等)则只能访问到子类的,父类的会被覆盖掉(构成重写),无法通过子类对象直接访问到。

(4)super关键字只能在非静态方法中使用。在子类方法中访问父类的成员。

super和this比较,相同点:

(1)都是Java中的关键字,只能在类的非静态方法中使用。

(2)用在构造方法中,都要放在相对的第一行。也就是说它们不能同时存在。

不同点:

(1)this是当前对象的引用,super是子类对象从父类中继承下来的成员的引用。也就是说,this一般用来访问子类的方法和属性,super用来访问父类的方法和属性。

(2)this是非静态成员方法的一个隐藏参数,super不是隐藏的参数。

(3)构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有。

4、子类构造方法

? ? 在没有继承的时候,对类中的成员变量进行初始化,有就地初始化、默认初始化,代码块初始化等、最重要的是用构造方法进行初始化。那么一个类被继承后,有没有什么变化呢?

在父类写出它的带参数的构造方法。

给子类也加一个构造方法。

? ? 我们知道,子类是继承了父类的。所以子类在进行构造的时候,应该要给父类也进行构造。所以子类在构造的时候,要先帮助父类进行构造,在调用子类的构造方法。使用super()就可以了。

带参数的构造方法:

不带参数的构造方法:

?总结:

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

(2)如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。

(3)super()和this()相似,都要放在构造方法中的第一行。也就是说,super()和this()不能同时使用。

5、protected关键字

? ? 当父类中的属性被private修饰的时候,只能在父类中使用。子类虽然是继承了父类,但是访问不到这个属性。

只需要将这个private改为protected(受保护的)权限就可以了。

? ? 我们知道,Java中权限有四种:private、默认、protected和public。private表示只能在当前类中使用。默认表示只能在同一个包中使用。protected是继承权限,范围比默认的要大,可以在不同的包中访问到。public是公共权限,在哪里都能访问。

? ? 类要尽量做到封装,隐藏内部实现细节,只留下必要的信息给类的调用者。因此,要使用比较严格的访问权限。能提供private的就不要提供protected权限。

6、继承方式

Java支持多种继承方式:

? ? Java不支持一个类继承多个类的继承方式。但是可以使用接口来实现。在继承关系中,一般不要超过三层的继承关系。如果一个类不想被继承,就可以使用final关键字来修饰。final可以用来修饰变量或字段,表示常量(不能被修改);修饰类,表示这个类是一个密封类,不能被继承;修饰方法,表示这个方法不能被重写。

7、组合

? ? 继承是一种表达类之间关系的方式,组合也是一种表达类之间关系的方式。组合只是将一个类的实例作为另一个类的字段。

有三个类:学生类、老师类和学校类。要把他们组合起来,就是学校里面有学生,学校里面有老师。

这样创建之后是不能直接访问的,要对学生类和老师类进行实例化后才能访问。

? ? 其他的就和前面的语法没有什么区别。我们写课后作业的时候的测试类就是一个组合。继承是is--a的关系:猫是一个动物,而组合是has--a的关系:动物有猫这个物种。

二、多态

? ? 多态具体一点就是要去完成某个行为,不同的对象去完成,会产生不同的状态。比如考试,学霸考试得到高分,学渣考试得到混合双打,这是一种多态。

1、多态实现的条件

(1)必须要在继承的体系下。

(2)子类对父类中的方法进行重写。

(3)通过父类引用去调用子类重写的方法。

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。下面对这些条件分别做介绍。

2、向上转型和向下转型

? ? 向上转型是实现多态的一部分条件,它在继承的体系下。多态中上指的是父类,下指的是子类。向上转型就是子类转型成父类。或者:父类引用,引用子类对象。只要记住父类引用,引用子类对象。这是从小的范围像大的范围转换。

class Base {
    public int data1;
    private int data2;
    
    public void BaseMethod() {
        System.out.println("父类方法");
    }
    
}

class FirstDerived extends Base {
    public int data3;
    private int data4;

    public void derivedMethod() {
        System.out.println("子类方法");
    }

}

public class test_04_03_04 {//本人的Java文件名

    public static void main(String[] args) {
        
        Base base = new FirstDerived();
        base.BaseMethod();
        
    }
}

? ? 父类引用了子类对象,但是不能调用子类方法。这个derivedMethod()方法在父类中是没有的。向上转型有三种用法:直接赋值、作为参数传参、作为返回值。

直接赋值:

作为参数传参:在main方法中新写一个方法,用来传参数。

?作为返回值:写一个方法,返回值是Base类型。

这三种虽然用处不一样,但本质上还是父类引用,引用子类对象。

向上转型能够让代码实现更灵活,缺点是不能调用子类特有的方法。

向下转型是子类引用,引用父类方法。但是这意味着范围的缩小,所以它不安全。

图示:

3、重写方法

? ? 重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。

定义一个Animal类,Cat类和Dog类继承了Animal类。

class Animal {
    public String name;
    public int age;
    
    public void eat() {
        System.out.println(this.name + " 吃食物");
    }
}

class Cat extends Animal {
    
    public void catchingMice() {
        System.out.println(this.name + " 抓老鼠");
    }
}

class Dog extends Animal {
    
    public void housekeeping() {
        System.out.println(this.name + " 看家门");
    }
}

public class test_04_03_05 {

    public static void main(String[] args) {
        Cat cat = new Cat();
        Dog dog = new Dog();
    }
}

? ? 我们知道,猫是可以吃猫粮的,狗是可以吃狗粮的。所以,对父类中的eat()方法能不能改一下呢?可以改,但是不能直接在父类方法中改,并不是所有的动物都是吃猫粮的。为了代码的直观,删去了Cat类中的catchingMice()方法和Dog类中的housekeeping()方法。

? ? 这个时候,子类和父类的eat方法中:返回值相同、方法名相同、方法参数列表相同。这就构成了方法的重写:子类的eat()方法对父类的eat()方法进行了扩展或覆盖。我们在用向上转型去调用子类的eat()方法。?

发现这里的结果是调用了子类的eat()方法。为了让重写显着更清楚,可以加上@Override。

这样,不仅可以显示更清楚,而且如果变量名等写错,就可以提示出来。?

? ? 在编译的时候,eat()调用应该是父类的方法,结果运行的时候,调用的是子类的方法。这种情况是在运行的时候发生的,叫运行时绑定(动态绑定)。

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

重写要注意的几个点:

(1)子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致。

(2)返回值可以不同,但是要形成父子关系,叫协变类型。

(3)子类的重写方法访问权限不能比父类低。如果父类时protected,子类就不能是?默认或private;如果父类是public,子类只能是public。

(4)父类静态的,private修饰的,final修饰的方法都不能重写。

(5)和重载的区别

方法的重载是一个类的多态的体现,方法的重写是子类和父类多态的体现。

? ? 现在,在主类中写一个方法,传进去不同的对象,?调用父类的eat()方法,显示因为传进去猫这个对象,打印吃猫粮,传进去狗这个对象,打印吃狗粮。

class Animal {
    public String name;
    public int age;

    public void eat() {
        System.out.println(this.name + " 吃食物");
    }
}

class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println(this.name + " 吃猫粮");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println(this.name + " 吃狗粮");
    }
}

public class test_04_03_05 {

    public static void function(Animal animal) {
        animal.eat();
    }

    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "mimi";
        Dog dog = new Dog();
        dog.name = "大黄";
        function(cat);
        function(dog);
    }
}

4、使用多态的优缺点

优点:

(1)提高了代码的可读性,避免了使用大量的if-else。

(2)可扩展能力更强。

比如,打印不同的图形,这样,使用多态直接扩展就可以了。

class Shape{

    public void draw() {
        System.out.println("打印图形");
    }
}

class Cycle extends Shape {

    @Override
    public void draw() {
        System.out.println("打印?");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("打印?");
    }
}

class Flower extends Shape {

    @Override
    public void draw() {
        System.out.println("打印?");
    }
}

public class test_04_04_04 {

    public static void drawShapes() {
        Shape[] shapes = {new Cycle(), new Rect(), new Flower()};
        for (Shape shape : shapes) {
            shape.draw();
        }
    }

    public static void main(String[] args) {
        drawShapes();
    }
}

缺点:代码的运行效率低,而且,不能使用子类的方法。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:04:04  更:2022-04-06 16:04:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:29:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码