引言

        在 Java 面向对象编程中,继承多态是两大核心特性,它们共同支撑了代码的复用性、扩展性和灵活性。本章将从继承的基本实现开始,逐步深入到方法覆盖、访问控制、抽象类等概念,最终揭示多态的本质与应用。通过大量可运行的代码示例和直观的图表,帮助你彻底掌握这些重要知识点。

7.1 类的继承

        继承是面向对象编程的三大特性之一,它允许我们基于已有的类创建新类,从而实现代码复用和扩展。被继承的类称为父类(超类),新创建的类称为子类(派生类)

7.1.1 类继承的实现

在 Java 中,使用extends关键字实现类的继承,语法如下:

class 子类名 extends 父类名 {// 子类新增属性和方法
}

核心特点

  • 子类拥有父类的非私有属性和方法(代码复用)
  • 子类可以新增自己的属性和方法(功能扩展)
  • Java 只支持单继承(一个子类只能有一个直接父类)

代码示例:基础继承实现

// 父类:动物
class Animal {// 父类属性protected String name;// 父类方法public void eat() {System.out.println(name + "正在吃东西");}public void sleep() {System.out.println(name + "正在睡觉");}
}// 子类:狗(继承自动物)
class Dog extends Animal {// 子类新增属性private String breed; // 品种// 子类新增方法public void bark() {System.out.println(name + "在汪汪叫");}// 子类setter方法(设置名字和品种)public void setInfo(String name, String breed) {this.name = name; // 直接访问父类的protected属性this.breed = breed;}public void showBreed() {System.out.println("品种:" + breed);}
}// 测试类
public class InheritanceDemo {public static void main(String[] args) {// 创建子类对象Dog dog = new Dog();dog.setInfo("旺财", "金毛");// 调用父类继承的方法dog.eat();   // 输出:旺财正在吃东西dog.sleep(); // 输出:旺财正在睡觉// 调用子类新增的方法dog.bark();     // 输出:旺财在汪汪叫dog.showBreed();// 输出:品种:金毛}
}

类图:Animal 与 Dog 的继承关系

@startuml
class Animal {- String name+ void eat()+ void sleep()
}class Dog {- String breed+ void bark()+ void setInfo(String, String)+ void showBreed()
}Animal <|-- Dog : extends
@enduml

7.1.2 方法覆盖

        当子类需要修改父类的方法实现时,可以使用方法覆盖(Override),也称为方法重写。

方法覆盖的规则

