八 Java代码块、继承、方法重写和final关键字

本文深入探讨Java中的代码块、继承、方法重写及final关键字,解析各概念的应用与区别,提升编程技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

八 Java代码块、继承、方法重写和final关键字

一. 主要内容
在这里插入图片描述
在这篇博客中,主要介绍了:
1.代码块的相关知识(重要):将在博客中通过代码介绍各个代码块的相关概念,并进行比较。
2.继承(重要):继承是面向对象编程过程中的一个重要方法。在以前的博客中介绍了面向对象编程的三个重要方法,分别是:封装、继承和多态。在这个博客中,将对继承予以介绍。
3.方法重写的相关概念:在学习了继承的相关概念之后,当我们不满足直接继承父类的方法,而想对父类的方法进行改造,加入子类特有内容,可以使用方法重写。同时需注意与方法重载做区分。
4.final关键字:我们已经学过多个java关键字,在这次博客中,对final关键字进行介绍。
二. 代码块
什么是代码块?代码块怎样分类?
答:用程序做例子:

public class Test {
    {
        ////
    }
}

这种形式的程序段我们将其称之为代码块,所谓代码块就是用大括号“{}”将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法。一般来说代码块是不能单独运行的,它必须要有运行主体。
代码块分为四种:
局部代码块,构造代码块,静态代码块,同步代码块,其中同步代码块将在以后进行介绍。
2.1 普通代码块
普通代码块是我们用得最多的也是最普遍的,它就是在方法名后面用{}括起来的代码段。普通代码块是不能够单独存在的,它必须要紧跟在方法名后面。同时也必须要使用方法名调用它。

public class Test {
    public void test(){
        System.out.println("普通代码块");
    }
}

2.2 静态代码块
要用到static关键字,静态代码块就是用static修饰的用“{}”括起来的代码段,它的主要目的就是对静态属性进行初始化。

public class Test {
    static{
        System.out.println("静态代码块");
    }
}

其特点是,在加载的时候就执行,并且只执行一次。
2.3 构造代码块
在类中直接定义没有任何修饰符、前缀、后缀的代码块即为构造代码块。我们明白一个类必须至少有一个构造函数,构造函数在生成对象时被调用。构造代码块和构造函数一样同样是在生成一个对象时被调用,通过下面的程序可以看出构造代码块何时被调用。

public class Test {
    /**
     * 构造代码
     */
    {
        System.out.println("执行构造代码块...");
    }
    
    /**
     * 无参构造函数
     */
    public Test(){
        System.out.println("执行无参构造函数...");
    }
    
    /**
     * 有参构造函数
     */
    public Test(String id){
        System.out.println("执行有参构造函数");
    }
}

定义了一个简单的类,该类包含无参构造函数、有参构造函数以及构造代码块,同时在上面也提过代码块是没有独立运行的能力,其必须要有一个可以承载的载体。编译器会将代码块按照他们的顺序(假如有多个代码块)插入到所有的构造函数的最前端,这样就能保证不管调用哪个构造函数都会执行所有的构造代码块。
所以最终运行的结果为:

public static void main(String[] args) {
        new Test();
        System.out.println("----------------");
        new Test("1");
    }
------------
Output:
执行构造代码块...
执行无参构造函数...
----------------
执行构造代码块...
执行有参构造函数...

由此可见,当new一个对象的时候,总是先执行构造代码块,再执行构造函数。需要注意的是,构造代码块不是在构造函数之前运行的,而是依托构造函数执行。
正是基于构造代码块的特点,我们可以用它来干一些事情:
1.初始化实例变量
如果一个类中存在若干个构造函数,这些构造函数都需要对实例变量进行初始化,如果我们直接在构造函数中实例化,必定会产生很多重复代码,繁琐和可读性差。这里我们可以充分利用构造代码块来实现。
2.初始化实例环境
一个对象必须在适当的场景下才能存在,如果没有适当的场景,则就需要在创建对象时创建此场景。我们可以利用构造代码块来创建此场景,尤其是该场景的创建过程较为复杂。构造代码会在构造函数之前执行。
三. 继承
什么是继承?为什么要使用继承?
答:继承是面向对象最显著的一个特性。在使用继承之后,可以提高代码的复用性、提高了代码的维护性。对以后多态的产生奠定了基础。
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
3.1 继承的语法

class 子类 extends 父类 {}

