当前位置: 首页 > news >正文

从浅入深讲解Java继承

从浅入深讲解Java继承

  • 一、继承基础
    • 1、引入
    • 2、继承语法
  • 二、父类子类的优先级问题
    • 1、引入
  • 三、this 与 super关键字
    • 1、基础使用
    • 2、拓展
    • 3、super与构造方法
  • 三、代码块深入剖析
    • 1、代码块分类
      • (1)实例代码块
      • (2)静态代码块
    • 2、代码块与父子类
  • 四、final关键字
    • 1、final修饰变量
    • 2、final修饰方法
    • 3、final修饰类

一、继承基础

1、引入

**继承语法的存在是为了提高代码的复用性。**假设我们想创建以下几个类:DogCatFish

那么就会有以下代码:

class Dog{
    public String name;
    public int age;
    public String color;
    
    public void eat(){
        System.out.println(this.name + "在吃。");
    };
    
    public void run(){
        System.out.println("狗在跑。");
    }
    
}
class Cat{
    public String name;
    public int age;
    
    public void eat(){
        System.out.println(this.name + "在吃。");
    };
    
     public void jump(){
        System.out.println("猫在跳。");
    }   
}
class Fish{
    public String name;
    public int age;
    
    public void eat(){
        System.out.println(this.name + "在吃。");
    };
    
    public void swim(){
        System.out.println("鱼在游泳。");
    }
}

我们发现上面的代码太冗余了,代码重复度太高了。这种代码的可读性是很低的。那么怎么解决这个问题呢?

2、继承语法

不难发现这些都是动物,既然都是动物,就说明他们存在很多共同点。比如他们都有姓名、年龄等,同时他们都会有吃这个行为。

因此,我们可以将这些共有属性直接抽离出来构成一个单独的类Animal。然后让上面的三个类继承Animal类。

继承的结果就是子类中拥有了父类的变量和方法。

语法如下所示:

class 子类名 extends 父类名{
}

那么刚刚的代码就能够改为下面的形式:

class Animal {
    public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "在吃。");
    };
}

class Dog extends Animal{

    public String color;
    public void run(){
        System.out.println("狗在跑。");
    }

}

class Cat extends Animal{
    public void jump(){
        System.out.println("猫在跳。");
    }
}

class Fish extends Animal{
    public void swim(){
        System.out.println("鱼在游泳。");
    }
}

作出上述改动后,我们的代码就变得更加简洁清楚了,同时代码的复用性也大大提高了。

二、父类子类的优先级问题

1、引入

class A{
    public int a = 111;
}

class B extends A{
    public int a = 222;

    public void test(){
        System.out.println(a);
    }

    public static void main(String[] args) {
        B b1 = new B();
        b1.test();
    }
}

当我们的子类和父类出现了相同的变量和方法的时候,当我们直接调用这些重复的变量时,是调用子类还是父类呢?

我们运行上述代码:
在这里插入图片描述
最终打印的结果是子类中的变量。

因此,我们的变量(方法)的调用是符合就近原则的。

子类中的方法肯定优先找子类的变量和方法,子类中没有再去父类中找。

三、this 与 super关键字

1、基础使用

如果我们不想要就近原则,我们就想在子类方法中调用父类中的变量,此时我们就需要用super关键字了。
我们只需要在方法调用和变量名前面加上super.即可。

因此,观察下面的代码:

class A{
    public int a = 111;
}

class B extends A{
    public int a = 222;

    public void test(){
        System.out.println(super.a);
    }

    public static void main(String[] args) {
        B b1 = new B();
        b1.test();
    }
}

在这里插入图片描述
观察终端中的结果,我们就会发现成功打印出了父类中的变量。

2、拓展

在父类和子类没有重名的情况下,我们能不能用this去访问父类中的变量呢?如果没看懂这句话的话,我们可以看下面这段代码:

class A{
    public int a = 1;
    public int b = 2;
}

class B extends A{
    public int c = 3;
    public int d = 4;

    public void test(){
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(this.c);
        System.out.println(this.d);
    }

    public static void main(String[] args) {
        B b1 = new B();
        b1.test();
    }
}

在上面的代码中,我们发现,ab是父类中的变量,但我们却用了关键字this。这样写会不会报错呢?
在这里插入图片描述
答案是没有报错,并且成功引用了。通过这个例子向大家介绍一下thissuper的使用范围。

在这里插入图片描述

