(java)继承和多态 (详解)

这篇具有很好参考价值的文章主要介绍了(java)继承和多态 (详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1 继承

1.1为什么需要继承

1.2 继承概念

1.3 继承的语法

1.4 父类成员访问 

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

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

 1.5 super关键字

1.6 子类构造方法

1.7 super和this

1.7.1 this

1.7.2 super和this 

1.8 再谈初始化

1.9 继承方式

1.10 继承与组合

2 多态

2.1 多态的概念

2.2 多态实现条件

2.3 重写

2.4 向上转移和向下转型

2.4.1 向上转型

2.4.2 向下转型

2.5 多态的优缺点

2.6 避免在构造方法中调用重写的方法


1 继承

1.1为什么需要继承

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


(java)继承和多态 (详解)

 那我们在对猫和狗的各种动作进行描述时,利用Java,就会设计出这样的代码

(java)继承和多态 (详解)

发现了吗,这里面有很多重复的地方 

那能否将这些共性抽取呢?面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。(共性的抽取,达到代码的复用)

1.2 继承概念

继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
例如:上面的代码,狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用

图解:

(java)继承和多态 (详解)

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类Dog和Cat可以称为Animal的子类/派生类继承之后子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可
 

 从继承概念中可以看出继承最大的作用就是:实现代码复用

1.3 继承的语法

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

修饰符 class 子类 extends 父类{

}

实操 :(将上面的代码用继承方法重新设计)

上面的代码: 

 (java)继承和多态 (详解)

重新设计的: 

(java)继承和多态 (详解)

然后我们测试一下 :

 (java)继承和多态 (详解)

运行: 

 (java)继承和多态 (详解)

注意:1.没有赋值的话,默认放null或0

(java)继承和多态 (详解)

(java)继承和多态 (详解)

2.子类会将父类中的成员变量或者成员方法继承到子类中了
3. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

 

1.4 父类成员访问 

在继承体系中,子类将父类中的方法和字段继承下来了,那在子类中如何访问父类中继承下来的成员呢? 

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

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

(java)继承和多态 (详解)

(java)继承和多态 (详解)

 2. 子类和父类成员变量同名

(java)继承和多态 (详解)

(java)继承和多态 (详解)

(java)继承和多态 (详解)

总结:由上诉可知 

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

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

1. 成员方法名字不同 


(java)继承和多态 (详解)

(java)继承和多态 (详解)

总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。

2. 成员方法名字相同
 (java)继承和多态 (详解)

(java)继承和多态 (详解)

 总结:

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

 1.5 super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,直接访问是无法做到的Java提供了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 = 10;//相当于 this.a = 10
        b = '1';//相当于 this.b = '1'
        
        // 访问父类的成员变量时,需要借助super关键字
        // super是获取到子类对象中从基类继承下来的部分
        super.a = 3;
        super.b = 4;
        // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
        methodA(); // 没有传参,访问父类中的methodA()
        methodA(20); // 传递int参数,访问子类中的methodA(int)

        // 如果在子类中要访问重写的基类方法,则需要借助super关键字
        methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到
        super.methodB(); // 访问基类的methodB()
    }
}

简单来说就是,想要明确访问父类中的成员,要使用 super 关键字 

 注意:

  • super只能在非静态方法中使用
  • super是在子类方法中,访问父类的成员变量和方法。 

ps:那super和this的区别是什么? 

  • this可以访问父类,也可以访问子类
  • this优先访问子类(有同名时)
  • super只能访问从父类继承下来的成员变量
  • 当前类使用了super,那当前类一定是子类

1.6 子类构造方法

