【JAVA |继承与多态】JAVA中的继承与多态超详细!!

✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 
🎈🎈作者主页: 🎈丠丠64-CSDN博客🎈


✨✨ 帅哥美女们,我们共同加油!一起进步!✨✨ 

目录

一、前言

二、继承

1.为什么要继承

2.继承语法

3.父类成员访问

4.super

--this与super的关系

5.子类构造方法

6.初始化

7.继承方式

8.final

9.组合

三、多态

1.重写

-静态绑定

-动态绑定

2.向上转型

​编辑

3.向下转型

4.instanceof

5.多态的优缺点

优点

缺点


一、前言

在谈 Java 面向对象的时候,不得不提到面向对象的三大特征:封装open in new window继承open in new window多态open in new window。三大特征紧密联系而又有区别,合理使用继承能大大减少重复代码,提高代码复用性


二、继承

在设计两个实体的时候,有时候会出现偶同的情况,一些属性是共有的,所以面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用


1.为什么要继承

继承使程序员在原有的类特性基础进行修改、拓展、增加新的功能,产生新的类叫做派生类/子类、而共有那部分被称为父类/基类/超类


2.继承语法

Java 中如果要表示类之间的继承关系,需要借助 extends 关键字,具体如下:
修饰符 class 子类 extends 父类 {
// ...
}

 举个例子

Animal为父类,是共有属性

class Animal{
    String name;
    int age;

    public void eat(){
        System.out.println(name + "正在吃饭");
    

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

子类为dog和cat,为特有属性,继承了特有属性

class Dog extends Animal{

    {
    this.name ="小黄";
    }

    void bark(){
        System. out. println(name +"汪汪注~~~");
    }

    class Cat extends Animal{
        this.name = "小眯";
    }

    void mew(){
    System.out.println(name + "喵喵喵");
    }
}

通过调试呢,我们发现,子类不仅仅继承了父类的成员属性,也同时继承了父类的方法,并且也同样的继承了自己本身的成员属性及其方法

public class Test {
    public static void main(String[] args){

        Dog dog = new Dog();
        dog.eat();
        dog.sleep();
        dog.bark();
      
        Cat cat = new Cat();
        cat.eat();
        cat.sleep();
        cat.mew();
    }
}

这样我们就不难发现,子类会将父类中的成员变量或者成员方法继承到子类中,并且每一个子类都有着他独特的属性又有着跟父类一样的共性


3.父类成员访问

在子类与父类中访问过程中,只遵守一个方法:优先访问子类中自己的,倘若自己没有再向父类中找,找不到则报错

为了更好地验证,我们设计以下代码,G为父类,A为子类且A继承G

class G{

    public int a= 10;
    public int b= 20;

    public void P(){
        System.out.println("我是父类");
    }
    public void Pl(int i){
        System.out.println("我是父类");
    }
}


class A extends G{

    public int a=30.
    public int c= 40

    public void P(){
        System.out.println("我是子类");
    }
    public void P1(){
        System.out.println("我是子类");
    }
}

我们来验证一下

public class Test {
    public static void main(String[] args){
        A f = new A();
        System.out.println(f.a);    //子类父类中都有a,遵循就近原则,这个a为子类的a
        System.out.println(f.b);    //子类没有b父类有b,这个b为父类的b
        System.out.println(f.c);    //父类没有c子类有c,这个c为子类的c
        f.P();    //子类父类中都有方法P,遵循就近原则,这个方法为子类的方法
        f.P1();    //父类没有无参的方法P1子类有无参的方法P1,这个方法为子类的方法
        f.P1(1);    //子类没有带一个参的方法P1子类没有带一个参的方法P1,这个方法为父类的方法

验证通过


4.super

倘若在子类与父类中,存在相同的名称的成员,想要跳过子类直接访问父类成员,有就近原则的限制,所以JAVA中提供了super关键字,在子类方法中访问父类的成员

我们再来构造一段代码,G为父类,A为子类且A继承G

class G{
    public int a= 10;

    public void P()
        System. out. println("我是父类");
    }
}


class A extends G{
    public int a=20;

    public void P(){
        super.P();        //访问父类方法P
        System. out. println("我是子类");
    }
     
    public void display(){
        System.out.println(super.a);//访间父类的a变量 10
        System.out.println(this.a);// 访间子类的a变量 20
    }
}

按理说,属性与方法子类父类相同时应该全部访问子类,当我们用super关键字修饰以后就可以直接引用到父类,我们来验证一下

public class Test {
    public static void main(String[] args){
        A f= new A();
        f.P();
        f.display();
    }
}

 验证通过


--this与super的关系

  • 都为JAVA中的关键字
  • 都只能在类的非静态方法中使用
  • 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在 

       

5.子类构造方法

子类构建方法时,应该调用父类构造方法,再执行子类构建方法

具体实现方法为:在子类构建方法的首行,加上super()语句访问父类的构建方法,不然会报错

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

class Gi
    public G(){
        System. out. println("我是父类");
    }
}

class A extends G{
    public A(){
        super();
        System. out.println("我是子类");
    }
}

public class Test{
    public static void main(String[] args){
        A f= new A();
    }
}

6.初始化

用来子类继承父类这一关系以后,在代码的运行过程中,代码的初始化的逻辑顺序应该是怎么样的呢?

我们来构造一段代码验证一下

class G{

    static{
        System.out. println("我是父类静态代码块");
    }
    
    {
        System. out.println("我是父类实例代码块");
    }

    public G(){
        System. out. println("我是父类构造方法");
    }
}

class A extends G{

    static{
        System.out.println("我是子类静态代码块");
    }
    
    {
        System.out.println("我是子类实例代码块");
    }

    public A(){
        System.out.println("我是子类构造方法");
    }
}

我们分别定义两个对象,看看实现的一个顺序是怎么样的

public class Test
    public static void main(String[] args){
        A fl = new A();
       
        A f2 = new A();
    }
}

我们不难得出结果

