Java面向对象的核心:继承与多态

        “继承”和“多态”是面向对象编程(OOP)中的两个核心概念,它们共同为代码的复用性、可扩展性和灵活性提供了强大的支持。在Java中,这两个概念被广泛应用于类的设计和程序的实现中。

一、继承

(一)基本概念

        在Java中,继承是一种通extends关键字实现类与类之间关系的机制。它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以复用父类的代码,同时还可以添加新的功能或修改父类的行为。

        例如,假如我们有一个People类,它定义了所有人的通用属性和通用行为

public class People {
    private String name;
    private int age;
  
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    public void eat() {
        System.out.println("人吃饭");
    }
}

        现在,我们可以创建一个Student类和一个Teacher类,他们都继承自People类:

class Student extends People {
    private String studentId;
    public void study() {
        System.out.println("学生学习");
    }
}

class Teacher extends People {
    private String teacherId;
    public void teach() {
        System.out.println("老师教学");
    }
}

        过让Student类和Teacher类继承自People类,我们可以复用People类的通用属性和行为,同时为每个子类添加特定的功能。这不仅让代码更加简洁,还提高了可维护性和可扩展性。

(二)继承的特性

1.成员变量的继承

        在子类中访问其他成员(成员变量或者成员方法)时,按照就近原则,先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果子父类中,出现了重名的变量,会优先使用子类的,如果一定要在子类中使用父类的相同变量,使用super

class Fu {
   String name="Fu的name";
}

class Zi extends Fu {
    String name="Zi的name";
    public void show(){
        String name="show的name";
        System.out.println(name);  // show的name
        System.out.println(this.name);// Zi的name
        System.out.println(super.name);// Fu的name
    }
}

        在这个例子中,Zi类继承了Fu类的name变量,但同时也定义了自己的name变量。在show方法中,name变量的访问顺序如下:

  1. 局部变量name(优先级最高)。

  2. 子类的成员变量name(通过this.name访问)。

  3. 父类的成员变量name(通过super.name访问)。

2.方法的继承与重写(Override)

        子类会继承父类中所有非私有的方法。在子类中可以直接调用父类的方法,就像这些方法是子类自己的方法一样。

class Fu {
    public void show() {
        System.out.println("Fu类的show方法");
    }
}

class Zi extends Fu {
    public void display() {
        show(); // 调用继承自父类的show方法
    }
}

        方法重写是继承中的一个重要概念,它允许子类根据自己的需求重新定义父类的方法。当子类重写父类的方法时,子类方法的名称和参数列表必须与父类方法完全一致。重写的方法可以改变方法的行为,但不能改变方法的签名。

        例如,假设我们有一个People类,它定义了一个skill方法:

class People {
    public void skill() {
        System.out.println("人有技能");
    }
}

        现在,我们创建了一个司机类和一个厨师类,司机和厨师都有各种特有的技能,因此这两类

都对父类的skill方法进行重写:

class Driver extends People {
    @Override
    public void skill() {
        System.out.println("司机会开车");
    }
}

class Cooker extends People {
    @Override
    public void skill() {
        System.out.println("厨师会做饭");
    }
}

         在这个例子中,Driver类和Cooker类都通过@Override注解明确表示它重写了父类的skill方法。当我们调用对象的skill方法时,输出将是“司机会开车”和”厨师会做饭“,而不是“人有技能”。

需要注意的是,方法重写需要满足以下条件

  • 子类方法的名称和参数列表必须与父类方法完全一致。

  • 子类方法的返回值类型必须与父类方法的返回值类型相同或兼容。

  • 子类方法不能抛出比父类方法更宽的异常。

  • 子类方法的访问修饰符不能比父类方法更严格。

3.子类构造器与父类构造器

        在Java中,子类的构造器总是会先调用父类的构造器,然后再执行自己的构造代码。

class Fu {
    public Fu() {
        System.out.println("Fu类无参构造器执行了");
    }
}

class Zi extends Fu {
    public Zi() {
        System.out.println("Zi类无参构造器执行了");
    }
}

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

运行这段代码时,输出将是:

Fu类无参构造器执行了
Zi类无参构造器执行了

        如果父类没有无参构造器,或者我们需要调用父类的有参构造器,可以通过super关键字显式调用。

class People {
    private String name;
    private int age;

    public People() {
    }

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

class Teacher extends People {
    private String subject;

    public Teacher() {
    }

    public Teacher(String name, int age, String subject) {
        super(name, age); // 调用父类的有参构造器
        this.subject = subject;
    }
}

        在这个例子中,Teacher类的有参构造器通过super(name, age)显式调用了父类People的有参构造器。

(三)继承的限制

虽然继承可以提高代码的复用性,但也有一些限制:

  • 单继承限制:Java中一个类只能继承一个父类,不能实现多继承。如果需要实现多继承的功能,可以通过接口来实现。

  • 继承的深度限制:虽然理论上可以有任意深度的继承关系,但过深的继承关系会使代码变得复杂,难以维护。因此,应尽量避免过深的继承关系。

  • 继承的耦合性:继承会增加类之间的耦合性,子类对父类的依赖度过高。如果父类发生变化,可能会对子类产生较大的影响。因此,在设计类时,应尽量减少类之间的耦合性。

二、多态

        多态是面向对象编程(OOP)中的一个重要概念,是指允许通过父类的引用调用子类的方法,具体调用哪个方法取决于运行时对象的实际类型,而不是引用的类型

  1. 继承关系:子类必须继承父类。

  2. 方法重写:子类必须重写父类的方法。

  3. 父类引用指向子类对象:通过父类类型的引用指向子类对象。

多态分为两种类型:编译时多态运行时多态

(一)多态类型

1.编译时多态(方法重载)

        方法重载(Overloading)是指在同一个类中,方法名相同但参数列表不同的方法。编译器根据方法的参数类型和数量来决定调用哪个方法。

class Calculator {
    public int add(int a, int b) {
        System.out.println("调用 int 参数的 add 方法");
        return a + b;
    }

    public double add(double a, double b) {
        System.out.println("调用 double 参数的 add 方法");
        return a + b;
    }
}

public class Test {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        calc.add(1, 2);        // 调用 int 参数的 add 方法
        calc.add(1.0, 2.0);    // 调用 double 参数的 add 方法
    }
}
2.运行时多态(方法重写)

        运行时多态(Override)是指通过子类重写父类的方法,在运行时根据对象的实际类型调用相应的方法。这是多态的核心形式。

class People {
    public void skill() {
        System.out.println("人有技能");
    }
}

class Driver extends People {
    @Override
    public void skill() {
        System.out.println("司机会开车");
    }
}

class Cooker extends People {
    @Override
    public void skill() {
        System.out.println("厨师会做饭");
    }
}

public class Test {
    public static void main(String[] args) {
        People people1 = new Driver();
        People people2 = new Cooker();

        Driver.skill(); // 输出:司机会开车
        Cooker.skill(); // 输出:厨师会做饭
    }
}

        在这个例子中,people1people1都是People类型的引用,但它们的实际类型分别是DriverCooker。调用skill方法时,运行时会根据对象的实际类型调用相应的方法。

(二)多态的应用

多态在实际开发中有很多应用场景,例如:

  • 接口编程:通过接口定义一组方法,然后让不同的类实现这些方法,从而实现多态。这种方式可以提高代码的可扩展性和可维护性。

  • 框架设计:在框架设计中,多态可以用于定义通用的接口或抽象类,然后让不同的实现类根据具体需求实现相应的方法。这种方式可以提高框架的灵活性和可扩展性。

  • 算法设计:在算法设计中,多态可以用于定义通用的算法接口,然后让不同的实现类根据具体需求实现相应的算法。这种方式可以提高算法的可扩展性和可维护性。

三、总结

        继承和多态是Java面向对象编程中的两个核心概念。继承通过extends关键字实现了类之间的父子关系,让子类可以复用父类的代码,同时还可以通过方法重写来扩展或修改父类的行为。多态则通过父类引用指向子类对象的方式,让代码更加灵活和可扩展。

你可能感兴趣的:(java学习笔记,java,开发语言)