  1. 方法名、参数列表必须与父类完全相同
  2. 返回值类型:父类返回值为T,子类可以是TT的子类(协变返回)
  3. 访问权限:子类方法权限不能低于父类(如父类protected,子类可protectedpublic
  4. 不能抛出比父类更多的 checked 异常
  5. @Override注解显式声明(非必须,但推荐,编译器会校验正确性)

代码示例:方法覆盖实现

// 父类:形状
class Shape {// 父类方法:计算面积(默认实现)public double calculateArea() {System.out.println("形状的面积计算");return 0.0;}
}// 子类:圆形(重写面积计算方法)
class Circle extends Shape {private double radius; // 半径public Circle(double radius) {this.radius = radius;}// 重写父类的面积计算方法@Overridepublic double calculateArea() {System.out.println("圆形的面积计算");return Math.PI * radius * radius; // 圆面积公式:πr²}
}// 子类:矩形(重写面积计算方法)
class Rectangle extends Shape {private double length; // 长private double width;  // 宽public Rectangle(double length, double width) {this.length = length;this.width = width;}// 重写父类的面积计算方法@Overridepublic double calculateArea() {System.out.println("矩形的面积计算");return length * width; // 矩形面积公式:长×宽}
}// 测试类
public class OverrideDemo {public static void main(String[] args) {Shape circle = new Circle(5);System.out.println("圆面积:" + circle.calculateArea()); // 输出:圆形的面积计算 圆面积:78.539...Shape rectangle = new Rectangle(4, 6);System.out.println("矩形面积:" + rectangle.calculateArea()); // 输出:矩形的面积计算 矩形面积:24.0}
}

7.1.3 super 关键字

super关键字用于访问父类的属性、方法和构造器,主要场景:

  • 调用父类的非私有属性:super.属性名
  • 调用父类的非私有方法:super.方法名(参数)
  • 调用父类的构造器:super(参数)(必须放在子类构造器第一行)

代码示例:super 关键字的使用

// 父类:员工
class Employee {protected String name;protected double salary;// 父类构造器public Employee(String name, double salary) {this.name = name;this.salary = salary;}// 父类方法public void showInfo() {System.out.println("姓名:" + name + ",薪资:" + salary);}
}// 子类:经理(继承自员工)
class Manager extends Employee {private double bonus; // 奖金// 子类构造器public Manager(String name, double salary, double bonus) {super(name, salary); // 调用父类构造器(必须在第一行)this.bonus = bonus;}// 重写父类方法,并用super调用父类方法@Overridepublic void showInfo() {super.showInfo(); // 调用父类的showInfo()System.out.println("奖金:" + bonus + ",总薪资:" + (salary + bonus));}// 子类方法:使用super访问父类属性public void raiseSalary(double percent) {// 父类salary是protected,子类可通过super访问salary = salary * (1 + percent / 100) + bonus; System.out.println(name + "的薪资已调整为:" + salary);}
}// 测试类
public class SuperDemo {public static void main(String[] args) {Manager manager = new Manager("张三", 8000, 2000);manager.showInfo(); // 输出:// 姓名:张三,薪资:8000.0// 奖金:2000.0,总薪资:10000.0manager.raiseSalary(10); // 涨薪10%// 输出:张三的薪资已调整为:9800.0}
}

7.1.4 调用父类的构造方法

        子类构造器中,默认会隐式调用父类的无参构造器super());如果父类没有无参构造器,子类必须显式调用父类的有参构造器(super(参数)),否则编译报错。

流程图:构造器调用顺序

代码示例:父类构造器调用

// 父类:Person
class Person {private String name;private int age;// 父类有参构造器(注意:没有无参构造器)public Person(String name, int age) {this.name = name;this.age = age;System.out.println("Person构造器被调用:" + name + "," + age + "岁");}
}// 子类:Student(继承自Person)
class Student extends Person {private String school; // 学校// 子类构造器:必须显式调用父类有参构造器public Student(String name, int age, String school) {super(name, age); // 显式调用父类构造器(否则编译报错)this.school = school;System.out.println("Student构造器被调用:" + school);}
}// 测试类
public class ConstructorCallDemo {public static void main(String[] args) {// 创建子类对象时,先调用父类构造器,再调用子类构造器Student student = new Student("李四", 18, "北京大学");// 输出:// Person构造器被调用:李四,18岁// Student构造器被调用:北京大学}
}

7.1 综合案例:动物继承体系

需求:设计动物继承体系,包含父类Animal子类DogCat,展示继承、方法覆盖和 super 关键字的综合应用。

类图

@startuml
class Animal {- String name+ Animal(String name)+ void eat()+ void makeSound()+ void sleep()
}class Dog {+ Dog(String name)+ void makeSound()+ void fetch()
}class Cat {+ Cat(String name)+ void makeSound()+ void climbTree()
}Animal <|-- Dog
Animal <|-- Cat
@enduml

完整代码

// 父类:动物
class Animal {protected String name; // 名字// 父类构造器public Animal(String name) {this.name = name;System.out.println("Animal构造器:" + name);}// 吃东西(通用实现)public void eat() {System.out.println(name + "在吃东西");}// 发出声音(父类默认实现)public void makeSound() {System.out.println(name + "发出声音");}// 睡觉(通用实现)public void sleep() {System.out.println(name + "在睡觉");}
}// 子类:狗
class Dog extends Animal {// 子类构造器public Dog(String name) {super(name); // 调用父类构造器System.out.println("Dog构造器:" + name);}// 重写:狗的叫声@Overridepublic void makeSound() {System.out.println(name + "汪汪叫");}// 子类特有方法:捡东西public void fetch() {System.out.println(name + "在捡球");super.eat(); // 调用父类的eat()方法}
}// 子类:猫
class Cat extends Animal {// 子类构造器public Cat(String name) {super(name); // 调用父类构造器System.out.println("Cat构造器:" + name);}// 重写:猫的叫声@Overridepublic void makeSound() {System.out.println(name + "喵喵叫");}// 子类特有方法:爬树public void climbTree() {System.out.println(name + "在爬树");}
}// 测试类
public class AnimalInheritanceDemo {public static void main(String[] args) {System.out.println("===== 创建Dog对象 =====");Dog dog = new Dog("旺财");dog.eat();      // 继承父类方法dog.makeSound();// 调用重写的方法dog.sleep();    // 继承父类方法dog.fetch();    // 子类特有方法System.out.println("\n===== 创建Cat对象 =====");Cat cat = new Cat("咪咪");cat.eat();      // 继承父类方法cat.makeSound();// 调用重写的方法cat.sleep();    // 继承父类方法cat.climbTree();// 子类特有方法}
}

运行结果

7.2 封装性与访问修饰符

        封装是将数据和操作数据的方法捆绑在一起,并通过访问修饰符控制外部访问权限,实现 "数据隐藏"。

7.2.1 类的访问权限

Java 中类的访问权限只有两种:

  • public:公开类,可被所有包中的类访问
  • 默认权限(无修饰符):包内可见,仅同一包中的类可访问

规则

  • 一个 Java 源文件中最多有一个public,且文件名必须与public类名相同
  • 若类为public,其包路径需与文件夹结构一致

代码示例:类访问权限

// 文件:com/example/PublicClass.java(public类)
package com.example;
public class PublicClass {public void publicMethod() {System.out.println("public类的public方法");}
}// 文件:com/example/DefaultClass.java(默认权限类)
package com.example;
class DefaultClass { // 无访问修饰符,默认权限public void defaultClassMethod() {System.out.println("默认类的public方法");}
}// 文件:com/other/TestClass.java(不同包的测试类)
package com.other;
import com.example.PublicClass;
// import com.example.DefaultClass; // 编译报错:DefaultClass是默认权限,不同包不可访问public class TestClass {public static void main(String[] args) {PublicClass publicObj = new PublicClass();publicObj.publicMethod(); // 正常访问:public类可跨包访问// DefaultClass defaultObj = new DefaultClass(); // 编译报错:无法访问默认权限类}
}

7.2.2 类成员的访问权限

类成员(属性和方法)有 4 种访问权限,权限从大到小为:

修饰符本类同包类不同包子类其他类
public✔️✔️✔️✔️
protected✔️✔️✔️
默认✔️✔️
private✔️

最佳实践

  • 属性通常用private修饰,通过publicgetter/setter方法访问
  • 方法根据需要设置权限,对外暴露的接口用public,内部工具方法用private
  • 父子类共享的方法 / 属性用protected

代码示例:成员访问权限

// 父类:com/example/Parent.java
package com.example;
public class Parent {public String publicField = "public属性";protected String protectedField = "protected属性";String defaultField = "default属性"; // 默认权限private String privateField = "private属性";public void publicMethod() {System.out.println("public方法:" + privateField); // 本类可访问private}protected void protectedMethod() {System.out.println("protected方法");}void defaultMethod() {System.out.println("default方法");}private void privateMethod() {System.out.println("private方法");}
}// 同包子类:com/example/ChildSamePackage.java
package com.example;
public class ChildSamePackage extends Parent {public void accessParent() {System.out.println(publicField); // ✔️ publicSystem.out.println(protectedField); // ✔️ protectedSystem.out.println(defaultField); // ✔️ 同包默认权限// System.out.println(privateField); // ❌ 不可访问privatepublicMethod(); // ✔️protectedMethod(); // ✔️defaultMethod(); // ✔️ 同包// privateMethod(); // ❌}
}// 不同包子类:com/other/ChildDifferentPackage.java
package com.other;
import com.example.Parent;
public class ChildDifferentPackage extends Parent {public void accessParent() {System.out.println(publicField); // ✔️ publicSystem.out.println(protectedField); // ✔️ protected(子类)// System.out.println(defaultField); // ❌ 不同包默认权限不可访问// System.out.println(privateField); // ❌publicMethod(); // ✔️protectedMethod(); // ✔️ 子类可访问// defaultMethod(); // ❌ 不同包默认方法不可访问// privateMethod(); // ❌}
}// 不同包非子类:com/other/OtherClass.java
package com.other;
import com.example.Parent;
public class OtherClass {public void accessParent() {Parent parent = new Parent();System.out.println(parent.publicField); // ✔️ public// System.out.println(parent.protectedField); // ❌ 非子类不可访问protected// System.out.println(parent.defaultField); // ❌ 不同包默认不可访问// System.out.println(parent.privateField); // ❌parent.publicMethod(); // ✔️// parent.protectedMethod(); // ❌ 非子类不可访问// parent.defaultMethod(); // ❌// parent.privateMethod(); // ❌}
}

7.2 综合案例:封装与访问控制

需求:设计一个User类,通过访问修饰符实现封装,提供安全的属性访问方式。

完整代码

package com.example.encapsulation;// 用户类(封装示例)
public class User {// 属性私有化(private)private String username; // 用户名private String password; // 密码private int age;         // 年龄// 无参构造器public User() {}// 有参构造器public User(String username, String password, int age) {this.username = username;this.password = password;this.age = age;}// 用户名的getter(public,对外提供读取权限)public String getUsername() {return username;}// 密码的getter(仅返回脱敏后的密码)public String getPasswordMasked() {if (password == null || password.length() <= 2) {return "***";}return password.substring(0, 2) + "***"; // 前2位显示,其余脱敏}// 密码的setter(提供修改权限,带简单验证)public void setPassword(String password) {if (password == null || password.length() < 6) {throw new IllegalArgumentException("密码长度不能少于6位");}this.password = password;}// 年龄的getterpublic int getAge() {return age;}// 年龄的setter(带验证逻辑)public void setAge(int age) {if (age < 0 || age > 150) {throw new IllegalArgumentException("年龄必须在0-150之间");}this.age = age;}// 公开方法:用户登录public boolean login(String inputPassword) {return password.equals(inputPassword); // 内部可访问private属性}
}// 测试类
public class EncapsulationDemo {public static void main(String[] args) {User user = new User("zhangsan", "123456", 25);// 访问用户名(通过getter)System.out.println("用户名:" + user.getUsername()); // 输出:用户名:zhangsan// 访问脱敏密码System.out.println("密码(脱敏):" + user.getPasswordMasked()); // 输出:密码(脱敏):12***// 测试年龄设置user.setAge(30);System.out.println("年龄:" + user.getAge()); // 输出:年龄:30// 测试密码设置(合法)user.setPassword("newpass123");System.out.println("修改密码后(脱敏):" + user.getPasswordMasked()); // 输出:ne***// 测试登录boolean loginSuccess = user.login("newpass123");System.out.println("登录成功?" + loginSuccess); // 输出:true// 测试非法年龄(会抛出异常)try {user.setAge(200);} catch (IllegalArgumentException e) {System.out.println("年龄设置错误:" + e.getMessage()); // 输出:年龄必须在0-150之间}}
}

7.3 防止类扩展和方法覆盖

final关键字用于限制类、方法或变量的修改,实现 "不可变" 特性。

7.3.1 final 修饰类

final修饰的类不能被继承(最终类),确保类的功能不被修改。

典型应用

  • JDK 中的StringInteger等类都是final
  • 工具类通常设计为final(如java.util.Math

代码示例:final 类

// final类:不能被继承
final class FinalClass {public void show() {System.out.println("这是final类的方法");}
}// 尝试继承final类(编译报错)
// class SubClass extends FinalClass { // 错误:无法从最终类FinalClass继承
//     @Override
//     public void show() {
//         System.out.println("尝试覆盖final类的方法");
//     }
// }// 测试类
public class FinalClassDemo {public static void main(String[] args) {FinalClass obj = new FinalClass();obj.show(); // 输出:这是final类的方法}
}

7.3.2 final 修饰方法

final修饰的方法不能被子类覆盖,但类可以被继承。

应用场景

  • 确保核心方法的实现不被修改
  • 提升性能(JVM 可能对 final 方法进行优化)

代码示例:final 方法

// 父类:包含final方法
class ParentWithFinalMethod {// final方法:不能被覆盖public final void finalMethod() {System.out.println("这是final方法,不能被覆盖");}// 普通方法:可以被覆盖public void normalMethod() {System.out.println("这是普通方法,可以被覆盖");}
}// 子类
class ChildWithFinalMethod extends ParentWithFinalMethod {// 尝试覆盖final方法(编译报错)// @Override// public void finalMethod() { // 错误:final方法不能被覆盖//     System.out.println("尝试覆盖final方法");// }// 覆盖普通方法(合法)@Overridepublic void normalMethod() {System.out.println("子类覆盖了普通方法");}
}// 测试类
public class FinalMethodDemo {public static void main(String[] args) {ChildWithFinalMethod child = new ChildWithFinalMethod();child.finalMethod();   // 输出:这是final方法,不能被覆盖child.normalMethod();  // 输出:子类覆盖了普通方法}
}

7.3.3 final 修饰变量

final修饰的变量只能被赋值一次(常量),赋值后不可修改。

特性

  • 局部变量:声明时或构造器中赋值
  • 成员变量:声明时、构造块或构造器中赋值(必须保证创建对象时已初始化)
  • 引用类型变量:引用地址不可变,但对象内容可修改

代码示例:final 变量

public class FinalVariableDemo {// 成员常量:声明时赋值public static final double PI = 3.14159; // 静态常量(通常全大写)private final String name; // 实例常量// 构造块中初始化final变量(可选){// name = "默认名称"; // 若此处赋值,构造器中不可再赋值}// 构造器中初始化final变量public FinalVariableDemo(String name) {this.name = name; // 必须赋值,否则编译报错}public void showFinalVars() {// 局部final变量final int MAX_COUNT;MAX_COUNT = 100; // 第一次赋值(合法)// MAX_COUNT = 200; // 错误:final变量不能重复赋值System.out.println("PI:" + PI);System.out.println("name:" + name);System.out.println("MAX_COUNT:" + MAX_COUNT);}public void modifyFinalObject() {// final引用类型变量final StringBuilder sb = new StringBuilder("final引用");sb.append(",但内容可修改"); // 合法:对象内容可改System.out.println(sb.toString()); // 输出:final引用,但内容可修改// sb = new StringBuilder("新对象"); // 错误:引用地址不可改}public static void main(String[] args) {FinalVariableDemo demo = new FinalVariableDemo("测试");demo.showFinalVars();demo.modifyFinalObject();}
}

7.4 抽象类

        抽象类(abstract class)是包含抽象方法的类,它不能被实例化只能作为父类被继承。抽象类用于定义通用模板,强制子类实现特定方法。

核心特性

  • abstract关键字修饰
  • 可包含抽象方法(无实现的方法)和具体方法(有实现)
  • 子类必须实现所有抽象方法,否则子类也必须是抽象类
  • 不能用final修饰(抽象类必须能被继承)

类图:抽象类与子类关系

@startuml
abstract class Shape {+ abstract double calculateArea()+ abstract double calculatePerimeter()+ void printInfo()
}class Circle {- double radius+ Circle(double radius)+ double calculateArea()+ double calculatePerimeter()
}class Rectangle {- double length- double width+ Rectangle(double length, double width)+ double calculateArea()+ double calculatePerimeter()
}Shape <|-- Circle
Shape <|-- Rectangle
@enduml

代码示例:抽象类应用

// 抽象类:形状(定义通用模板)
abstract class Shape {// 抽象方法:计算面积(无实现,由子类实现)public abstract double calculateArea();// 抽象方法:计算周长public abstract double calculatePerimeter();// 具体方法:打印形状信息(已有实现)public void printInfo() {System.out.println("面积:" + calculateArea() + ",周长:" + calculatePerimeter());}
}// 子类:圆形(必须实现所有抽象方法)
class Circle extends Shape {private double radius; // 半径public Circle(double radius) {this.radius = radius;}// 实现抽象方法:计算面积@Overridepublic double calculateArea() {return Math.PI * radius * radius;}// 实现抽象方法:计算周长@Overridepublic double calculatePerimeter() {return 2 * Math.PI * radius;}
}// 子类:矩形
class Rectangle extends Shape {private double length; // 长private double width;  // 宽public Rectangle(double length, double width) {this.length = length;this.width = width;}@Overridepublic double calculateArea() {return length * width;}@Overridepublic double calculatePerimeter() {return 2 * (length + width);}
}// 测试类
public class AbstractClassDemo {public static void main(String[] args) {// 抽象类不能实例化// Shape shape = new Shape(); // 错误:Shape是抽象的,无法实例化// 创建子类对象,用父类引用接收Shape circle = new Circle(5);System.out.println("圆形信息:");circle.printInfo(); // 调用抽象类的具体方法,实际执行子类实现Shape rectangle = new Rectangle(4, 6);System.out.println("\n矩形信息:");rectangle.printInfo();}
}

运行结果

7.5 对象转换与多态

        对象转换和多态是实现灵活编程的关键,它们允许我们用统一的方式处理不同类型的对象

7.5.1 对象转换

对象转换分为向上转型向下转型

  • 向上转型(自动转换):子类对象 → 父类引用(Parent p = new Child();
  • 向下转型(强制转换):父类引用 → 子类对象(Child c = (Child)p;,需确保安全性)

流程图:对象转换流程

代码示例:对象转换

// 父类:Animal
class Animal {public void eat() {System.out.println("动物吃东西");}
}// 子类:Dog
class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃骨头");}// 子类特有方法public void bark() {System.out.println("狗汪汪叫");}
}// 子类:Cat
class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");}// 子类特有方法public void meow() {System.out.println("猫喵喵叫");}
}// 测试类
public class ObjectCastDemo {public static void main(String[] args) {// 1. 向上转型(自动)Animal animal = new Dog(); // animal引用指向Dog对象animal.eat(); // 输出:狗吃骨头(多态)// 2. 尝试直接调用子类特有方法(编译报错)// animal.bark(); // 错误:Animal类没有bark()方法// 3. 向下转型(强制)if (animal instanceof Dog) { // 先判断类型(安全)Dog dog = (Dog) animal; // 强制转换dog.bark(); // 输出:狗汪汪叫(调用子类特有方法)}// 4. 错误的向下转型(运行时异常)Animal animal2 = new Cat();// Dog dog2 = (Dog) animal2; // 编译通过,但运行时抛出ClassCastException}
}

7.5.2 instanceof 运算符

instanceof用于判断对象的实际类型返回boolean,语法:对象 instanceof 类型

用途

  • 向下转型前检查类型,避免ClassCastException
  • 判断对象是否属于某个类或其子类

代码示例:instanceof 使用

// 复用上面的Animal、Dog、Cat类public class InstanceOfDemo {public static void main(String[] args) {Animal animal = new Dog();// 判断animal是否是Dog类型System.out.println(animal instanceof Dog); // true// 判断animal是否是Animal类型(父类)System.out.println(animal instanceof Animal); // true// 判断animal是否是Cat类型System.out.println(animal instanceof Cat); // false// 空对象的instanceof判断Animal nullAnimal = null;System.out.println(nullAnimal instanceof Animal); // false(空对象返回false)// 安全的向下转型if (animal instanceof Dog) {Dog dog = (Dog) animal;dog.bark(); // 安全调用}}
}

7.5.3 多态与动态绑定

        多态(Polymorphism)指同一操作作用于不同对象,产生不同结果。表现为:父类引用指向子类对象,调用方法时实际执行子类的实现。

        动态绑定(Dynamic Binding):程序运行时,JVM 根据对象的实际类型确定调用哪个方法的过程(而非引用类型)。

多态的条件

  1. 存在继承关系
  2. 子类覆盖父类方法
  3. 父类引用指向子类对象

代码示例:多态与动态绑定

// 父类:Shape
class Shape {public void draw() {System.out.println("绘制形状");}
}// 子类:Circle
class Circle extends Shape {@Overridepublic void draw() {System.out.println("绘制圆形");}
}// 子类:Rectangle
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("绘制矩形");}
}// 工具类:绘图工具(多态应用)
class DrawingTool {// 接收父类引用,实现通用绘图方法public static void drawShape(Shape shape) {shape.draw(); // 动态绑定:调用实际类型的draw()}
}// 测试类
public class PolymorphismDemo {public static void main(String[] args) {// 父类引用指向不同子类对象Shape circle = new Circle();Shape rectangle = new Rectangle();// 多态:同一方法调用,不同结果DrawingTool.drawShape(circle);    // 输出:绘制圆形DrawingTool.drawShape(rectangle); // 输出:绘制矩形}
}

运行结果

7.5 综合案例:多态计算器

需求:设计一个计算器,支持整数、小数和字符串拼接的加法运算,用多态实现统一调用接口。

完整代码

// 抽象父类:计算器操作
abstract class Calculator {// 抽象方法:计算(子类实现不同类型的计算)public abstract Object calculate(Object a, Object b);
}// 子类:整数计算器
class IntegerCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 类型检查if (!(a instanceof Integer) || !(b instanceof Integer)) {throw new IllegalArgumentException("整数计算器只支持Integer类型");}// 强转并计算int num1 = (Integer) a;int num2 = (Integer) b;return num1 + num2;}
}// 子类:小数计算器
class DoubleCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 支持Integer和Double混合计算double num1 = (a instanceof Integer) ? (Integer) a : (Double) a;double num2 = (b instanceof Integer) ? (Integer) b : (Double) b;return num1 + num2;}
}// 子类:字符串计算器(拼接)
class StringCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 任何类型都转为字符串拼接return a.toString() + b.toString();}
}// 测试类
public class PolymorphismCalculatorDemo {public static void main(String[] args) {// 创建不同计算器(多态数组)Calculator[] calculators = {new IntegerCalculator(),new DoubleCalculator(),new StringCalculator()};// 测试整数计算System.out.println("整数计算:10 + 20 = " + calculators[0].calculate(10, 20));// 测试小数计算System.out.println("小数计算:3.5 + 4.8 = " + calculators[1].calculate(3.5, 4.8));System.out.println("混和计算:5 + 3.2 = " + calculators[1].calculate(5, 3.2));// 测试字符串拼接System.out.println("字符串拼接:\"Hello\" + \"World\" = " + calculators[2].calculate("Hello", "World"));System.out.println("对象拼接:123 + true = " + calculators[2].calculate(123, true));}
}

运行结果

7.6 小结

本章重点讲解了 Java 的继承与多态特性,核心知识点包括:

  1. 继承:通过extends实现代码复用,子类继承父类的属性和方法,可通过super访问父类资源
  2. 方法覆盖:子类重写父类方法,需遵循方法签名、权限等规则,用@Override注解标识
  3. 封装与访问修饰符:通过public/protected/default/private控制访问权限,实现数据安全
  4. final 关键字:用于修饰类(不可继承)、方法(不可覆盖)、变量(常量)
  5. 抽象类:含抽象方法的类,强制子类实现特定方法,作为通用模板使用
  6. 对象转换:向上转型(自动)和向下转型(强制),instanceof用于类型检查
  7. 多态与动态绑定:父类引用指向子类对象,运行时调用实际类型的方法,实现灵活编程

编程练习

练习 1:继承与方法覆盖

        需求:设计Person类作为父类,包含nameage属性及introduce()方法;Student类继承Person,新增studentId属性,并重写introduce()方法;Teacher类继承Person,新增subject属性,并重写introduce()方法。

参考答案

// Person类
class Person {protected String name;protected int age;public Person(String name, int age) {this.name = name;this.age = age;}public void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "岁");}
}// Student类
class Student extends Person {private String studentId;public Student(String name, int age, String studentId) {super(name, age);this.studentId = studentId;}@Overridepublic void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "岁,学号是" + studentId);}
}// Teacher类
class Teacher extends Person {private String subject;public Teacher(String name, int age, String subject) {super(name, age);this.subject = subject;}@Overridepublic void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "岁,教" + subject);}
}// 测试类
public class PersonDemo {public static void main(String[] args) {Person student = new Student("小明", 18, "2023001");Person teacher = new Teacher("李老师", 35, "数学");student.introduce(); // 输出:大家好,我叫小明,今年18岁,学号是2023001teacher.introduce(); // 输出:大家好,我叫李老师,今年35岁,教数学}
}

练习 2:多态应用