子类被称为派生类,父类被称为超类(Super Class)
3.2 继承的特点
1.java语言中,不支持多继承,但支持多层继承。由以下程序予以说明:
错误写法:(语法不支持)

class A {}
class B {}
class C extends A,B {}          // 一个子类继承了两个父类

正确写法:

class A {}
class B extends A {}
class C extends B {}

上面的程序描述了继承的语法和支持多层继承的特点。
2.在一个子类继承的时候,实际上会继承父类之中的所有操作(属性、方法),但是需要注意的是,对于所有的非私有(no private)操作属于显式继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(间接完成)。

class A {
    private String msg;

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return this.msg;
    }
}

class B extends A {
    public void print() {
        //System.out.println(msg); // 错误: msg定义为private,不可见
    }
}

public class TestDemo {
    public static void main(String args[]) {
        B b = new B();
        b.setMsg("张三");
        System.out.println(b.getMsg());
    }
}

对于A类之中的msg这个私有属性发现无法直接进行访问,但是却发现可以通过setter、getter方法间接的进行操作。
3.在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。

class A {
    public A() {         // 父类无参构造
              System.out.println("*************************") ;
    }
}
class B extends A {
    public B() {         // 子类构造
              System.out.println("#########################");
    }
}
public class TestDemo {
    public static void main(String args[]) {
              B b = new B() ;   // 实例化子类对象
    }
}

程序运行之后,得到的结果为:

*************************
#########################

虽然实例化的是子类对象,但是发现它会默认先执行父类构造,调用父类构造的方法体执行,而后再实例化子类对象,调用子类的构造方法。而这个时候,对于子类的构造而言,就相当于隐含了一个super()的形式:
即:

class B extends A {
    public B() { // 子类构造
        super(); // 调用父类构造
        System.out.println("#########################");
    }
}

如果默认调用的是无参构造,而如果这个时候父类没有无参构造,则子类必须通过super()调用指定参数的构造方法:
即:

class A {
    public A(String msg) { // 父类构造
        System.out.println("*************************");
    }
}

class B extends A {
    public B() { // 子类构造
        super("Hello"); // 调用父类构造
        System.out.println("#########################");
    }
}

public class TestDemo {
    public static void main(String args[]) {
        B b = new B(); // 实例化子类对象
    }

由此可以看出,需要通过super(String …)来调用父类的方法。
3.3 继承中成员变量的关系
1 子类中的成员变量和父类中的成员变量名称不一样
2 子类中的成员变量和父类中的成员变量名称一样
在子类中访问一个变量的查找顺序(“就近原则”)
(1)在子类的方法的局部范围找,有就使用
(2)在子类的成员范围找,有就使用
(3)在父类的成员范围找,有就使用
(4)如果还找不到,就报错
3.4 继承中成员方法的关系
1 当子类的方法名和父类的方法名不一样的时候
2 当子类的方法名和父类的方法名一样的时候
通过子类调用方法:
(1) 先查找子类中有没有该方法,如果有就使用
(2)在看父类中有没有该方法,有就使用
(3)如果没有就报错
4. 方法重写
为什么要使用方法重写?重写的前提是什么?
答:子类当然可以原封不动的继承父类的变量与方法,但是如果我们对于父类的方法功能不满意时,就要使用方法重载。重写的前提是必须要有重载。
总之一句话,方法重写即是子类定义特殊方法的过程。
4.1 方法重写注意事项
1.方法重写时, 方法名与形参列表必须一致。
2.方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。
3.方法重写时,子类的返回值类型必须要小于或者 等于父类的返回值类型。
4.2 方法重写与方法重载的区别
首先,回顾一下方法重载的概念:
构成方法重载的必要条件是 方法名相同 参数列表不同(参数类型\当有两个不同类型参数时,顺序不同\个数不同)
两者比较:
1.方法的声明:
方法重载:方法名相同,参数列表不同
方法重写:方法名相同,参数列表相同
2.返回值:
方法重载:没有要求
方法重写:返回值必须相同或者子类
3.访问权限:
方法重载:没有要求
方法重写:子类不能比父类更严格
4.范围:
方法重载:同一个类中
方法重写:继承关系中
5. final关键字
final关键字有以下几种主要用途:
1.用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲,我们必须在声明时或者构造方法中对它赋值;
2.用来修饰方法参数,表示在变量的生存期中它的值不能被改变;
3.修饰方法,表示该方法无法被重写;
4.修饰类,表示该类无法被继承。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值