子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法。

  • 当子类继承了父类之后,一定要先帮助父类构造他的构造方法,然后再构造子类自己的构造方法(因为子类对象中成员是有两部分组成的,父类继承下来的以及子类新增加的部分
  • 父类的构造方法只能在子类中调用

代码演示: 

(java)继承和多态 (详解)

(java)继承和多态 (详解)

初学小贴士:构造方法不需要通过d.来访问,只要实例化对象就行 😀

                    (当调用完相应的构造方法之后,实例化对象才产生,也就是说你创建对象的时候即使不写构造方法,也会有一个默认的空构造方法)

注意:

  • 子类构造方法默认调用父类的无参构造方法super(),用户没有写时,编译器会自动添加,而且super(..)必须是子类构造方法中第一条语句,并且只能出现一次,并且不能和this()同时出现
  • 如果父类构造方法带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。

代码演示:

(java)继承和多态 (详解)

父类的构造方法有参数,子类没有,编译失败,如何做看下面吧

(java)继承和多态 (详解)

 在调用构造方法时,super(..)与this()不能同时出现,因为他们俩都只能在第一行

 (java)继承和多态 (详解)

1.7 super和this

1.7.1 this

再说super和this之前我先来介绍一下this 

  1. this引用 用在形参名不小心与成员变量名相同时

(java)继承和多态 (详解)

那函数体中到底是谁给谁赋值?形参给成员变量?还是成员变量给形参?傻傻分不清楚😵😵😵

看看这样会打印出什么?

(java)继承和多态 (详解)
 

(java)继承和多态 (详解)

这不是我们想要的,这时候就要用this了,

我们可以使用"this.属性"或"this.方法"的方式,调用当前正在运行的对象属性或方法。

如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性(类中的属性),而非形参。

(java)继承和多态 (详解)

(java)继承和多态 (详解)

(java)继承和多态 (详解)

 总结:

  • this表示当前对象的引用,不能再引用其他对象(成员方法运行时调用该成员方法的对象)
  • this只能使用在非静态方法(又称实例方法,或成员方法)中谁调用这个实例方法,this就是谁。所以this代表的是:当前对象。
  • this不能在静态方法中使用, 因为this代表当前对象,静态方法中不存在当前对象。  

ps:静态方法静态方法属于类,可以在实例化之前被类直接调用

(java)继承和多态 (详解)

   2.可以通过this调用其他构造方法来简化代码 (语法this();)

  • this();调用当前类当中的其他构造方法(看匹配到那个,就调用那个)
  • 只能在当前的构造方法内部来使用
  • 只能在第一行 

(java)继承和多态 (详解)

(java)继承和多态 (详解)

(java)继承和多态 (详解)

1.7.2 super和this 

  • < 相同点>

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

  • <不同点>

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

1.8 再谈初始化

1、父类静态代码块优先子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

(java)继承和多态 (详解)  

(java)继承和多态 (详解)

1.9 继承方式

Java中只支持以下几种继承方式:

  1.单继承
public class A{
}
public class  B extends A{
}
  2.多层继承
public class A{
}
public class  B extends A{
}

public class C extends B{

}

3.不同类继承同一个类 
public class A{
}
public class  B extends A{
}

public class C extends A{

}

注意:

  • Java中不支持多继承。(public class A{ }   public class B{ } public class C extends A,B )
  •  一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.
  • 如果想从语法上进行限制继承, 就可以使用 final 关键字 

1.10 继承与组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。

  • 继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
  • 组合表示对象之间是has-a的关系,比如:汽车

(java)继承和多态 (详解)

ps:  组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,

       一般建议:能用组合尽量用组合

2 多态  

2.1 多态的概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态

同一件事情,发生在不同对象身上,就会产生不同的结果。

2.2 多态实现条件

在java中要实现多态,必须要满足如下几个条件,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

2.3 重写

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

 方法重写的规则:

  • 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的

(java)继承和多态 (详解)

  • 访问权限不能比父类中被重写的方法的访问权限更低。(private < 默认权限 < protected < public)
  • 父类被static、private修饰的方法、构造方法都不能被重写。
  • 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.

(java)继承和多态 (详解)

(java)继承和多态 (详解)

重写重载区别 :

区别点 重写(override) 重载(override)
参数列表 一定不能修改 必须修改
返回类型 一定不能修改【除非可以构成父子类关系】 可以修改
访问限定符 一定不能做更严格的限制(可以降低限制) 可以修改

2.4 向上转移和向下转型


2.4.1 向上转型

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

当向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法。也会覆盖掉与父类中相同的方法(重写)。(向上转型,无法访问子类特有的成员)
所以向上转型时,可以调用的方法就是,父类中有的,但是子类中没有的方法,和子类中的重写父类的方法

1. 直接赋值 

(java)继承和多态 (详解)
2. 方法传参

(java)继承和多态 (详解)
3. 方法返回

(java)继承和多态 (详解)

向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。

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

在编译的时候确实认为应该调用Animal的eat方法。但是运行的时候发现,子类重写父类的这个
eat方法,所以会直接调用子类 

(java)继承和多态 (详解)

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


2.4.2 向下转型

向下转型,可以调用子类的所有成员。

我们看下面的代码报错了,因为这样调用的是父类的fly()方法,但是父类中有没有fly()方法,当然报错上面的可以调用子类中的eat()方法,是因为父类中本来就有eat()方法,但是子类中又重写了eat方法,调用时发生了动态绑定,所以调用了子类的方法) 

