六、面向对象编程(2)
继承是面向对象编程的三大特性之一(封装、继承、多态),它允许我们基于现有类创建新类,实现代码复用和层次化设计。本文将从六个核心方面系统解析Java继承机制。
一、继承快速入门
1.1 基本语法
class ParentClass {
// 父类成员
}
class ChildClass extends ParentClass {
// 子类特有成员
}
1.2 入门示例
class Animal {
void eat() {
System.out.println("Eating...");
}
}
class Dog extends Animal {
void bark() {
System.out.println("Barking...");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.eat(); // 继承自Animal
myDog.bark(); // 自有方法
}
}
二、继承的优势
- 代码复用:子类自动获得父类非私有成员
- 扩展性:在不修改父类的情况下扩展功能
- 多态基础:为方法重写和方法重载提供支持
- 层次结构:更符合现实世界的分类逻辑
三、权限修饰符详解
修饰符 | 类内部 | 同包 | 不同包子类 | 不同包非子类 |
| ✔ | ✘ | ✘ | ✘ |
| ✔ | ✔ | ✘ | ✘ |
| ✔ | ✔ | ✔ | ✘ |
| ✔ | ✔ | ✔ | ✔ |
继承规则:
- 子类继承父类的
public
和protected
成员 - 默认访问权限成员只能在同包继承
private
成员不可继承但可通过公有方法访问
典型继承场景:
class Parent {
private String secret; // 仅本类可见
String familyName; // 同包可见
protected String heirloom; // 子类可见
public String motto; // 全局可见
}
class Child extends Parent {
void showInherited() {
// System.out.println(secret); // 编译错误
System.out.println(familyName); // 同包可访问
System.out.println(heirloom); // 子类可访问
System.out.println(motto); // 始终可访问
}
}
四、方法重写(Override)
4.1 概念
当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
注意:重写后,方法的访问遵循就近原则。
4.2 核心规则
1.重写的方法上面,可以加一个注解@Override,用于标注这个方法是复写的父类方法
2.子类复写父类方法时,访问权限必须大于或者等于父类方法的权限
public > protected > 缺省
3. 重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
4. 私有方法、静态方法不能被重写,如果重写会报错。
4.3 示例代码
- 写一个A类作为父类,定义两个方法print1和print2
public class A {
public void print1(){
System.out.println("111");
}
public void print2(int a, int b){
System.out.println("111111");
}
}
- 再写一个B类作为A类的子类,重写print1和print2方法。
public class B extends A{
// 方法重写
@Override // 安全,可读性好
public void print1(){
System.out.println("666");
}
// 方法重写
@Override
public void print2(int a, int b){
System.out.println("666666");
}
}
接下来,在测试类中创建B类对象,调用方法
public class Test {
public static void main(String[] args) {
// 目标:认识方法重写,掌握方法重写的常见应用场景。
B b = new B();
b.print1();
b.print2(2, 3);
}
}
执行代码,我们发现真正执行的是B类中的print1和print2方法
五、子类成员访问特点
5.1 访问优先级
- 子类成员变量 > 父类成员变量
- 原则:在子类中访问其他成员(成员变量、成员方法),是依据就近原则的
定义一个父类,代码如下
public class F {
String name = "父类名字";
public void print1(){
System.out.println("==父类的print1方法执行==");
}
}
再定义一个子类,代码如下。有一个同名的name成员变量,有一个同名的print1成员方法;
public class Z extends F {
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部名称
}
@Override
public void print1(){
System.out.println("==子类的print1方法执行了=");
}
public void showMethod(){
print1(); // 子类的
}
}
接下来写一个测试类,观察运行结果,我们发现都是调用的子类变量、子类方法。
public class Test {
public static void main(String[] args) {
// 目标:掌握子类中访问其他成员的特点:就近原则。
Z z = new Z();
z.showName();
z.showMethod();
}
}
- 如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分。
public class Z extends F {
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部名称
System.out.println(this.name); // 子类成员变量
System.out.println(super.name); // 父类的成员变量
}
@Override
public void print1(){
System.out.println("==子类的print1方法执行了=");
}
public void showMethod(){
print1(); // 子类的
super.print1(); // 父类的
}
}
六、构造器访问特点
6.1 构造器调用规则
- 子类构造器必须调用父类构造器
- 默认调用父类无参构造器
super()
- 显式调用必须放在构造器首行
6.2 示例分析
class Person {
private String name;
public Person(String name) {
this.name = name;
System.out.println("Person 构造器执行");
}
}
class Student extends Person {
private int grade;
public Student(String name, int grade) {
super(name); // 必须首行调用
this.grade = grade;
System.out.println("Student 构造器执行");
}
}
// 测试输出:
Student s = new Student("Alice", 10);
// 输出顺序:
// Person构造器执行
// Student构造器执行
最佳实践与注意事项
- 遵循
is-a
关系进行继承设计 - 谨慎使用继承层级(建议不超过3层)
- 优先使用组合替代深层继承
- 注意构造器调用链的执行顺序
- 使用
@Override
注解增强代码可读性