        需求:设计抽象类Vehicle,包含抽象方法run()CarBicycleTrain类继承Vehicle并实现run();创建工具类TrafficTool,用多态方法start(Vehicle vehicle)调用不同交通工具的运行方法。

参考答案

// 抽象类:交通工具
abstract class Vehicle {public abstract void run();
}// 汽车
class Car extends Vehicle {@Overridepublic void run() {System.out.println("汽车在公路上行驶");}
}// 自行车
class Bicycle extends Vehicle {@Overridepublic void run() {System.out.println("自行车在自行车道骑行");}
}// 火车
class Train extends Vehicle {@Overridepublic void run() {System.out.println("火车在铁轨上行驶");}
}// 工具类
class TrafficTool {public static void start(Vehicle vehicle) {System.out.print("交通工具启动:");vehicle.run(); // 多态调用}
}// 测试类
public class VehicleDemo {public static void main(String[] args) {Vehicle car = new Car();Vehicle bicycle = new Bicycle();Vehicle train = new Train();TrafficTool.start(car);     // 输出:交通工具启动:汽车在公路上行驶TrafficTool.start(bicycle); // 输出:交通工具启动:自行车在自行车道骑行TrafficTool.start(train);   // 输出:交通工具启动:火车在铁轨上行驶}
}

练习 3:抽象类与 final 综合应用

