Appearance
11.2 继承(extends)
继承的概念
继承(Inheritance)是面向对象编程的三大特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法,实现代码复用。
继承的语法
语法:
java
public class 子类名 extends 父类名 {
// 子类的属性和方法
}示例:
java
// 父类
public class Person {
private String name;
private int age;
// 构造方法、getter 和 setter 方法
}
// 子类
public class Student extends Person {
private int studentId;
private double grade;
// 构造方法、getter 和 setter 方法
}继承的特点
- 单继承:Java 支持单继承,一个子类只能继承一个父类
- 多层继承:Java 支持多层继承,一个子类可以继承一个父类,而这个父类又可以继承另一个父类
- 继承的内容:子类继承父类的非私有属性和方法
- 方法重写:子类可以重写父类的方法,实现自己的逻辑
- super 关键字:子类可以使用 super 关键字访问父类的属性和方法
示例:继承的基本使用
示例 1:学生类继承人类
java
// 父类:Person
public class Person {
private String name;
private int age;
// 构造方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter 和 setter 方法
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(name + " is eating.");
}
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
// 子类:Student
public class Student extends Person {
private int studentId;
private double grade;
// 构造方法
public Student() {
}
public Student(String name, int age, int studentId, double grade) {
super(name, age); // 调用父类的构造方法
this.studentId = studentId;
this.grade = grade;
}
// getter 和 setter 方法
public int getStudentId() {
return studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public double getGrade() {
return grade;
}
public void setGrade(double grade) {
this.grade = grade;
}
// 方法
public void study() {
System.out.println(getName() + " is studying.");
}
public boolean isPass() {
return grade >= 60;
}
}
// 测试类
public class StudentExample {
public static void main(String[] args) {
// 创建学生对象
Student student = new Student("John", 18, 1001, 85.5);
// 调用继承自父类的方法
student.eat();
student.sleep();
// 调用子类自己的方法
student.study();
System.out.println(student.getName() + " is pass: " + student.isPass());
// 使用 getter 和 setter 方法
student.setName("Jane");
student.setAge(19);
student.setGrade(92.0);
System.out.println("Name: " + student.getName());
System.out.println("Age: " + student.getAge());
System.out.println("Student ID: " + student.getStudentId());
System.out.println("Grade: " + student.getGrade());
}
}示例 2:多层继承
java
// 父类:Person
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类:Student,继承自 Person
public class Student extends Person {
private int studentId;
public Student(String name, int age, int studentId) {
super(name, age);
this.studentId = studentId;
}
public int getStudentId() {
return studentId;
}
public void study() {
System.out.println(getName() + " is studying.");
}
}
// 子类:GraduateStudent,继承自 Student
public class GraduateStudent extends Student {
private String major;
public GraduateStudent(String name, int age, int studentId, String major) {
super(name, age, studentId);
this.major = major;
}
public String getMajor() {
return major;
}
public void research() {
System.out.println(getName() + " is researching in " + major + ".");
}
}
// 测试类
public class GraduateStudentExample {
public static void main(String[] args) {
// 创建研究生对象
GraduateStudent graduateStudent = new GraduateStudent("John", 22, 2001, "Computer Science");
// 调用继承自 Person 的方法
graduateStudent.eat();
// 调用继承自 Student 的方法
graduateStudent.study();
// 调用自己的方法
graduateStudent.research();
// 使用 getter 方法
System.out.println("Name: " + graduateStudent.getName());
System.out.println("Age: " + graduateStudent.getAge());
System.out.println("Student ID: " + graduateStudent.getStudentId());
System.out.println("Major: " + graduateStudent.getMajor());
}
}示例 3:方法重写
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound.");
}
public void eat() {
System.out.println("Animal eats.");
}
}
// 子类:Dog
public class Dog extends Animal {
// 重写父类的方法
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
// 继承父类的方法,不重写
// public void eat() { ... }
}
// 子类:Cat
public class Cat extends Animal {
// 重写父类的方法
@Override
public void makeSound() {
System.out.println("Cat meows.");
}
// 重写父类的方法
@Override
public void eat() {
System.out.println("Cat eats fish.");
}
}
// 测试类
public class AnimalExample {
public static void main(String[] args) {
// 创建动物对象
Animal animal = new Animal();
animal.makeSound();
animal.eat();
// 创建狗对象
Dog dog = new Dog();
dog.makeSound(); // 调用重写的方法
dog.eat(); // 调用继承的方法
// 创建猫对象
Cat cat = new Cat();
cat.makeSound(); // 调用重写的方法
cat.eat(); // 调用重写的方法
}
}继承的优势
- 代码复用:子类可以继承父类的属性和方法,避免重复代码
- 扩展性:子类可以在父类的基础上扩展功能
- 维护性:修改父类的代码,所有子类都会受益
- 多态:通过继承,可以实现多态
继承的最佳实践
- 合理设计继承层次:避免过深的继承层次,一般不超过 3-4 层
- 遵循里氏替换原则:子类应该能够替换父类,而不影响程序的正确性
- 使用抽象类和接口:对于共同的行为,使用抽象类或接口
- 避免过度继承:只在真正需要继承的情况下使用继承
- 正确使用 super 关键字:在子类中使用 super 关键字访问父类的属性和方法
示例:合理的继承设计
java
// 抽象类:Shape
public abstract class Shape {
public abstract double calculateArea();
public abstract double calculatePerimeter();
}
// 子类:Circle
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
}
// 子类:Rectangle
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return 2 * (width + height);
}
}
// 测试类
public class ShapeExample {
public static void main(String[] args) {
// 创建圆形对象
Shape circle = new Circle(5);
System.out.println("Circle area: " + circle.calculateArea());
System.out.println("Circle perimeter: " + circle.calculatePerimeter());
// 创建矩形对象
Shape rectangle = new Rectangle(4, 6);
System.out.println("Rectangle area: " + rectangle.calculateArea());
System.out.println("Rectangle perimeter: " + rectangle.calculatePerimeter());
}
}常见问题
1. 构造方法的调用
症状:子类构造方法中没有调用父类的构造方法
解决方案:在子类构造方法中使用 super() 调用父类的构造方法
示例:
java
public class Parent {
public Parent() {
System.out.println("Parent constructor");
}
}
public class Child extends Parent {
public Child() {
super(); // 调用父类的构造方法
System.out.println("Child constructor");
}
}2. 方法重写错误
症状:子类重写父类方法时,方法签名不匹配
解决方案:确保子类重写的方法与父类方法的签名完全相同
示例:
java
public class Parent {
public void method(int x) {
}
}
public class Child extends Parent {
// 错误:方法签名不匹配
public void method(double x) {
}
// 正确:方法签名匹配
@Override
public void method(int x) {
}
}3. 访问权限问题
症状:子类无法访问父类的私有属性和方法
解决方案:将父类的属性和方法设置为 protected 或 public,或者提供公共的 getter 和 setter 方法
示例:
java
public class Parent {
private int privateField; // 私有属性,子类无法访问
protected int protectedField; // 受保护属性,子类可以访问
private void privateMethod() { // 私有方法,子类无法访问
}
protected void protectedMethod() { // 受保护方法,子类可以访问
}
}
public class Child extends Parent {
public void accessParentMembers() {
// 错误:无法访问私有属性
// privateField = 10;
// 正确:可以访问受保护属性
protectedField = 10;
// 错误:无法访问私有方法
// privateMethod();
// 正确:可以访问受保护方法
protectedMethod();
}
}4. 继承层次过深
症状:继承层次过深,导致代码难以理解和维护
解决方案:减少继承层次,使用组合代替继承
示例:
java
// 过深的继承层次
public class A {
}
public class B extends A {
}
public class C extends B {
}
public class D extends C {
}
// 更好的设计:使用组合
public class A {
}
public class B {
private A a;
}
public class C {
private B b;
}总结
继承是面向对象编程的三大特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法,实现代码复用。
继承的特点:
- 单继承:一个子类只能继承一个父类
- 多层继承:支持多层继承
- 继承的内容:子类继承父类的非私有属性和方法
- 方法重写:子类可以重写父类的方法
- super 关键字:子类可以使用 super 关键字访问父类的属性和方法
继承的优势:
- 代码复用:避免重复代码
- 扩展性:在父类的基础上扩展功能
- 维护性:修改父类的代码,所有子类都会受益
- 多态:通过继承,可以实现多态
通过合理使用继承,可以使代码更加简洁、可维护和可扩展。