(java)继承和多态 (详解)

此时就利用了向下转型,但是不安全 

(java)继承和多态 (详解)

 你往下看

(java)继承和多态 (详解)

报了一个错误: Dog cannot be cast to Bird

(java)继承和多态 (详解)

Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换,是这样用的,如下:
 

(java)继承和多态 (详解)

2.5 多态的优缺点

比如我们想打印【"●", "♦", "●", "♦", "❀"】, 那我们利用多态思想就会写出这样的代码

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

利用多态 :(动态绑定,向上转型)

public class Test {
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        Cycle cycle = new Cycle();
        Rect rect = new Rect();
        Flower flower = new Flower();
        Shape[] shapes = {cycle, rect, cycle, rect, flower};
        for(int i = 0; i < shapes.length; i++){
           drawMap(shapes[i]);
        }
    }

}

(java)继承和多态 (详解)

如果没有多态就会这样:(很多if else 语句)

public class Test2 {
    public static void main(String[] args) {
        Cycle cycle = new Cycle();
        Rect rect = new Rect();
        Flower flower = new Flower();
        String[] shape = {"cycle", "rect", "cycle", "rect", "flower"};
        for(int i = 0; i < shape.length; i++){
            if (shape[i].equals("cycle")) {
                cycle.draw();
            } else if (shape[i].equals("rect")) {
                rect.draw();
            } else if (shape[i].equals("flower")) {
                flower.draw();
            }
        }
    }
}

总结

多态优点:

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

2. 可扩展能力更强
    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低

 多态缺陷:代码的运行效率降低

2.6 避免在构造方法中调用重写的方法

一段有坑的代码. 我们创建两个类, B 是父类, D 是子类. D 中重写 func() 方法. 并且在 B 的构造方法中调用 func()

public class B {
    public B() {
        func();
    }

     public void func() {
         System.out.println("B.func()");
     }
}

class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
}

 运行结果:

 (java)继承和多态 (详解)

是0,可是我们的num明明被初始化赋值成了1 ,因为此时D这个对象还没有构造完成

(java)继承和多态 (详解)

 
所以在构造函数内,尽量避免使用实例方法,除了final和private方法。
结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.

 

╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯完╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯╰(*°▽°*)╯文章来源地址https://www.toymoban.com/news/detail-435847.html

