Java继承

1.继承语法

为了表示继承关系,Java提供了一个关键字,也就是extends。

java 复制代码
class 父类 {
    // 父类的成员变量和方法
}

class 子类 extends 父类 {
    // 子类特有的成员变量和方法
}

2.继承------子类对父类成员的访问

重新定义一对父子类,即Base类和Derived类,为方便理解,这里都在同一个java文件实现

2.1 对父类成员变量的访问
1)父类和子类没有同名变量情况:
java 复制代码
//定义父类 Base
class Base {
    int a = 10;
    double b = 3.14;
    char c = 'A';
}

//子类[Derived]继承父类[Base]
class Derived extends Base {
    int d = 20;
    public void method() {
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);
        System.out.println("d = " + d);
    }
}

//程序主方法
public class Main {
    public static void main(String[] args) {
        //创建Derived对象
        Derived derived = new Derived();
        derived.method();
    }
}

运行结果:

结果说明:当子类中没有和父类同名成员变量时,对其进行编译输出,子类访问父类中继承下来的,并且访问子类自身新增加的成员。

2)父类和子类有同名变量情况:
java 复制代码
class Base {
    int a = 10;
    double b = 3.14;
    char c = 'A';
}
 
class Derived extends Base {
    int a = 20;//与父类同名且同类型
    int c = 52;//与父类同名但不同类型
    public void method() {
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);
        //System.out.println("d = " + d);没有d,编译失败
    }
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived = new Derived();//创建Derived对象
        derived.method();
    }
}

运行结果:

结果说明: 当子类中有和父类同名成员变量时,不论是否同类型,对其进行编译输出,子类会优先访问自己的成员。如果父类子类中都没有的成员,则会编译失败。

以上可以得出:成员变量访问遵循就近原则,子类自己有的,就优先访问子类自己,否则再访问父类成员,如果都没有,则会编译失败。

2.2 对父类成员方法的访问
1)父类和子类没有同名方法情况:
java 复制代码
class Base {
    public void baseMethodA() {
        System.out.println("这是父类方法A");
    }
    public void baseMethodB() {
        System.out.println("这是父类方法B");
    }
}
 
class Derived extends Base {
    public void derivedMethod() {
        System.out.println("这是子类方法");
    }
 
    public void method() {
        baseMethodA();//访问父类成员方法A
        baseMethodB();//访问父类成员方法B
        derivedMethod();//访问子类自身成员方法
    }
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived = new Derived();//创建Derived对象
        derived.method();
    }
}

运行结果:

结果说明:当子类中没有和父类同名成员方法时,对其进行编译输出,子类访问父类中继承下来的,并且访问子类自身新增加的成员。

2)父类和子类有同名方法情况:
java 复制代码
class Base {
    public void baseMethodA() {
        System.out.println("这是父类方法A");
    }
    public void baseMethodB() {
        System.out.println("这是父类方法B");
    }
}
 
class Derived extends Base {
    public void baseMethodA() {   //同名
        System.out.println("这是子类方法A........");
    }
 
    public void method() {
        baseMethodA();//访问父类成员方法A
        baseMethodB();//访问父类成员方法B
        //derivedMethod();//访问子类自身成员方法-->没有这个方法,编译失败
    }
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived = new Derived();//创建Derived对象
        derived.method();
    }
}

输出结果:

结果说明: 当子类中有和父类同名成员方法时,对其进行编译输出,子类会优先访问自己的成员。如果父类子类中都没有的成员,则会编译失败。

以上可以得出:成员方法访问遵循就近原则,子类自己有的,就优先访问子类自己,否则再访问父类成员,如果都没有,则会编译失败。

看到这里,也许会有这么一个疑惑:如果子类和父类出现相同成员(包括变量和方法)名时,能不能精确访问到父类的成员呢?

在这里,Java提供了一个关键字,也就是super。

3.super关键字

为了解决上面提出的疑问,**Java提供了super关键字,它的作用是在子类中访问父类的成员。**为了可以更好地区分子类和父类的成员,在子类访问自己的成员中,也可以给自己加上this关键字。

拿上述成员变量同名的情况来举例:

java 复制代码
class Base {
    int a = 10;
    char c = 'A';
}
 
