【Java SE】继承

继承

类继承

类继承的概念

继承的本质 :子类继承父类的所有非私有成员(属性和方法),但能否访问取决于访问权限修饰符。

java 复制代码
class Parent {
    // 父类的成员
}

public class Child extends Parent {
    // 子类的成员
}

子类到底继承了父类的哪些成员?

继承关系图示

复制代码
        ┌─────────────────────────────────────┐
        │             父类 (Parent)            │
        ├─────────────────────────────────────┤
        │  ─────────────────────────────────  │
        │  - private     方法/属性             │  ← 不被继承,子类完全不可见
        │  ~ default     方法/属性 (同包)       │  ← 被继承,同包时可访问
        │  # protected   方法/属性             │  ← 被继承,子类可访问/重写
        │  + public      方法/属性             │  ← 被继承,子类可访问/重写
        │  ─────────────────────────────────  │
        │  【特殊成员】                         │
        │  $ static      方法                  │  ← 被继承,但被隐藏(不是重写)
        │  ◇ final       方法                  │  ← 被继承,但不能重写
        │  ▣ 构造方法      Parent()             │  ← 不被继承,需super()调用
        └────────────────┬────────────────────┘
                         │
                         │ extends
                         ▼
        ┌─────────────────────────────────────┐
        │             子类 (Child)             │
        ├─────────────────────────────────────┤
        │  【继承的成员】                        │
        │  ✓ public/protected 成员             │  ← 可直接使用
        │  ✓ default 成员 (同包时)              │  ← 受包限制
        │  ✓ static 方法 (但被隐藏)              │  ← 建议用类名调用
        │  ✓ final 方法 (不可修改)               │  ← 只能原样使用
        │  ─────────────────────────────────  │
        │  【新增成员】                          │
        │  + 扩展属性                           │
        │  + 新方法                             │
        │  ⤻ 重写的方法 (@Override)             │  ← 改写父类行为
        │  $ static 隐藏方法                    │  ← 隐藏父类static方法
        └─────────────────────────────────────┘

        ═════════════ 构造顺序 ═════════════
        
           ① 父类静态块/属性初始化
                      ↓
           ② 子类静态块/属性初始化
                      ↓
           ③ 父类实例属性初始化
                      ↓
           ④ 父类构造方法执行
                      ↓
           ⑤ 子类实例属性初始化
                      ↓
           ⑥ 子类构造方法执行

📌总结一下不会被继承的方法

  1. private 方法
  2. 跨包时的包级私有方法(默认访问权限)
  3. 构造方法

final 方法和 static 方法实际上是被继承的,只是在重写方面有限制。

private 方法

java 复制代码
public class Parent {
    private void privateMethod() {
        System.out.println("父类私有方法");
    }
}

public class Child extends Parent {
    // 无法继承 privateMethod,完全不可见
    public void test() {
        // privateMethod(); // 编译错误
    }
}

final 方法(会被继承但不能重写)

严格来说,final 方法是会被继承的,但不能被子类重写(override):

java 复制代码
public class Parent {
    public final void finalMethod() {
        System.out.println("final方法");
    }
}

public class Child extends Parent {
    // 可以调用 finalMethod()
    // 但不能重写:
    // public void finalMethod() { } // 编译错误
}

构造方法

构造方法不会被继承,子类必须通过 super() 调用父类构造方法:

java 复制代码
public class Parent {
    public Parent(String name) {
        // 有参构造
    }
}

public class Child extends Parent {
    public Child(String name) {
        super(name); // 必须显式调用
    }
}

包级私有方法(跨包时)

如果父类和子类不在同一个包中,父类的默认访问权限(无修饰符)方法不会被继承:

java 复制代码
// 包 com.parent
package com.parent;
public class Parent {
    void packagePrivateMethod() { } // 默认访问权限
}

// 包 com.child
package com.child;
import com.parent.Parent;
public class Child extends Parent {
    // 无法访问 packagePrivateMethod()
}

关于 static 方法的补充说明

  • static 方法确实会被继承,子类可以通过类名或实例调用
  • static 方法不支持重写(override) ,只支持隐藏(hide)
  • 如果子类定义了相同的 static 方法,父类的方法被隐藏,调用取决于引用类型
