小工具知识网

当前位置: 首页 > 行业知识 > JavaSE--继承和多态

行业知识

JavaSE--继承和多态

2026-10-26 15:26:34 来源:互联网转载

目录

1.继承

1.1父类成员访问

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

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

1.2子类构造方法

1.3继承关系的执行顺序

1.4继承方式

1.5final关键字 

2.多态

2.1重写

2.2向上转型和向下转型

2.3多态的优缺点

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


1.继承

继承是面向对象编程中的一个概念,它允许一个类从另一个类继承属性和方法。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以继承父类的公共成员(如属性和方法),并且可以根据需要添加新成员或重写父类的成员。

继承可以帮助实现代码的重用和模块化,减少重复编写相似的代码的工作量。子类可以获得父类的属性和方法,并可以根据需要进行修改或扩展。通过继承,我们可以建立类的层次结构,从而更好地组织和管理代码。

在 Java 中如果要表示类之间的继承关系,需要借助 extends 关键字。
//Dog.java(子类)package demo1;public class Dog extends Animals{    public void bark(){        System.out.println(this.name+" wang wang wang~~~");    }}//Cat.java(子类)package demo1;public class Cat extends Animals{    public void mew(){        System.out.println(this.name+" mi mi mi~~~");    }}//Animals.java(父类)package demo1;public class Animals {    public String name;    public int age;    public void eat(){        System.out.println(this.name+" is eating...");    }}//Test.javapackage demo1;public class Test {    public static void main(String[] args) {        Dog dog = new Dog();        dog.name = "大黄";        dog.age = 2;        dog.eat();        dog.bark();        System.out.println("--------------------------");        Cat cat = new Cat();        cat.name = "小虎";        cat.age = 2;        cat.eat();        cat.mew();    }}

 

1.1父类成员访问

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

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

                直接访问或者使用this方法。

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

                用this访问子类变量,用super访问父类变量(super只能在非静态方法中使用)。