        需求:设计抽象类BankAccount(银行账户),包含抽象方法calculateInterest()(计算利息);SavingsAccount(储蓄账户)和CurrentAccount(活期账户)继承并实现利息计算;用final修饰BankAccountaccountNumber属性(账号不可修改)。

参考答案

// 抽象类:银行账户
abstract class BankAccount {// 账号:final修饰,不可修改private final String accountNumber;protected double balance; // 余额public BankAccount(String accountNumber, double balance) {this.accountNumber = accountNumber;this.balance = balance;}// 抽象方法:计算利息public abstract double calculateInterest();// 获取账号(只提供getter,无setter)public String getAccountNumber() {return accountNumber;}// 存款public void deposit(double amount) {if (amount > 0) {balance += amount;System.out.println("存款" + amount + "元,当前余额:" + balance);}}// 显示账户信息public void showAccountInfo() {System.out.println("账号:" + accountNumber + ",余额:" + balance + "元,利息:" + calculateInterest() + "元");}
}// 储蓄账户(利息3%)
class SavingsAccount extends BankAccount {public SavingsAccount(String accountNumber, double balance) {super(accountNumber, balance);}@Overridepublic double calculateInterest() {return balance * 0.03; // 年利率3%}
}// 活期账户(利息0.3%)
class CurrentAccount extends BankAccount {public CurrentAccount(String accountNumber, double balance) {super(accountNumber, balance);}@Overridepublic double calculateInterest() {return balance * 0.003; // 年利率0.3%}
}// 测试类
public class BankAccountDemo {public static void main(String[] args) {BankAccount savings = new SavingsAccount("SA2023001", 10000);BankAccount current = new CurrentAccount("CA2023001", 5000);savings.deposit(2000);current.deposit(1000);savings.showAccountInfo(); // 输出:账号:SA2023001,余额:12000.0元,利息:360.0元current.showAccountInfo(); // 输出:账号:CA2023001,余额:6000.0元,利息:18.0元}
}