因此,当某个变量(方法)父类子类中没有重复时,this也可以调用父类中的变量(方法)。当某个变量(方法)父类子类重复时,this就只能调用子类中的了。

3、super与构造方法

父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。

当我们不继承的时候,编译器提供的构造方法如下:

public B(){

}

但是当我们继承了父类A后,编译器提供的构造方法如下:

public B(){
	super();
}

如果我们重写构造方法的话,我们也必须在子类构造方法中的第一行调用父类构造方法,否则会出现报错。比如我们可以这样写:

class A{
    public int a = 1;
    public int b = 2;
    public A(int a, int b){
        this.a = a;
        this.b = b;
    }
}

class B extends A{
    public int c = 3;
    public int d = 4;
    public B(int a, int b, int c, int d){
        super(a, b);
        this.b = b;
        this.d = d;
    }
}

三、代码块深入剖析

1、代码块分类

(1)实例代码块

我们在类中,直接写一个{},此时就构成了实例代码块,如下图所示:

class A{
    public int a = 1;
    public int b = 2;
    
    {
        //实例代码块    
    }
    public A(int a, int b){
        this.a = a;
        this.b = b;
    }
}

实例代码块在对象创建时执行,并且在构造方法执行之前执行。

(2)静态代码块

静态代码块就是在实例代码块前面写一个static关键字。

class A{
    public int a = 1;
    public int b = 2;

    static{
        //静态代码块
    }
    public A(int a, int b){
        this.a = a;
        this.b = b;
    }
}

静态代码块只会执行一次,并且执行顺序在实例代码块、构造方法之前。

2、代码块与父子类

如果我们父类和子类中都包含代码块,那此时的执行顺序又是怎样的呢?

class A{
    public int a = 1;
    public int b = 2;

    static{
        System.out.println("父类静态代码块。");
    }

    {
        System.out.println("父类实例代码块。");
    }

    public A(){
        System.out.println("父类构造方法。");
    }


}

class B extends A{
    public int c = 3;
    public int d = 4;

    {
        System.out.println("子类实例代码块。");
    }

    static{
        System.out.println("子类静态代码块。");
    }

    public B(){
        super();
        System.out.println("子类构造方法。");
    }

    public static void main(String[] args) {
        B b = new B();
    }
}

上述代码的执行结果如下:
在这里插入图片描述
说明此时我们是先执行父类静态方法,再执行子类静态方法。然后执行父类的实例代码块和构造方法,最终执行子类的实例代码块和构造方法。

四、final关键字

1、final修饰变量

final关键字修饰变量后,该变量就变成了常量。变成常量后,说明这个常量的数值无法在后续的调用过程中修改。
例如:

public final int a;

2、final修饰方法

final修饰的方法是无法重写的。因此,当父类方法加了final关键字后,子类继承后,无法重写该方法。

public final void test(){

}

3、final修饰类

final修饰的类无法被继承。

final class A{
}

相关文章:

  • ctfshow-反序列化(web267-web270)
  • 用的到的linux-Day1
  • 100天精通Python(实用脚本篇)——第113天:基于Tesseract-OCR实现OCR图片文字识别实战
  • 如何发布自己的npm包
  • C语言算法赛——蓝桥杯(省赛试题)
  • 基于SpringBoot Vue高校失物招领系统
  • Docker consul
  • Flink(十四)【Flink SQL(中)查询】
  • How to disagree with ideas
  • 【Nginx】使用自生成证书配置nginx代理https
  • Nginx快速入门
  • mariadb数据库从入门到精通
  • 数据库常用系统表及常用功能
  • 大模型学习与实践笔记(七)
  • SQL慢语句执行的很慢,如何分析优化呢,(如何优化的呢?)
  • H5小游戏如何提升APP变现收益?
  • SQL执行时间过长如何优化
  • 阿里云国外服务器多少钱一个月?24元/月
  • VRPSolverEasy:支持VRP问题快速建模的精确算法Python包
  • 架构篇05-复杂度来源:高可用
  • 国务院办公厅印发《国务院2025年度立法工作计划》
  • 俄乌拟在土耳其举行会谈,特朗普:我可能飞过去
  • 巴基斯坦全国航班仍持续延误或取消
  • 湛江霞山通报渔船火灾:起火船舶共8艘,无人员伤亡或被困
  • 这些网红果蔬正在收割你的钱包,营养师:吃了个寂寞
  • 哈尔滨工业大学原副校长王魁业逝世,享年92岁