class Derived extends Base {
    int a = 20;
    int c = 52;
    public void method() {
        System.out.println("父类a = " + super.a);
        System.out.println("子类a = " + this.a);
        System.out.println("父类c = " + super.c);
        System.out.println("子类c = " + this.c);
        //System.out.println("d = " + d);没有d,编译失败
    }
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived = new Derived();//创建Derived对象
        derived.method();
    }
}

运行结果:

说明:想要在子类中精确地访问父类成员,可以借助super关键字来完成。

4.子类构造方法

在继承关系里,如果有构造方法,父类和子类的执行顺序又是怎样的呢?

java 复制代码
class Base {
    //构造Base方法
    public Base() {
        System.out.println("这是父类的构造方法Base()");
    }
}
 
class Derived extends Base {
    //构造Derived方法
    public Derived() {
        System.out.println("这是子类的构造方法Derived()");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived = new Derived();//创建Derived对象
    }
}

运行结果:

可以看出:在子类的构造方法中,并没有任何调用父类构造方法的代码,但是从运行结果来看,代码是先执行父类的构造方法,再执行子类的构造方法。 这是为什么呢?

这是因为:在了解继承的概念并通过例子发现,在子类对象中的成员实际上是由两部分组成,继承父类的成员和自己新增加的成员,所以在构造子类对象的时候,先要调用父类的构造方法,讲父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。

为什么会先调用父类的构造方法呢?这背后就藏着一个super关键字,在我们没有定义任何的构造方法的时候,编译器会自动添加一个无参的父类的构造方法,即"super();",值得注意的是,在子类的构造方法中调用父类的构造方法时,"super(); "必须是子类构造方法的第一条语句,这样说明了在该子类构造方法中,super和this这两个关键字是不能同时存在的。即以下写法是错误的:

5.super和this对比

super和this都是对类的访问,二者区别这里笔者整理了一部分表格:

6.代码块

在Java中,代码块是一段用来{}括起来的代码,用于控制变量的作用域或实现特定的初始化逻辑。代码块主要分为以下几种类型:

6.1普通代码块

作用:限制变量的作用范围(局部变量)

位置:方法内或者语句块中

例如:以下代码

java 复制代码
public class Main {
    public static void main(String[] args) {
        int x = 10;
        System.out.println("x = " + x); // 可以访问 x
 
        { // 普通代码块
            int y = 20;
            System.out.println("y = " + y); // 可以访问 y
        }
 
        // System.out.println("y = " + y); // 编译错误,y 超出作用域
    }
}

说明:y 的作用域仅限于 {} 内部,外部无法访问。

6.2实例代码块

作用:用于初始化实例变量,在每次创建对象时执行

位置:类中,方法外

执行时机:每次创建对象时,在构造方法之前执行。

例如以下代码:

复制代码
public class Main {
 
    public Main() {
        System.out.println("构造方法执行...");
    }
 
    { // 实例代码块
        System.out.println("实例代码块执行...");
    }
 
    public static void main(String[] args) {
        new Main(); // 创建对象
    }
}

运行结果:

说明:每次 new 一个新对象时都会先执行实例代码块,再执行构造方法

6.3 静态代码块(static 代码块)

作用:用于初始化静态变量,在类加载时执行

位置 :类中,方法外,用 static 修饰

执行时机类加载时执行,且只执行一次

例如以下代码:

复制代码
public class Main {
 
    public static void main(String[] args) {
        System.out.println("main方法执行...");
    }
 
    static { // 静态代码块
        System.out.println("静态代码块执行...");
    }
}

运行结果:

说明:静态代码块在类加载时执行,比 main() 方法更早。

6.4在继承中的代码块

在了解以上代码块后,回到继承里,这些代码块的会如何执行呢?

复制代码
class Base {
    public int a;
    public double b;
 
    public Base(int a , double b) {
        this.a = a;
        this.b = b;
        System.out.println("父类的构造代码块...");
    }
    {
        System.out.println("父类的实例代码块...");
    }
    static{
        System.out.println("父类的静态代码块");
    }
}
 
class Derived extends Base {
    public Derived(int a , double b) {
        super(a, b);
        System.out.println("子类的构造方法");
    }
    {
        System.out.println("子类的实例代码块...");
    }
    static{
        System.out.println("子类的静态代码块");
    }
 
}
 
public class Main {
    public static void main(String[] args) {
        Derived derived1 = new Derived(5,3.14);//第一次创建Derived对象
        System.out.println("-----------分割线-----------");
        Derived derived2 = new Derived(6,2.78);//第二次创建Derived对象
    }
}