//Base.javapackage demo;public class Base {    public int a = 10;    public int b = 20;}//Derive.javapackage demo;public class Derive extends Base{    public int c = 30;    public int a = 100;    public void show(){        System.out.println(super.a);//父类中的a=10        System.out.println(this.a);//子类中的a=100        System.out.println(b);        System.out.println(c);    }}//Test.javapackage demo;public class Test {    public static void main(String[] args) {        Derive derive = new Derive();        derive.show();    }}//输出:10//     100//     20//     30
在子类方法中或者通过子类对象访问成员时 :
  • 如果访问的成员变量子类中有,优先访问自己的成员变量。
  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
  • 成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
    1.1.2子类中访问父类的成员方法

    和访问成员一样。

    1.2子类构造方法

    子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
    import java.util.Scanner;public class Main {    public static void main(String[] args) {        Scanner scanner = new Scanner(System.in);        while (scanner.hasNextInt()) {            int x = scanner.nextInt();            int y = scanner.nextInt();            int z = scanner.nextInt();            Sub sub = new Sub(x, y, z);            System.out.println(sub.calculate());        }    }}class Base {    private int x;    private int y;    public Base(int x, int y) {        this.x = x;        this.y = y;    }    public int getX() {        return x;    }    public int getY() {        return y;    }}class Sub extends Base {    private int z;    public Sub(int x, int y, int z) {        super(x,y);        this.z = z;    }    public int getZ() {        return z;    }    public int calculate() {        return super.getX() * super.getY() * this.getZ();    }}//输入:1  2  3 //输出:6
    • 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法。
    • 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的 父类构造方法调用,否则编译失败。
    • 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
    • super(...)只能在子类构造方法中出现一次,并且不能和this同时出现。
    • 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有 。

      1.3继承关系的执行顺序

      先来看没有继承关系时的执行顺序:

      class Person {    public String name;    public int age;    public Person(String name, int age) {    this.name = name;    this.age = age;    System.out.println("构造方法执行");    }{    System.out.println("实例代码块执行");}static {    System.out.println("静态代码块执行");    }}    public class TestDemo {    public static void main(String[] args) {    Person person1 = new Person("bit",10);    System.out.println("============================");    Person person2 = new Person("gaobo",20);    }}//执行结果://静态代码块执行//实例代码块执行//构造方法执行//============================//实例代码块执行//构造方法执行
      • 静态代码块先执行,并且只执行一次,在类加载阶段执行
      • 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行

        有继承关系时的执行顺序:

        class Person {    public String name;    public int age;    public Person(String name, int age) {    this.name = name;    this.age = age;    System.out.println("Person:构造方法执行");    }    {        System.out.println("Person:实例代码块执行");    }    static {        System.out.println("Person:静态代码块执行");    }}class Student extends Person{    public Student(String name,int age) {    super(name,age);    System.out.println("Student:构造方法执行");    }    {        System.out.println("Student:实例代码块执行");    }    static {        System.out.println("Student:静态代码块执行");    }}public class TestDemo4 {    public static void main(String[] args) {    Student student1 = new Student("张三",19);    System.out.println("===========================");    Student student2 = new Student("gaobo",20);}//执行结果://Person:静态代码块执行//Student:静态代码块执行//Person:实例代码块执行//Person:构造方法执行//Student:实例代码块执行//Student:构造方法执行//===========================//Person:实例代码块执行//Person:构造方法执行//Student:实例代码块执行//Student:构造方法执行
        • 父类静态代码块优先于子类静态代码块执行,且是最早执行
        • 父类实例代码块和父类构造方法紧接着执行
        • 子类的实例代码块和子类构造方法紧接着再执行
        • 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

          1.4继承方式

          继承关系一般不超过三层!

          1.5final关键字 

          1. 修饰变量或字段,表示常量 ( 即不能修改) 2. 修饰类:表示此类不能被继承 3. 修饰方法:表示该方法不能被重写

          2.多态

          多态是面向对象编程中的一个重要概念,它指的是同一个方法在不同的对象上具有不同的行为。多态性可以使得一个方法可以被不同类型的对象调用,从而实现不同的操作或者返回不同的结果。

          实现条件:

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

            下面是一个关于多态的简单例子:

            假设有一个“动物”类(Animal)和它的两个子类,分别是“狗”类(Dog)和“猫”类(Cat)。每个类都有一个“叫声”(makeSound)的方法。可以通过多态来实现不同动物的叫声。

            class Animal {    public void makeSound() {        System.out.println("动物发出叫声");    }}class Dog extends Animal {    @Override    public void makeSound() {        System.out.println("狗发出汪汪叫声");    }}class Cat extends Animal {    @Override    public void makeSound() {        System.out.println("猫发出喵喵叫声");    }}public class PolymorphismExample {    public static void main(String[] args) {        Animal animal1 = new Dog();        Animal animal2 = new Cat();                animal1.makeSound(); // 输出:狗发出汪汪叫声        animal2.makeSound(); // 输出:猫发出喵喵叫声    }}

            在上面的例子中,通过将父类引用(Animal)指向子类的对象(Dog、Cat),实现了多态性。通过调用父类引用的同名方法(makeSound),根据实际对象类型确定调用的是子类的方法。

            在这个例子中,无论是animal1还是animal2的类型都是Animal,但是实际指向的是Dog和Cat对象,所以调用makeSound方法时会根据实际对象类型来决定调用的方法。这样就实现了多态性,不同的动物对象调用makeSound方法时会有不同的叫声输出。

            2.1重写

            重写(Override)是面向对象编程中的一个概念,指的是在子类中重新定义父类中已经存在的方法,以便使得子类对象可以根据自己的特性来执行该方法。

            简单来说,当子类中定义了与父类中同名、同参数列表、同返回类型的方法时,就称为方法的重写。重写的方法在子类中可以有不同的实现,即可以改变方法的行为,但不能改变方法的签名(方法名、参数列表和返回类型)。

            重写的规则如下:

            • 重写的方法必须和父类的方法具有相同的方法名、参数列表和返回类型。
            • 重写的方法不能有更严格的访问权限,可以有更宽松的访问权限。例如,父类的方法是public,子类的重写方法可以是public或protected,但不能是private。
            • 重写的方法不能抛出比父类方法更多的异常,可以抛出相同的异常或子类异常,或者不抛出异常。
            • 当调用重写的方法时,实际执行的是子类中的方法,而不是父类中定义的方法。这个过程称为动态绑定。

              重写的目的是为了实现多态,通过子类的不同实现来满足不同的需求。在实际使用中,可以根据具体的业务场景来决定是否需要重写某个方法。

              2.2向上转型和向下转型

              向上转型(upcasting)指的是将一个子类的对象赋值给一个父类的引用变量。这样做可以实现多态性,即通过父类引用变量调用子类对象的方法。

              例如,假设有一个父类Animal和一个子类Dog,可以将一个Dog对象向上转型为Animal对象:

              Animal animal = new Dog();

              在这里,将Dog对象赋值给Animal引用变量,即实现了向上转型。

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

              向下转型(downcasting)指的是将一个父类的引用变量赋值给一个子类的引用变量。在向上转型后,如果需要使用子类特有的方法或属性,可以通过向下转型实现。

              例如,在上述例子中,可以使用向下转型将Animal引用变量转换为Dog对象:

              Dog dog = (Dog) animal;

              在这里,使用了强制类型转换符将Animal引用变量转换为Dog对象。

              需要注意的是,向下转型可能会出现运行时异常,因为在向上转型后,父类引用变量只能访问父类的方法和属性,无法访问子类特有的方法和属性。如果在向下转型时将父类引用变量转换为错误的子类类型,会导致ClassCastException异常的抛出。因此,在向下转型前,最好使用instanceof运算符进行类型检查,以确保转型的安全性。

                                                         

              2.3多态的优缺点

              多态的优点:

              1. 代码灵活性:多态允许使用父类引用变量来引用子类对象,这样可以实现更灵活的代码编写。通过多态,可以在不改变代码结构的情况下,通过改变对象的具体类型来修改程序的行为。

              2. 可扩展性:通过多态,可以轻松地扩展已有的代码。例如,可以通过添加新的子类来扩展已有的父类,而不需要修改原有的代码。

              3. 代码复用:多态可以在不重写代码的情况下,直接复用已有的父类代码。这样可以提高代码的可维护性和复用性。

              多态的缺点:

              1. 性能损失:多态在运行时需要进行类型检查和动态绑定,这会引入一定的性能损失。相比于直接调用对象的方法,通过多态来调用方法会稍微慢一些。

              2. 可读性降低:多态会增加代码的复杂性,使得代码更难阅读和理解。当多个不同的子类实现了相同的方法时,需要通过查看父类的定义来确定具体会执行哪个子类的方法。

              需要注意的是,多态并不适用于所有的情况。当需要调用特定子类的特定方法时,就不能使用多态。在这种情况下,可以通过向下转型来获得子类的引用,然后调用子类的方法。

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

              class B {    public B() {        // do nothing        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();    }}// 执行结果:D.func() 0
              • 构造 D 对象的同时, 会调用 B 的构造方法。
              • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func。
              • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1。
              • 所以在构造函数内,尽量避免使用实例方法,除了final和private方法。

                在构造方法中调用重写的方法可能会导致不可预知的行为。这是因为在对象构造期间,子类的构造方法会在父类的构造方法执行之前执行。因此,如果在父类的构造方法中调用了一个被子类重写的方法,那么实际调用的将是子类重写的方法。

                为了避免这种情况,应该尽量避免在构造方法中调用重写的方法。如果确实需要在构造方法中执行某些逻辑,可以考虑将这些逻辑提取为一个独立的方法,在构造方法执行完毕后再调用该方法。这样可以保证对象的正确构造和初始化。

上一篇:北京元人科技有限公司请问福利待遇如何。是否缴纳五险一金。

下一篇:揭秘安卓源码学习秘籍,带你走进安卓开发殿堂