Java面向对象高级-继承、多态

一、继承

继承是 Java 中实现代码复用的重要手段,它允许一个类获取另一个类的属性和方法。通过extends关键字,建立类之间的父子关系。

(一)继承的定义与作用

例如,在员工管理系统的场景下,Teacher类和Consultant类都有name属性,为了减少重复代码,可以创建一个父类People,将name属性提取到People类中。

java

typescript 复制代码
class People {
    private String name;

    public String getName() {
        return name;
    }

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

class Teacher extends People {
    private String skill;

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}

class Consultant extends People {
    private int number;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}

在上述代码中,TeacherConsultant类继承自People类,它们可以使用People类中的getNamesetName方法,提高了代码的重用性。

(二)权限修饰符

权限修饰符用于限制类中成员(成员变量、成员方法、构造器)的访问范围,Java 中有private、缺省(默认不写修饰符)、protectedpublic四种权限修饰符。

修饰符 本类里 同一个包中的其他类 不同包的子孙类 不同包的任意类
private
缺省(默认不写修饰符)
protected
public
Java 复制代码
class AccessModifierExample {
    private int privateVar;
    int defaultVar;
    protected int protectedVar;
    public int publicVar;

    private void privateMethod() {}
    void defaultMethod() {}
    protected void protectedMethod() {}
    public void publicMethod() {}
}

在这个类中,privateVarprivateMethod只能在本类中访问;defaultVardefaultMethod在同一个包中的其他类可以访问;protectedVarprotectedMethod在不同包的子孙类中也能访问;publicVarpublicMethod在任何地方都可以访问 。

(三)继承的特点

  1. 单继承模式:Java 是单继承的,一个类只能继承一个直接父类,这避免了多继承带来的冲突。例如:

java

kotlin 复制代码
class A {}
class B {}
// 以下代码会报错,因为Java不支持多继承
// class C extends A, B {} 
  1. 多层继承 :Java 支持多层继承,所有类最终都直接或间接继承自Object类。例如:

java

scala 复制代码
class Grandparent {}
class Parent extends Grandparent {}
class Child extends Parent {}

在这个例子中,Child类间接继承了Grandparent类的非私有成员,并且Object类是所有类的祖宗类。

  1. 子类访问成员特点 :子类访问成员遵循就近原则,先在子类局部范围找,然后到子类成员范围找,最后到父类成员范围找,找不到则报错。如果子父类中出现重名成员,优先使用子类的,若要访问父类成员,可通过super关键字。例如:

java

scala 复制代码
class ParentClass {
    int value = 10;
}

class ChildClass extends ParentClass {
    int value = 20;

    void printValue() {
        // 输出子类的value
        System.out.println("子类的value: " + value);
        // 输出父类的value
        System.out.println("父类的value: " + super.value);
    }
}

ChildClassprintValue方法中,通过super.value访问父类的value成员变量。

(四)方法重写

当子类觉得父类中的某个方法不好用或无法满足自身需求时,可以重写该方法。方法重写需要满足方法名称、参数列表一样,并且要注意一些规则。

java

scala 复制代码
class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    // 使用@Override注解校验方法重写格式
    @Override
    public void sound() {
        System.out.println("狗汪汪叫");
    }
}

在上述代码中,Dog类重写了Animal类的sound方法。重写时需注意:

  • 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public > protected > 缺省)。
  • 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
  • 私有方法、静态方法不能被重写,如果重写会报错。

(五)子类构造器

子类的全部构造器,都会先调用父类的构造器,再执行自己。默认情况下,子类构造器的第一行代码是super()(写不写都有),它会调用父类的无参数构造器;如果父类没有无参数构造器,则必须在子类构造器的第一行手写super(...),指定调用父类的有参数构造器。

java

arduino 复制代码
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 skill;

    public Teacher() {}

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

Teacher类的有参数构造器中,通过super(name, age)调用父类的有参数构造器,先初始化从父类继承的属性,再初始化自身的skill属性。

(六)thissuper

this代表本类对象的引用,super代表父类存储空间的标识,它们在访问成员变量、成员方法和构造方法时有不同的用法。

java

csharp 复制代码
class Parent {
    int value = 10;

    void print() {
        System.out.println("父类的print方法");
    }

    Parent() {
        System.out.println("父类的无参构造器");
    }

    Parent(int value) {
        this.value = value;
        System.out.println("父类的有参构造器,value: " + value);
    }
}

class Child extends Parent {
    int value = 20;