运行结果:

说明:

静态代码块优先执行,且先执行父类静态代码块再执行子类静态代码块。

实例代码块比构造代码块先执行,且执行完父类的实例代码块和构造代码块后,才执行子类的实例代码块和构造代码块。

在第二次实例化对象的时候,父类和子类的静态代码块都不再执行,也就是说静态代码块最先执行,且只执行一次。

7.访问限定修饰符------protected关键字

在继承里,Java提供了一个访问限定修饰符,即protected关键字。可以在不同的包中的子类使用。protected 成员的访问权限比 private 大,比 public 小。 也就是说,当我们在封装和扩展之间取得一个平衡------即:既可以保护父类的实现细节,又不想全部暴露在外,又允许子类进行必要的访问和扩展,那么我们就可以使用protected关键字来限定访问修饰。

private < protected < public 访问权限由小到大

在这里借助不同的包和类实现代码来理解protected关键字:

1)同一个包中的子类:

从结果来看,父类中protected修饰的成员可以在子类中被访问,其中没有限定修饰符的int c系统默认被default(同一包中的同一类用)修饰。

2)不同包中的子类

3)不同包中的非子类

说明:**protected既可以保护父类的实现细节,又不想全部暴露在外,又允许子类进行必要的访问和扩展,**这为Java继承体系提供了精妙的访问控制设计。

8.继承方式与final关键字

8.1继承方式

在本篇文章开头引出继承时,用猫和狗两种动物引出,但在现实生活中,猫和狗还可以分为很多类,比如猫类还可以分为:中华田园猫、苏格兰折耳猫、布偶猫、英国短毛猫等,其中中华田园猫又可以分为:山东狮子猫、狸花猫、橘猫,这些猫的外貌特征又不一样,比如山东狮子猫是鸳鸯眼、头大脸圆等,狸花猫杏核形眼、头部圆润等,用一个关系图来表示就是:

因此,在Java中,继承方式就可以有以下几种表示:

需要注意的是:**Java中并不支持多继承。**也就是说,一个子类不能拥有两个父类。

8.2final关键字

在Java中,当定义一个变量并且初始化,在以后的应用中并希望它会被修改,这个时候就可以使用final关键字。

例如:

因此,在Java的继承关系里,如果不希望某一类会继承,那么就可以使用final关键字来修饰。

例如(为方便理解,这里也只在同一个java文件中展示):

例中,当定义的猫类继承动物时,用final修饰,在后面的田园猫便再也不能书写继承关系。这就说明: 使用final修饰类后,表示该类就不能再被继承。

9.总结

继承是Java面向对象编程的三大基本特性(其他两个是封装、多态)之一,允许子类复用父类的属性和方法,同时扩展自身功能。通过extends关键字建立继承关系,子类可以访问父类的非私有成员,并通过super关键字精确调用父类成员。

继承的关键规则包括:

成员访问:子类优先访问自身成员,如果父子类有同名成员,访问父类成员,需使用super。

构造方法:子类构造时默认先调用父类构造方法(隐式super()),确保父类成员正确初始化。

代码块执行顺序:静态代码块→ 实例代码块→ 构造方法。

访问控制:protected允许子类跨包访问父类成员,平衡封装与扩展性。

继承限制:Java不支持多继承,final修饰的类不可被继承。

相关推荐
八角.。1 小时前
面向对象-多态
java·开发语言
@卓越俊逸_角立杰出@1 小时前
快速学会 Java 实现意图识别:从规则匹配到 BiLSTM 分类器
java·开发语言·人工智能
wear工程师1 小时前
接口超时别只调大 timeout:Java HTTP 客户端其实有 3 段等待
java·http
小小龙学IT1 小时前
simdjson:利用 SIMD 指令实现 GB/s 级 JSON 解析的 C++ 开源库
开发语言·c++·json
ydyd202604211 小时前
设备OEE怎么提升?数据采集+分析优化的完整方案
java·服务器·前端
A_nanda1 小时前
c#WPF开发常见问题
开发语言·c#·wpf
Hello.Reader2 小时前
REALITY 技术深度解析从 TLS 伪装、抗主动探测到 VLESS + XTLS Vision 完整实践
开发语言·php
(╹◡╹)2 小时前
11.RK3588本地大模型内存评估优化
java·linux·前端