到了这里,关于(java)继承和多态 (详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【Java0基础学Java第八颗】 -- 继承与多态 -- 多态

    【Java0基础学Java第八颗】 -- 继承与多态 -- 多态

    通俗来说就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态 。 就比如打印机,彩色打印机和黑白打印机,打印出的效果一个是彩色,一个是黑白。 即: 同一件事情,发生在不同对象身上,就会产生不同的结果 。 在Java中要实现多态,

    2024年02月04日
    浏览(10)
  • Java之继承和多态

    Java之继承和多态

    1.  为什么需要继承 先看下代码 // Dog.java public class Dog { string name ; int age ; float weight ; public void eat (){ System . out . println ( name + \\\" 正在吃饭 \\\" ); } public void sleep (){ System . out . println ( name + \\\" 正在睡觉 \\\" ); } void Bark (){ System . out . println ( name + \\\" 汪汪汪 ~~~\\\" ); } } // Cat.Java public class Ca

    2024年04月23日
    浏览(13)
  • 面向对象详解,面向对象的三大特征:封装、继承、多态

    面向对象详解,面向对象的三大特征:封装、继承、多态

    一、面向对象与面向过程 面向对象编程 (Object-Oriented Programming,简称OOP)和 面向过程编程 (Procedural Programming,简称PP)是两种不同的 编程范式 。 面向对象编程强调把问题分解成对象,通过封装、继承和多态等机制,来处理对象之间的关系 。每个对象都可以独立地处理自

    2024年02月21日
    浏览(15)
  • Java三大特性:封装、继承、多态

    Java三大特性:封装、继承、多态

    高内聚,低耦合 高内聚:类内部操作自己完成,不允许外部干涉。 低耦合:仅暴露少量的方法给外部使用。 封装(数据的隐藏) 通常应禁止直接访问一个对象中数据的实际表达,而应该通过操作接口来访问,这称为信息的隐藏。 封装的特点 1.提高程序的安全性,保护数据

    2024年03月21日
    浏览(14)
  • Java 封装 继承 多态(深入理解)

    Java 封装 继承 多态(深入理解)

    登神长阶 第二阶 封装 继承 多态 🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀 目录 🍒一.面向对象编程的三大特性 🍍二.封装 🧉1.定义及其作用  🥝2.访问限定符 🫛3.封装扩展 包(package) 🥕3.1.定义及其作用  🥦 3.2.导入包的类 🍔3.3.自定义包 🌯

    2024年03月11日
    浏览(10)
  • Java系列——封装、继承、多态初了解

    Java系列——封装、继承、多态初了解

    目录 一、前言 二、封装 1.什么是封装?   2.封装的特点 3.封装的使用 三、继承 1.什么是继承? 2.继承的特点 3.继承的优点 4.继承的使用  4.1 继承的格式  4.2 继承的演示 4.3 成员变量 4.4 成员方法 4.5 构造方法 五、多态 1.什么是多态? 2.多态的特点 3.多态的使用  4.引用类型

    2024年02月08日
    浏览(14)
  • 【Java初阶(六)上】封装 继承 多态

    【Java初阶(六)上】封装 继承 多态

    ❣博主主页: 33的博客❣ ▶文章专栏分类: Java从入门到精通◀ 🚚我的代码仓库: 33的代码仓库🚚 对于面向对象程序三大特性:封装、继承、多态。这篇文章将会详细讲解到如何实现封装、继承、多态,以及具体的应用。 本章重点 掌握封装的概念,如何实现封装,包的概念,继

    2024年04月09日
    浏览(14)
  • Java面向对象 - 封装、继承和多态

    目录 第1关:什么是封装,如何使用封装 第2关:什么是继承,怎样使用继承 第3关:super的使用 第4关:方法的重写与重载 第5关:抽象类 第6关:final的理解与使用 第7关:接口 第8关:什么是多态,怎么使用多态 Java_Educoder

    2024年02月07日
    浏览(13)
  • java基础语法-package构造方法-继承-多态

    java中的包 - package 包的主要功能: 包的基本语法 在一个文件中,可以没有包,或者一个包。但是不能出现两个包。 包名一般小写,是为了区分类名,类名一般大写 java中存在不同包相同类的名称,我们可以使用包名进行区分 一般情况下,在使用类的情况下,我们都使用类的

    2024年02月05日
    浏览(15)
  • 1.0、Java 继承与多态 - 成员变量访问特点

    父类:Father.java 文件如下所示 - 子类:Child.java 文件如下所示 - - 父类对象只能访问父类中的成员变量; - 而子类对象既可以访问父类中的成员变量,也可以访问自己类中的成员变量;   在父子类的继承关系当中,如果父类和子类的成员变量重名时,子类对象有下面两种访问

    2023年04月08日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包