        希望通过本章内容,你能彻底掌握 Java 继承与多态的核心概念和实践技巧。如果有任何问题或代码运行问题,欢迎在评论区交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90836.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90836.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90836.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ksql连接数据库免输入密码交互

ksql连接数据库免输入密码交互1. 使用 .pgpass 文件&#xff08;推荐&#xff09;2. 使用环境变量3. 使用连接字符串4. 修改 ksql 的别名&#xff08;简化命令&#xff09;5. 注意事项6. 密码含特殊字符转义在 Kingbase 中使用 ksql 连接数据库时&#xff0c;如果希望避免每次手…

UniApp H5 适配 PC 端新姿势:打造跨设备一致体验

在移动互联网蓬勃发展的今天&#xff0c;很多开发者会选择使用 UniApp 来开发多端应用&#xff0c;尤其是 H5 端&#xff0c;因其无需下载安装即可访问的特性&#xff0c;深受用户喜爱。但 UniApp H5 应用在 PC 端直接打开时&#xff0c;往往会因为屏幕尺寸的巨大差异&#xff…

【MySQL#4】函数 复合查询 内外连接

&#x1f4c3;个人主页&#xff1a;island1314 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f49e; &#x1f49e; &#x1f49e; 生活总是不会一帆风顺&#xff0c;前进的道路也不会永远一马平川&#xff0c;如何面…

C++常见面试题之一

一、语言基础与内存管理const与constexpr的区别&#xff1f;应用场景&#xff1f; const&#xff1a;运行时常量&#xff0c;修饰变量/函数不可修改。constexpr&#xff1a;编译期常量&#xff08;C11&#xff09;&#xff0c;用于优化计算&#xff08;如数组大小&#xff09;。…

Golang 语言中的指针介绍

介绍&#xff1a;指针式一种数据类型&#xff0c;用来存储值的内存地址&#xff0c;为了便于理解&#xff0c;我们也可以把指针未内存地址&#xff0c;指针类型只占用内存4个或 8 个字节&#xff0c;在Golang 语言中&#xff0c;类型名称加 * 表示改类型的指针类型。指针类型变…

Flink 状态管理设计详解:StateBackend、State、RocksDB和Namespace

为什么需要 StateBackend&#xff1f;—— 职责分离原则我们可以用一个银行的例子来类比&#xff1a;State (如 ValueState, ListState) 就像是你的银行卡。AbstractKeyedStateBackend 就像是银行的整个后台系统&#xff08;包括总服务器、数据库、风控系统、会计系统等&#x…

橱柜铰链的革命:炬森精密如何以创新科技重塑家居体验

在现代化家居设计中&#xff0c;橱柜不仅是存储空间的核心&#xff0c;更是生活品质的象征。而作为橱柜的“关节”&#xff0c;橱柜铰链的性能直接影响着日常使用的便捷性、安全性和耐久性。然而&#xff0c;许多消费者在橱柜使用中常遭遇噪音干扰、频繁松动或早期损坏等痛点&a…

医疗系统国产化实录:SQL Server国产替代,乙方保命指南

医疗行业的SQL Server替代&#xff0c;和普通业务系统完全不是一个量级——医嘱逻辑嵌套几十层存储过程、收费结算小数点错位能引发医患纠纷、电子病历查询慢一秒医生直接拍桌子。作为被按在手术台上的乙方PM&#xff0c;实测四款主流国产库后&#xff0c;掏心窝子说句实话&…

WAIC 2025 盛大启幕,深思考邀您解锁端侧 AI 新可能​!

2025 世界人工智能大会今日正式启幕&#xff0c;深思考人工智能&#xff08;iDeepWise&#xff09;携端侧多模态大模型技术成果登陆展会&#xff0c;与行业伙伴共探端侧 AI 发展新机遇。作为专注于类端侧多模态大模型领域的创新力量&#xff0c;深思考此次重点展示了 iDeepWise…

MySQL相关概念和易错知识点(2)(表结构的操作、数据类型、约束)

目录1.表结构的操作&#xff08;1&#xff09;增加表&#xff08;2&#xff09;查看库中所有的表&#xff08;3&#xff09;查看表每个列的约束&#xff08;4&#xff09;删除整张表&#xff08;5&#xff09;删除某个具体的列&#xff08;6&#xff09;增加某个具体的列&#…

1. Qt多线程开发

目录方法1.继承QThread使用案例总结方法2.将qobject对象moveToThread&#xff08;官方推荐&#xff09;使用案例总结方法3.QRunnable QThreadPool使用案例总结方法4.快速线程QtConcurrentQFutureWatcher使用案例总结代码下载方法1.继承QThread 需要实现QThread的抽象函数run …

ARM入门学习方法分享

首先认识什么是ARM?ARM公司简介ARM是Advanced RISC Machines的缩写&#xff0c;它是一家微处理器行业的知名企业&#xff0c;该企业设计了大量高性能、廉价、耗能低的RISC &#xff08;精简指令集&#xff09;处理器。 1985年第一个ARM原型在英国剑桥诞生。公司的特点是只设计…

基于springboot的在线数码商城/在线电子产品商品销售系统的设计与实现

用户&#xff1a;数码产品&#xff0c;限时秒杀&#xff0c;种草分享&#xff0c;新品资讯&#xff0c;留言板&#xff0c;订单管理&#xff0c;在线客服&#xff0c;购物车&#xff0c;个人中心管理员&#xff1a;个人中心&#xff0c;用户管理&#xff0c;数码分类管理&#…

Zookeeper学习专栏(十):核心流程剖析之服务启动、请求处理与选举协议

文章目录前言一、服务端启动流程1.1 启动入口类&#xff1a;QuorumPeerMain1.2 集群模式启动核心&#xff1a;runFromConfig1.3 QuorumPeer线程核心逻辑&#xff1a;run()1.4 关键子流程&#xff1a;数据恢复1.5 关键设计要点二、请求处理链&#xff08;责任链模式&#xff09;…

网络基础19--OSPF路由业务多区域

一、OSPF多区域必要性单区域问题&#xff1a;LSDB庞大 → 内存占用高&#xff0c;SPF计算开销大LSA洪泛范围广 → 拓扑变化影响全域无法路由汇总 → 路由表膨胀&#xff0c;查找效率低2. 多区域优势&#xff1a;1. 划分区域&#xff1a;独立LSDB&#xff0c;缩小数据库规模2. 限…

MFC扩展库BCGControlBar Pro v36.2新版亮点:图形管理器等全新升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v36.2已全新发布了&#xff0c;在这个版本中添加了一个新的扩展器控件、改进了网格和报表…

QT开发---网络编程上

Qt Network 模块Qt Network 模块提供了丰富的类用于实现各种网络通信功能&#xff0c;涵盖 TCP、UDP、HTTP、FTP 等多种协议。 Qt 网络类均为异步操作&#xff0c;通过信号槽处理结果&#xff0c;避免阻塞 UI 线程。在使用QT进行网络编程之前&#xff0c;就必须在 CMakeLists.t…

[spring6: Mvc-函数式编程]-源码解析

接口 ServerRequest public interface ServerRequest {HttpMethod method();URI uri();UriBuilder uriBuilder();default String path() {return requestPath().pathWithinApplication().value();}default RequestPath requestPath() {return ServletRequestPathUtils.getPar…

Linux DNS 服务器正反向解析

一、环境说明与准备工作 1.基础信息 本次实验用两台 Linux 主机&#xff0c;分别作为 DNS 服务端和客户端&#xff0c;具体信息如下&#xff1a;服务端IP客户端IP网址192.168.120.130192.168.120.128www.zy.com2.准备工作 关闭安全软件&#xff1a;服务端和客户端都要关闭防火墙…

历史数据分析——中证旅游

中证旅游板块走势从月线级别来看2015年5月到2024年9月&#xff0c;月线上走出了一个震荡中枢的月线级别下跌段&#xff1b;目前月线级别底部放巨量&#xff0c;总体还在底部震荡&#xff0c;后续上涨的概率较大。从周线级别来看从2022年12月到2024年9月整体是下跌走势&#xff…