  1. 父类静态代码块优先于子类且最先执行(只执行一次
  2. 父类实例代码块和构造方法紧接着执行
  3. 子类实例代码块和构造方法最后执行


7.继承方式

单继承—— 一对一

class A{

}

class B extends A{

}

多层继承—— 套娃

class A{

}

class B extends A{

}

class C extends B{

}

不同类继承同一类—— 多对一

class A{

}

class B extends A{

}

class C extends A{

}

JAVA中不支持多继承—— 同一子类对应多个父类


8.final

用final修饰的字段、变量 不能修改,表示常量
final int a = 10;

用final修饰的方法 不能被重写,但可以有多个重载
final void bark(){

}

用final修饰的类不能被继承,不然会报错

final class A{

}
class B extends A{

}


9.组合

继承表示对象之间是 is-a 的关系 组合表示对象之间是 has-a 的关系
组合也是一种表达类之间关系的方式 , 也是能够达到代码重用的效果
class Tire{
    //轮胎..
}

class Engine{
    //发动机..
}

class VehicleSystem{
    //车载系统...
}

class Car{
    private Tire tire;//可以复用轮胎中的属性和方法
    private Engine engine;//可以复用发动机中的属性和方法
    private VehicleSystem vs;// 可以复用车载系统中的属性和方法
}

class Benz extends Car{
    //将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}

是将一个类的实例作为另外一个类的字段,实现了代码的复用

三、多态

多态大白话来讲就是多种形态,当不同的对象去完成某个相同的行为时产生不同的状态

传递不同类对象时,会调用对应类中的方法 

实现多态三个必备条件:在继承体系下、子类对父类方法重写、通过父类引用调用重写方法


1.重写

重写(override指的是:

  • 子类对父类(非静态、非private修饰、非final修饰、非构造方法等)的方法内容进行重新编写(返回值与形参不能改变
  • 子类的访问权限必须大于等于父类的访问权限
  • 可以使用 @Override 注解来显式指定发现问题

我们来构造一个父类Animal 和 子类 Cat、Dog继承关系,eat()方法红框代表父类,蓝框代表子类对其进行重写

class Animal
    public String name;
    public int age;
    public Animal (String name,int age){
        this.name = name;
        this.age = age;
    }

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

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

    @0verride
    public void eat(){
    System. out.println(name+"吃鱼");
    }
}

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

    @0verride
    public void eat(){
        System.out.println(name+"吃骨头");
    }
}

 

倘若重新的方法发生了非内容的修改,@Override 注解则会报错

@Override
public void eatl(){
    System. out. println(name+"吃鱼~");
}


-静态绑定

以下代码方法重载就可以算上一个静态绑定,在编译前已经确定好了具体调用哪个方法

public int Add(int x){
    return x;
}
public int Add(int x,int y){
    return x+y;
}
public int Add(int x,int y,int z){
    returnx+y+z;
}


-动态绑定

像上述的那段代码,在编译期间不能确定方法的行为,在程序运行时才能确定具体调用哪个类

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

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

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

    @0verride
    public void eat (){
        System. out. println(name+"吃骨头~");
    }
}


重写的设计原则:对于已经投入使用的类,最好重新定义一个新类,尽量不要进行修改


2.向上转型

是创建一个子类对象,将其当成父类对象来使用,从小范围向大范围的转换
父类类型 对象名 = new 子类类型 ()
//向上转型,父类的引用指向了于类
//animal的编译类型是Animal,运行类型是Dog
Animal animal = new Dog();

animal = new Cat();
//animal的运行类型变成了Cat,编译类型任然是Animal


3.向下转型

向上转型之后可以当成父类对象使用,若需要调用子类特有的方法,则需要将父类对象再还原为子类对象。这就称作向下转型。

子类转型 引用名 = (子类类型) 父类引用;
Dog d=(Dog)animal;
d.bark();//这里我们就可以调用Dog类中方法

4.instanceof

左边是对象,右边是类,作用是测试它左边的对象是否是它右边的类的实例,对象是右边类或子类所创建对象时,返回true;否则,返回false

//向上转型
Animal animal = new Dog();

//判断instanceof 是否为 true
if(animal instanceof Dog){
/向下转型
animal =(Dog)animal;
    ((Dog) animal).eat();
}else{
    System. out.println("Animal无法向下转型为Dog");
}


5.多态的优缺点

优点

降低代码的 " 圈复杂度 "
圈复杂度是一种描述一段代码复杂程度的方式,简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度",如果一个方法的圈复杂度太高, 就需要考虑重构.
有以下代码用来实现对应图像对应打印
class Shape{
//属性.
    public void draw(){
        System. out.println("画图形!");
    }
}

class Rect extends Shape{
    @0verride
    public void draw(){
        System. out.println("◆");
    }
]

class Cycle extends Shape {
    @0verride
    public void draw(){
        System. out. println("○");
    }
}

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

下述两种代码来说明
第一种非多态
Cycle cycle= new Cycle();
Rect rect = new Rect();
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();
    }
}

第二种多态

Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(),new Flower()};

for(Shape shape :shapes){
    shape.draw();
}

两种输出的结果是相同的
但是从圈复杂度及其代码简洁性来说都是多态比较好

可扩展能力更强

需要增加功能很方便容易实现

假如我们要给上述代码添内容,只需要进行方法重写即可

class Triangle extends Shape {

    @Override
    public void draw(){
        System. out.println("△");
    }
}


缺点

代码的运行效率降低
只针对于类中的方法,属性没有多态性 ,构造方法没有多态性,当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

希望对你有用

  • 51
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值