一、继承的基本概念
继承是一种由已有类创建新类的机制。新类(子类、派生类)能继承已有类(父类、基类)的属性和方法,并且可以添加自己独有的属性和方法。在 Java 中,继承通过 extends 关键字来实现。
二、继承的语法
class 父类 {
}
class 子类 extends 父类 {
}
三、继承的示例
java
// 父类
class Shape {
String color;
void setColor(String color) {
this.color = color;
}
String getColor() {
return color;
}
}
// 子类
class Rectangle extends Shape {
// 不要重复color属性及方法,
// 只需要定义新增的width和height属性及方法:
double width;
double height;
double calculateArea() {
return width * height;
}
}
四、继承的特点
**1. 单继承:**Java 只支持单继承,不支持多继承,但支持多层继承。
**2. 传递性:**若类 B 继承自类 A,类 C 继承自类 B,那么类 C 也会继承类 A 的属性和方法。
**3. 默认继承 Object 类:**Java 中所有的类都直接或者间接的继承于 Object 类。如果一个类没有明确继承其他类,那么它会默认继承 Object 类。

五、方法重写
方法重写是指子类重新定义父类中已有的方法。当子类需要修改父类方法的行为时,可以使用方法重写。
(一)方法重写的规则
**1. 方法签名必须一致:**方法名、参数列表和返回类型必须与父类方法完全相同。Java 5 及以上版本允许返回类型可以是父类方法返回类型的子类。
**2. 访问权限不能更严格:**子类方法的访问权限必须与父类一致或更宽松。
**3. 不能抛出更广泛的异常:**子类方法不能抛出比父类方法更广泛的受检异常。
**4. private、final 和 static 方法不能被重写:**private 方法在类内部可见,不能被其他类访问;final 关键字用于防止方法被修改;而 static 方法属于类本身,不能被重写。当在子类中定义一个与父类同名的 static 方法时,实际上是在子类中定义了一个新的静态方法,而不是重写了父类的静态方法。
(二)方法重写的注意事项
建议在重写方法上添加 @Override 注解,编译器会检查方法是否正确重写,避免拼写错误或参数不匹配。
(三)方法重写实例
java
class Shape {
void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a circle");
}
}
class Square extends Shape {
@Override
void draw() {
System.out.println("Drawing a square");
}
}
public class PolymorphismExample {
public static void main(String[] args) {
Shape shape1 = new Circle();
Shape shape2 = new Square();
shape1.draw(); // 输出 "Drawing a circle"
shape2.draw(); // 输出 "Drawing a square"
}
}
六、构造方法
构造方法用于对象的创建与初始化,方法名与类名相同且无返回类型。
根据 Java 的继承机制,子类的构造方法必须先调用父类的构造方法,再执行自己。这是因为子类需要继承父类的属性,而父类的构造方法负责初始化这些属性。
如果子类没有显式调用父类的构造方法,编译器会自动隐式调用父类的无参构造方法,即 super() 。在这种情况下,父类必须存在无参构造方法,否则编译会报错。
如果父类只定义了有参构造方法,子类必须显式调用父类的有参构造方法,即super(参数)。否则编译会报错。
例1:父类存在无参构造方法
java
class Person {
protected String name;
protected int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
this.score = score; // 先执行super(),再执行这句代码
}
}
例2:父类只定义了有参构造方法
java
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name, age); // 父类只定义了有参构造方法,子类必须显式调用父类的有参构造方法
this.score = score;
}
}
七、this 和 super 关键字
this 指向当前对象本身;用于访问本类的属性和方法、调用本类其他构造方法;
super 指向当前对象的父类对象;用于访问父类的属性和方法、调用父类构造方法。
java
class Animal {
void eat() {
System.out.println("吃饭");
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("啃骨头");
}
void eatTest() {
super.eat(); // 调用父类方法
this.eat(); // 调用自己的方法,可以省略this,只写eat()
}
}
八、继承的优缺点
(一)优点
-
实现代码复用,减少代码冗余。
-
便于系统的扩展和维护。
-
实现多态性,增强程序的灵活性。
(二)缺点
-
过度使用继承会导致类之间的耦合度增加,降低代码的可维护性。
-
父类的修改可能会对多个子类产生影响。
九、区分继承和组合
在使用继承时,我们要注意逻辑一致性。
比如,Book 类和 Student 类都有 name 属性,那么,我们能不能让 Student 继承自 Book 呢?
显然,从逻辑上讲,这是不合理的。究其原因,是因为 Student 与 Person 是 is 关系,而 Student 与 Book 是 has 关系。
具有 has 关系不应该使用继承,而是使用组合,即 Student 可以持有一个 Book 实例:
class Student extends Person {
protected Book book;
protected int score;
}
继承是 "is-a" 关系(例如,学生是人),而组合是 "has-a" 关系(例如,学生有书)。在设计类时,要优先考虑组合而非继承,这样可以降低类之间的耦合度。