java 复制代码
Parent p = new Child();
p.staticMethod(); // 调用 Parent 的 static 方法,不是 Child 的

访问权限与其他修饰符的组合效果表

Java 提供了四种访问权限修饰符,控制成员的可见范围:

访问权限 本类 同包 子类(不同包) 其他类
private
default
protected
public

访问权限 + static

组合 是否被继承 子类访问方式 特殊说明
public static ✅ 是 子类名.成员父类名.成员 完全可见,可被隐藏
protected static ✅ 是 子类名.成员父类名.成员 子类及同包可见
static (default) ⚠️ 有条件 同包:子类名.成员 跨包:❌ 不可访问 包级私有
private static ❌ 否 完全不可访问 仅父类内部可见

访问权限 + final(方法)

组合 是否被继承 能否重写 特殊说明
public final ✅ 是 ❌ 不能 所有子类都只能原样使用
protected final ✅ 是 ❌ 不能 子类及同包可见但不能改
final (default) ⚠️ 有条件 ❌ 不能 同包可继承但不能重写
private final ❌ 否 - 实际等同于private方法

访问权限 + final(属性)

组合 是否被继承 能否修改 特殊说明
public final ✅ 是 ❌ 不能 全局常量
protected final ✅ 是 ❌ 不能 子类可读不可改
final (default) ⚠️ 有条件 ❌ 不能 同包可读不可改
private final ❌ 否 - 仅父类内部使用

访问权限 + abstract

组合 是否被继承 必须重写? 特殊说明
public abstract ✅ 是 ✅ 必须 最常见形式
protected abstract ✅ 是 ✅ 必须 子类必须实现
abstract (default) ⚠️ 有条件 ✅ 必须 同包必须实现
private abstract ❌ 不允许 - 编译错误! 矛盾组合

访问权限 + synchronized

组合 是否被继承 锁对象 特殊说明
public synchronized ✅ 是 实例对象(this) 可被重写,锁行为不变
protected synchronized ✅ 是 实例对象(this) 同上
synchronized (default) ⚠️ 有条件 实例对象(this) 同包可继承
private synchronized ❌ 否 实例对象(this) 仅父类内部

访问权限 + native

组合 是否被继承 能否重写 特殊说明
public native ✅ 是 ✅ 能 可被Java方法重写
protected native ✅ 是 ✅ 能 同上
native (default) ⚠️ 有条件 ✅ 能 同包可继承
private native ❌ 否 - 仅父类内部

访问权限 + strictfp

组合 是否被继承 精度规则 特殊说明
public strictfp ✅ 是 严格浮点 被重写的方法继承精度规则
protected strictfp ✅ 是 严格浮点 同上
strictfp (default) ⚠️ 有条件 严格浮点 同包可继承
private strictfp ❌ 否 严格浮点 仅父类内部

📌 关键规则总结

  1. 访问权限优先private 成员永远不被继承,无论其他修饰符
  2. abstractprivate 冲突:不能组合使用
  3. final 与继承:可继承但不可修改/重写
  4. static 特殊:可继承但表现为"隐藏"而非"重写"
  5. default 访问权限:继承受包限制

构造方法的继承规则

父类的构造方法不会被子类继承 ,但子类必须在自己的构造方法中调用父类的构造方法,确保"先父类后子类"的构造顺序。

根据父类情况决定调用方式

父类构造方法情况 子类调用要求 示例
有无参构造 可以隐式调用,也可以显式调用 Child() { }Child() { super(参数); }
只有有参构造 必须显式调用父类有参构造 Child() { super(参数); }
规则一:隐式调用父类无参构造

如果父类存在无参构造,且子类构造方法中没有显式调用super(...),系统会自动调用父类的无参构造

java 复制代码
class Parent {
    Parent() {  // 父类有无参构造
        System.out.println("父类无参构造");
    }
}

class Child extends Parent {
    Child() {
        // 隐式调用 super()
        System.out.println("子类无参构造");
    }
}

输出结果

复制代码
父类无参构造
子类无参构造
规则二:显式调用父类有参构造

如果父类只定义了有参构造 (没有无参构造),子类构造方法必须显式调用 父类的有参构造,且super(参数)必须放在第一行。

java 复制代码
// 父类:只定义了有参构造
class Parent {
    int a;
    
