1.1 为什么需要继承?
继承(Inheritance) 是面向对象编程中的一个核心概念,通过继承,子类(Subclass) 可以获取**父类(Superclass)**的所有属性和方法,避免重复编写相同的代码。这种机制允许我们基于已有的类创建新的类,并对其进行扩展和修改。
继承的好处:
- 代码重用:通过继承,子类可以直接复用父类的代码,减少冗余,避免重复编写。
- 增强扩展性:可以在子类中扩展父类的功能,增加新的特性而无需修改父类。
- 提高维护性:继承使得代码结构更清晰,容易管理和维护。
举个例子:
假设我们有一个 Animal
类,里面有 eat()
和 sleep()
方法。如果我们想创建多个不同种类的动物,比如 Dog
、Cat
,我们可以继承 Animal
类,而不需要重复编写这些基础的行为。
class Animal {
public void eat() {
System.out.println("动物正在吃饭");
}
public void sleep() {
System.out.println("动物正在睡觉");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("狗在叫");
}
}
通过继承,Dog
类就可以直接复用 Animal
类中的 eat()
和 sleep()
方法,这就是继承的好处。
1.2 继承的语法
在 Java 中,继承通过关键字 extends
来实现。
class 子类 extends 父类 {
// 子类的成员和方法
}
class Animal {
public void eat() {
System.out.println("动物正在吃饭");
}
}
class Dog extends Animal { // Dog继承Animal
public void bark() {
System.out.println("狗在叫");
}
}
1.3 父类成员的关键字访问
子类可以直接访问父类的public
和 protected
成员,但无法访问父类的 private
成员。
示例:
class Animal {
public void eat() {
System.out.println("动物正在吃饭");
}
private void breathe() { // 私有方法,子类不能访问
System.out.println("动物在呼吸");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("狗在叫");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 可以访问父类的public方法
dog.bark(); // 访问子类自己的方法
// dog.breathe(); // 错误,无法访问父类的private方法
}
}
访问规则:
public
成员:子类和外部类都可以访问。protected
成员:子类和同包内的类可以访问。private
成员:子类和外部类都无法直接访问。
1.4 子类中访问父类的成员变量
当我们在子类中访问父类的成员变量时,有两种情况:
- 子类和父类不存在同名成员变量:这种情况下,子类可以直接访问父类的成员变量。
- 子类和父类成员变量同名:在这种情况下,子类可以通过
super
关键字访问父类的成员变量,而通过直接访问则会引用子类的同名变量。
1.4.1 子类和父类不存在同名成员变量
当子类和父类的成员变量名字不同时,子类可以直接访问父类的成员变量。
示例:
class Animal {
public String name = "动物";
}
class Dog extends Animal {
public int age = 5;
public void display() {
System.out.println("动物名字: " + name); // 直接访问父类的成员变量
System.out.println("狗的年龄: " + age); // 访问子类的成员变量
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.display();
}
}
结果:
动物名字: 动物
狗的年龄: 5
1.4.2 子类和父类成员变量同名
当子类和父类拥有同名的成员变量时,直接访问该成员变量时,引用的是子类的变量。如果我们想要在子类中访问父类的同名成员变量,可以使用 super
关键字。
示例:
class Animal {
public String name = "动物";
}
class Dog extends Animal {
public String name = "狗"; // 与父类变量同名
public void display() {
System.out.println("子类名字: " + name); // 访问子类的name
System.out.println("父类名字: " + super.name); // 通过super访问父类的name
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.display();
}
}
运行结果:
子类名字: 狗
父类名字: 动物
解释:
- 在
Dog
类中,name
覆盖了父类Animal
中的同名变量。如果直接访问name
,引用的是Dog
类中的name
,而通过super.name
可以访问父类Animal
的name
变量。
1.4.2 子类中访问父类的成员方法
子类可以继承父类的成员方法,同样有两种情况:
- 成员方法名字不同:子类直接访问父类的成员方法。
- 成员方法名字相同:子类可以通过
super
调用父类的同名方法,或重写该方法。
1.4.2.1 成员方法名字不同
当子类和父类的成员方法名字不同时,子类可以直接调用父类的方法,也可以调用自己的方法。
示例:
class Animal {
public void eat() {
System.out.println("动物正在吃东西");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("狗在叫");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 调用父类的方法
dog.bark(); // 调用子类的方法
}
}
运行结果:
动物正在吃东西
狗在叫
解释:
- 在
Dog
类中,eat()
是从父类Animal
继承而来的方法,bark()
是Dog
类自身的方法。子类可以直接调用父类的方法。
1.4.2.2 成员方法名字相同
当子类和父类拥有同名的成员方法时,如果子类重写了该方法,默认调用的是子类的版本。如果我们想要调用父类的同名方法,可以使用 super
关键字。
示例:
class Animal {
public void eat() {
System.out.println("动物正在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗正在吃东西");
}
public void displayEat() {
super.eat(); // 调用父类的eat方法
eat(); // 调用子类的eat方法
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.displayEat();
}
}
运行结果:
动物正在吃东西
狗正在吃东西
解释:
- 在
Dog
类中,eat()
方法重写了父类Animal
中的eat()
方法。因此,默认调用的是子类的eat()
方法。 - 通过
super.eat()
,我们可以在子类中调用父类的eat()
方法,而eat()
调用的则是子类自己重写的版本。