    void print() {
        // 访问本类的value
        System.out.println("本类的value: " + this.value);
        // 访问父类的value
        System.out.println("父类的value: " + super.value);
        // 调用本类的print方法
        this.print();
        // 调用父类的print方法
        super.print();
    }

    Child() {
        // 调用父类的无参构造器
        super();
        System.out.println("子类的无参构造器");
    }

    Child(int value) {
        // 调用父类的有参构造器
        super(value);
        this.value = value;
        System.out.println("子类的有参构造器,value: " + value);
    }
}

Child类中,通过thissuper可以清晰地区分访问本类和父类的成员。需要注意的是,this(...)super(...)都只能放在构造器的第一行,且二者不能同时出现。

二、多态

多态是在继承 / 实现情况下的一种现象,表现为对象多态和行为多态,它使程序更加灵活和可扩展。

(一)多态的定义与前提

多态的前提是有继承 / 实现关系、存在父类引用子类对象、存在方法重写。例如:

java

scala 复制代码
class Animal {
    public void move() {
        System.out.println("动物在移动");
    }
}

class Dog extends Animal {
    @Override
    public void move() {
        System.out.println("狗在跑");
    }
}

class Cat extends Animal {
    @Override
    public void move() {
        System.out.println("猫在走");
    }
}

使用时:

java

arduino 复制代码
public class Main {
    public static void main(String[] args) {
        // 父类引用指向子类对象,体现对象多态
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();

        // 调用重写的方法,体现行为多态
        animal1.move();
        animal2.move();
    }
}

在上述代码中,Animal是父类,DogCat是子类,它们重写了父类的move方法。通过父类引用指向不同的子类对象,调用move方法时会执行子类中重写后的方法。需要注意的是,Java 中的属性(成员变量)不谈多态。

(二)多态的好处

  1. 解耦合与便于扩展维护:在多态形式下,右边对象是解耦合的,更便于扩展和维护。例如,当需要添加新的动物子类时,只需要创建新的子类并实现相应的方法,而不需要修改使用多态的代码。

  2. 增强方法扩展性:定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。例如:

java

typescript 复制代码
class Animal {
    public void eat() {
        System.out.println("动物在进食");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗在啃骨头");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫在吃鱼");
    }
}

public class Main {
    // 使用父类类型作为形参
    public static void feed(Animal animal) {
        animal.eat();
    }

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

        // 可以传入不同的子类对象
        feed(dog);
        feed(cat);
    }
}

feed方法中,使用Animal类型的形参,可以接收DogCat等子类对象,提高了代码的扩展性。但多态下不能直接使用子类的独有功能,例如Dog类可能有bark方法,但通过Animal引用无法直接调用bark方法。

(三)多态下的类型转换

  1. 自动类型转换父类 变量名 = new 子类();,例如Animal animal = new Dog();,这是将子类对象自动转换为父类类型,也称为向上转型。在向上转型过程中,子类对象会失去子类特有的方法,只能调用父类中定义的方法。

  2. 强制类型转换子类 变量名 = (子类) 父类变量,例如Dog dog = (Dog) animal;,这是将父类引用强制转换为子类类型,也称为向下转型。但在进行强制类型转换前,建议使用instanceof关键字判断当前对象的真实类型,以避免运行时的ClassCastException异常。例如:

java

java 复制代码
public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();

        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.bark();
        }
    }
}

在上述代码中,通过instanceof判断animal对象是否是Dog类型,如果是,则进行强制类型转换并调用Dog类特有的bark方法。需要注意的是,存在继承 / 实现关系就可以在编译阶段进行强制类型转换,但运行时如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常。

相关推荐
TDengine (老段)18 分钟前
TDengine 使用最佳实践
java·大数据·数据库·物联网·时序数据库·iot·tdengine
怦然心动~32 分钟前
springboot 3 集成Redisson
java·spring boot·redisson
小Mie不吃饭1 小时前
Maven | 站在初学者的角度配置
java·spring boot·maven
林犀居士1 小时前
JVM系统变量的妙用
java·jvm系统变量
北枫寒夜2 小时前
JVM和运行时数据区
java·jvm
StevenLdh2 小时前
Java Spring Boot 常用技术及核心注解
java·开发语言·spring boot
考虑考虑2 小时前
JDK21中的Switch模式匹配
java·后端·java ee
IT利刃出鞘2 小时前
maven--依赖的搜索顺序
java·maven
ThisIsClark3 小时前
【gopher的java学习笔记】如何知道一个jar包对应的maven中的groupId和atrifactId
java·笔记·学习