    Parent(int a) {  // 有参构造
        this.a = a;
        System.out.println("父类有参构造");
    }
    // 此时没有无参构造方法
}

// ❌ 错误写法:编译报错
class Child1 extends Parent {
    Child1() {
        // 隐式调用 super(),但父类没有无参构造
        System.out.println("子类构造");
    }
}

// ✅ 正确写法:显式调用父类有参构造
class Child2 extends Parent {
    Child2(int a) {
        super(a);  // 必须显式调用父类的有参构造
        System.out.println("子类构造");
    }
}

// ✅ 也可以这样:子类无参构造调用本类其他构造,最终调用父类有参构造
class Child3 extends Parent {
    Child3() {
        this(10);  // 调用本类的有参构造
    }
    
    Child3(int a) {
        super(a);  // 最终调用父类有参构造
        System.out.println("子类构造");
    }
}
java 复制代码
// 父类:只定义了无参和有参构造
class Parent {
    Parent() {
        System.out.println("父类无参构造被调用");
    }
    
    Parent(int a) {
        System.out.println("父类有参构造被调用,a = " + a);
    }
}

class Child extends Parent {
    // 情况1:隐式调用父类无参构造
    Child() {
        System.out.println("子类无参构造");
    }
    
    // 情况2:显式调用父类无参构造
    Child(String name) {
        super();  // 显式调用父类无参构造
        System.out.println("子类有参构造:" + name);
    }
    
    // 情况3:也可以调用父类有参构造
    Child(int a) {
        super(a);  // 调用父类有参构造
        System.out.println("子类有参构造,a = " + a);
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println("=== 创建Child() ===");
        Child c1 = new Child();
        
        System.out.println("\n=== 创建Child(\"hello\") ===");
        Child c2 = new Child("hello");
        
        System.out.println("\n=== 创建Child(100) ===");
        Child c3 = new Child(100);
    }
}

三条关键原则

  1. super(参数)必须在第一行

    显式调用父类构造方法的语句必须放在子类构造方法的首行

  2. 必须直接或间接调用父类构造

    子类构造方法执行前,必须完成父类构造方法的调用

  3. 构造顺序不可改变

    永远是:父类构造方法先执行 → 子类构造方法后执行

  4. 一个子类构造方法只能调用一次父类构造方法

  5. 可以选择调用父类的无参构造或有参构造

接口继承

补充:接口继承

笔试面试题

例题1:继承构造方法调用错误

java 复制代码
class Parent {
    String name;

    // 父类只有带参构造方法
    public Parent(String x) {
        name = x;
    }
}

public class SubClass extends Parent {

    public static void main(String[] args) {
        SubClass obj = new SubClass();  // 这里会报编译错误
        System.out.println(obj.name);
    }
}

错误要点总结

  1. 父类 Parent :只定义了带参构造 Parent(String x),没有无参构造
  2. 子类 SubClass :没有定义任何构造方法,编译器自动添加 SubClass() { super(); }
  3. 冲突 :自动添加的 super() 试图调用父类的无参构造,但父类没有
  4. 结果:编译失败
相关推荐
故以往之不谏2 小时前
快慢双指针算法--数组删除目标元素--LeetCode27
开发语言·数据结构·c++·算法·leetcode·学习方法·数组
2301_792674862 小时前
java学习day21
java
DREW_Smile2 小时前
C语言内存函数
c语言·开发语言
不能只会打代码2 小时前
基于Vue 3 + Spring Boot的物联网生鲜品储运系统设计与实现(源码附有详细的文档讲解)
java·前端·vue.js·spring boot·后端·物联网·github
barbyQAQ2 小时前
GitLab CI/CD 基本用法指南
java·ci/cd·gitlab
历程里程碑2 小时前
Linux 38 网络协议:从独立主机到全球互通
java·linux·运维·服务器·网络·c++·职场和发展
任子菲阳2 小时前
学JavaWeb第七天——yml配置文件 & 后端实战Tlias案例
java·开发语言·spring
AI科技星2 小时前
空间光速螺旋动力学:统一质量、引力、电磁与时空本源的公理化理论与全现象验证
c语言·开发语言·opencv·算法·r语言
BUG?不,是彩蛋!2 小时前
AI智慧社区--实现修改密码、退出登录、动态路由
java·spring boot·后端·intellij-idea·mybatis