Java final修饰符详解

2023-05-16

final 在 Java 中的意思是最终,也可以称为完结器,表示对象是最终形态的,不可改变的意思。final 应用于类、方法和变量时意义是不同的,但本质是一样的,都表示不可改变,类似 C# 里的 sealed 关键字。

使用 final 关键字声明类、变量和方法需要注意以下几点:

  • final 用在变量的前面表示变量的值不可以改变,此时该变量可以被称为常量。
  • final 用在方法的前面表示方法不可以被重写(子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写,又称为方法覆盖。这里了解即可,教程后面我们会详细讲解)。
  • final 用在类的前面表示该类不能有子类,即该类不可以被继承。

final 修饰变量

final 修饰的变量即成为常量,只能赋值一次,但是 final 所修饰局部变量和成员变量有所不同。

  1. final 修饰的局部变量必须使用之前被赋值一次才能使用。
  2. final 修饰的成员变量在声明时没有赋值的叫“空白 final 变量”。空白 final 变量必须在构造方法或静态代码块中初始化。

注意:final 修饰的变量不能被赋值这种说法是错误的,严格的说法是,final 修饰的变量不可被改变,一旦获得了初始值,该 final 变量的值就不能被重新赋值。

public class FinalDemo {
    void doSomething() {
        // 没有在声明的同时赋值
        final int e;
        // 只能赋值一次
        e = 100;
        System.out.print(e);
        // 声明的同时赋值
        final int f = 200;
    }
    // 实例常量
    final int a = 5; // 直接赋值
    final int b; // 空白final变量
    // 静态常量
    final static int c = 12;// 直接赋值
    final static int d; // 空白final变量
    // 静态代码块
    static {
        // 初始化静态变量
        d = 32;
    }
    // 构造方法
    FinalDemo() {
        // 初始化实例变量
        b = 3;
        // 第二次赋值,会发生编译错误
        // b = 4;
    }
}

上述代码第 4 行和第 6 行是声明局部常量,其中第 4 行只是声明没有赋值,但必须在使用之前赋值(见代码第 6 行),其实局部常量最好在声明的同时初始化。

代码第 13、14、16 和 17 行都声明成员常量。代码第 13 和 14 行是实例常量,如果是空白 final 变量(见代码第 14 行),则需要在构造方法中初始化(见代码第 27 行)。

代码第 16 和 17 行是静态常量,如果是空白 final 变量(见代码第 17 行),则需要在静态代码块中初始化(见代码第 21 行)。

另外,无论是那种常量只能赋值一次,见代码第 29 行为 b 常量赋值,因为之前 b 已经赋值过一次,因此这里会发生编译错误。

final 修饰基本类型变量和引用类型变量的区别

当使用 final 修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。 但对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。

下面程序示范了 final 修饰数组和 Person 对象的情形。

import java.util.Arrays;
class Person {
    private int age;
    public Person() {
    }
    // 有参数的构造器
    public Person(int age) {
        this.age = age;
    }
    // 省略age的setter和getter方法
    // age 的 setter 和 getter 方法
}
public class FinalReferenceTest {
    public static void main(String[] args) {
        // final修饰数组变量,iArr是一个引用变量
        final int[] iArr = { 5, 6, 12, 9 };
        System.out.println(Arrays.toString(iArr));
        // 对数组元素进行排序,合法
        Arrays.sort(iArr);
        System.out.println(Arrays.toString(iArr));
        // 对数组元素赋值,合法
        iArr[2] = -8;
        System.out.println(Arrays.toString(iArr));
        // 下面语句对iArr重新赋值,非法
        // iArr = null;
        // final修饰Person变量,p是一个引用变量
        final Person p = new Person(45);
        // 改变Person对象的age实例变量,合法
        p.setAge(23);
        System.out.println(p.getAge());
        // 下面语句对P重新赋值,非法
        // p = null;
    }
}

从上面程序中可以看出,使用 final 修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。

例如上面 iArr 变量所引用的数组对象,final 修饰后的 iArr 变量不能被重新赋值,但 iArr 所引用数组的数组元素可以被改变。

与此类似的是,p 变量也使用了 final 修饰,表明 p 变量不能被重新赋值,但 p 变量所引用 Person 对象的成员变量的值可以被改变。

注意:使用 final 声明变量时,要求全部的字母大写,如 SEX,这点在开发中是非常重要的。

如果一个程序中的变量使用 public static final 声明,则此变量将称为全局变量,如下面的代码:

public static final String SEX= "女";

final修饰方法

final 修饰的方法不可被重写,如果出于某些原因,不希望子类重写父类的某个方法,则可以使用 final 修饰该方法。

Java 提供的 Object 类里就有一个 final 方法 getClass(),因为 Java 不希望任何类重写这个方法,所以使用 final 把这个方法密封起来。但对于该类提供的 toString() 和 equals() 方法,都允许子类重写,因此没有使用 final 修饰它们。

下面程序试图重写 final 方法,将会引发编译错误。

public class FinalMethodTest {
    public final void test() {
    }
}
class Sub extends FinalMethodTest {
    // 下面方法定义将出现编译错误,不能重写final方法
    public void test() {
    }
}

上面程序中父类是 FinalMethodTest,该类里定义的 test() 方法是一个 final 方法,如果其子类试图重写该方法,将会引发编译错误。

对于一个 private 方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法——如果子类中定义一个与父类 private 方法有相同方法名、相同形参列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。

因此,即使使用 final 修饰一个 private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。

下面程序示范了如何在子类中“重写”父类的 private final 方法。

public class PrivateFinalMethodTest {
    private final void test() {
    }
}
class Sub extends PrivateFinalMethodTest {
    // 下面的方法定义不会出现问题
    public void test() {
    }
}

上面程序没有任何问题,虽然子类和父类同样包含了同名的 void test() 方法,但子类并不是重写父类的方法,因此即使父类的 void test() 方法使用了 final 修饰,子类中依然可以定义 void test() 方法。

final 修饰的方法仅仅是不能被重写,并不是不能被重载,因此下面程序完全没有问题。

public class FinalOverload {
    // final 修饰的方法只是不能被重写,完全可以被重载
    public final void test(){}
    public final void test(String arg){}
}

final修饰类

final 修饰的类不能被继承。当子类继承父类时,将可以访问到父类内部数据,并可通过重写父类方法来改变父类方法的实现细节,这可能导致一些不安全的因素。为了保证某个类不可被继承,则可以使用 final 修饰这个类。

下面代码示范了 final 修饰的类不可被继承。

final class SuperClass {
}
class SubClass extends SuperClass {    //编译错误
}

因为 SuperClass 类是一个 final 类,而 SubClass 试图继承 SuperClass 类,这将会引起编译错误。

final 修饰符使用总结

1. final 修饰类中的变量

表示该变量一旦被初始化便不可改变,这里不可改变的意思对基本类型变量来说是其值不可变,而对对象引用类型变量来说其引用不可再变。

其初始化可以在两个地方:

一是其定义处,也就是说在 final 变量定义时直接给其赋值;

二是在构造方法中。

这两个地方只能选其一,要么在定义时给值,要么在构造方法中给值,不能同时既在定义时赋值,又在构造方法中赋予另外的值。

2. final 修饰类中的方法

说明这种方法提供的功能已经满足当前要求,不需要进行扩展,并且也不允许任何从此类继承的类来重写这种方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。

在声明类中,一个 final 方法只被实现一次。

3. final 修饰类

表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。

对于 final 类中的成员,可以定义其为 final,也可以不是 final。而对于方法,由于所属类为 final 的关系,自然也就成了 final 型。也可以明确地给 final 类中的方法加上一个 final,这显然没有意义。

Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java final修饰符详解 的相关文章

  • 方法重载。你能过度使用它吗?

    当定义多个使用不同过滤器返回相同形状的数据的方法时 什么是更好的做法 显式方法名称或重载方法 例如 如果我有一些产品并且我正在从数据库中提取 显式方式 public List
  • JTree 避免重新加载后崩溃

    我正在尝试找到解决崩溃问题的方法JTree重新加载后 情况 JTree Office A Office A 1 Office A 1 1 Office A 1 2 Office B Office B 1 Office B 1 1 Offic
  • Guice:当 FactoryBuilder 中提供合适的构造函数时,“找不到合适的构造函数”

    我使用 Guice 进行依赖注入 但收到此错误 1 Could not find a suitable constructor in java lang Void Classes must have either one and only
  • 枚举内的枚举

    这不是我被卡住的问题 而是我正在寻找一种简洁的方式来编写我的代码 本质上 我正在编写一个事件驱动的应用程序 用户触发事件 事件被发送到适当的对象 然后对象处理事件 现在我正在编写偶数处理程序方法 我希望使用 switch 语句来确定如何处理
  • 外部化 Spring Security 配置?

    我有一个 Web 应用程序 可以使用 Spring Security 的几种不同配置 但是 这些差异配置都是在我的 applicationContext 配置文件中设置的 因此 为了在客户站点调整这些内容 必须在 WAR 文件内修改这些内容
  • 如何在不使用反射的情况下查看对象是否是数组?

    在Java中如何在不使用反射的情况下查看对象是否是数组 如何在不使用反射的情况下迭代所有项目 我使用 Google GWT 所以不允许我使用反射 我很想在不使用反射的情况下实现以下方法 private boolean isArray fin
  • Java J文件选择器

    我希望能够控制外观JFileChooser 我特别想保存如何JFileChooser上次显示时显示 我想保存它是否在详细信息 列表视图中使用以及列表被排序的列 例如 大小或修改日期 我知道有很多关于JFileChooser但我一直没能找到我
  • SQLiteAssetHelper 甚至在从资产文件夹复制数据库之前就导致立即崩溃

    https github com jgilfelt android sqlite asset helper https github com jgilfelt android sqlite asset helper 我要从SQLiteOpe
  • 在仔细锁定但不受信任的代码上使用 Thread.stop()

    我知道Thread stop 已被弃用 并且有充分的理由 它通常不安全 但这并不意味着它是never安全 据我所知 在我想要使用它的上下文中它是安全的 而且 据我所知 我别无选择 上下文是一个两人策略游戏的第三方插件 以国际象棋为例 第三方
  • SQLite 64位整数在jooq中被识别为int

    我有一个与 jOOQ 一起使用的 SQLite 数据库 当我使用 jOOQ 的代码生成工具时 它会按预期构建所有表和记录类 然而 所有的 SQLiteINTEGER列变成java lang Integer生成的代码中的字段 问题是 SQLi
  • 使用 Jboss7 加载资源返回 null

    如何使用Jboss7 1从java代码中加载图像等资源 这曾经与 Jboss4 一起使用 this getClass getClassLoader getResourceAsStream myapp includes images imag
  • 我有什么理由应该嘲笑?

    我也是 Mockito 和 PowerMockito 的新手 我发现我无法使用纯 Mockito 测试静态方法 因此我需要使用 PowerMockito 对吗 我有一个非常简单的类 名为 Validate 使用这个非常简单的方法 publi
  • 抛出 UnsupportedOperationException

    因此其中一种方法的描述如下 public BasicLinkedList addToFront T data 该操作无效 对于排序列表 将生成 UnsupportedOperationException 使用消息 排序列表的操作无效 我的代
  • Maven编译错误:包不存在

    我正在尝试向现有企业项目添加 Maven 支持 这是一个多模块项目 前 2 个模块编译和打包没有问题 但我面临编译错误 我尝试在多个模块中使用相同的依赖项 我的结构是 gt parent gt pom xml gt module 1 gt
  • 在Java中,为什么某些变量首先需要初始化,而其他变量只需要声明?

    我试图更深入地理解我是否遗漏了一些关于 Java 何时需要变量初始化与简单声明的理解 在以下代码中 不需要为变量 row 赋值即可编译和运行 但变量 column 则需要赋值 注意 该程序没有任何用处 它已被修剪为仅显示此问题所需的内容 以
  • 如何强制 Spark 执行代码?

    我如何强制 Spark 执行对 map 的调用 即使它认为由于其惰性求值而不需要执行它 我试过把cache 与地图调用 但这仍然没有解决问题 我的地图方法实际上将结果上传到 HDFS 所以 它并非无用 但 Spark 认为它是无用的 简短回
  • 我的代码线程不安全吗?

    我编写了代码来理解 CyclicBarrier 我的应用程序模拟选举 每轮选出得票少的候选人 该候选人从竞争中淘汰以获得胜利 source class ElectoralCommission public volatile boolean
  • 使用 System.out.println 显示特殊字符

    我在将带有特殊字符的文本从网络服务发送或显示到数据库时遇到问题 在我的 Eclipse 上 我已将字符编码设置为 UTF 8 但它仍然不允许我显示字符 例如 像下面的代码一样简单的打印 String test System out prin
  • FocusEvent 没有获取 JFormattedTextField 的最后一个值,我如何获取它?

    我有两个JFormattedTextField我的物体JFrame目的 我想要通过这些值进行基本数学 加法 JFormattedTextField对象 我希望当焦点丢失第一个或第二个文本字段时发生这种情况 但当 focusLost 事件没有
  • 如何在java中进行多处理,以及预期的速度提升是多少?

    我是一个新手 使用 Java 对 csv 文件进行一些数据处理 为此 我使用 Java 的多线程功能 线程池 将 csv 文件批量导入到 Java 中 并对每一行执行一些操作 在我的四核处理器上 多线程大大加快了处理速度 我很想知道多处理如

随机推荐

  • 我人生的转折点

    来到兄弟连 差不多快两周的时间了 xff0c 在这里的每一天每天都觉得非常的充实 xff0c 来到这里才知道什么才做学习 xff0c 什么才叫只要努力了用功了 xff0c 用心了 xff0c 就会有成果 就如第一次的测验 xff0c 每天都
  • Canal AdminGuide

    背景 先前开源了一个开源项目 xff1a 阿里巴巴开源项目 基于mysql数据库binlog的增量订阅 amp 消费 本文主要是介绍一下如何部署 amp 使用 环境要求 1 操作系统 a 纯java开发 xff0c windows linu
  • 【Android病毒分析报告】 - Usbcleaver

    本文章由Jack Jia编写 xff0c 转载请注明出处 文章链接 xff1a http blog csdn net jiazhijun article details 9179749 作者 xff1a Jack Jia 邮箱 xff1a
  • 【浅墨著作】《逐梦旅程:Windows游戏编程之从零开始》勘误&配套源代码下载...

    I 39 m back 恩 xff0c 几个月不见 xff0c 大家还好吗 xff1f 这段时间真的好多童鞋在博客里留言说或者发邮件说浅墨你回来继续更新博客吧 woxiangnifrr童鞋说每天都在来浅墨的博客逛一下看有没有更新 xff0c
  • Anychart图表系列九之Dashboard

    Dashboard中文释义为仪表盘 控制台 xff0c 这个用词很生动 xff0c 它指的是多个图组合在一起的效果 xff0c 就像模具工作中的操作台一样由多种图形仪器组成 在项目中 xff0c 特别是高管平台 xff0c 领导看重的是多套
  • 若依前后端分离版本启动教程

    第一步 xff1a 搜索若依官网 第二步 xff1a 选择前后端分离版本 第三步 复制链接 第四步 随便在自己的电脑找找一个文件夹 xff0c 执行git命令 xff08 git安装使用教程这里不做说明 xff09 回车 96 96 96
  • char指针与char数组的区别(非常详细!)

    首先看指针 char p 61 helloworld 在这里p 是一个变量 其类型为指针类型 并且指向一个字符串 字符串内容为 helloworld 如果要访问p 2 的话 就需要先从p 中取出地址 该地址为 helloworld 的首地址
  • 大学六年我读过的一些书

    明天就是2014年 xff0c 今晚是13年最后一天 xff0c 我想不少人会写一下自己13年的一些心得 xff0c 在上一篇日志中 xff0c 该总结的已经总结 xff0c 想了解更多的已经加我个人微信了 我在想 xff0c 大学其实很多
  • Ubuntu硬盘分区和分区文件类型及Linux分区基础知识

    Ubuntu硬盘分区的常用系统 xff0c 于是我学习研究了Ubuntu硬盘分区 xff0c 在这里对大家详细介绍下Ubuntu硬盘分区应用 xff0c 希望对大家有用Ubuntu硬盘分区包含了非常好的翻译和容易使用的架构 如果你准备在硬盘
  • 欢迎使用CSDN-markdown编辑器

    我和谁都不争 xff0c 和谁争 xff0c 我都不屑 xff1b 我爱大自然 xff0c 其次就是艺术 xff1b 我双手烤着生命之火取暖 xff0c 火萎了 xff0c 我也准备走了 本Markdown编辑器使用StackEdit修改而
  • 从高考到程序员--------你到底在追求着什么?------串烧似的文章

    随便写写 xff0c 万事开头难 xff0c 不知道应该怎么写出来 xff0c 因为只有自己经历的才能是最美好的回忆 xff0c 不知道这个编辑器写的效果如何 xff0c 凑合着看吧 xff0c 从高考到程序员征文看了几篇 xff0c 决定
  • AI以来----已不再是未来

    在过去的2016年 xff0c 可以说AI的核心发展一直围绕着开源和生态 国际巨头纷纷涉足深度学习平台 xff0c 让原本是高高在上的人工智能 xff0c 迅速落地细化到了行业的各个领域 随着AI技术正在为全球开发者所用 xff0c 极大的
  • 即兴嘻哈

    kao ha wowo 深夜不能寐 xff0c 那个翻来覆也不能把觉睡 一首即兴style说说ni为何半夜还不睡 xff0c 一线城市压力大来人总疲惫 xff0c 为了买房买车拼命都在向前奔 年轻人出来打拼为何这么累 xff0c 多少人为了
  • 裸辞3个月扛不住后,随便接了offer更惨!

    最近发现年底找工作的人不少 xff0c 部门里就2个hc xff0c 一周能收2000 43 简历 xff0c 这比例有点 过分 了 虽说大部分是年底先看看机会试试水 xff0c 准备年后冲击的 xff0c 但看简历里也有不少中间裸辞的 x
  • Docker常用命令-自用

    Docker常用命令 自用 1 镜像仓库 1 1 登陆 登出 登陆 默认Docker Hub docker login u span class token punctuation span username span class toke
  • 程序猿的浪漫之二进制表白篇

    那天情人节 xff0c 我给她发了一串数字 xff08 01001001 00100000 01101100 01101111 01110110 01100101 00100000 01111001 01101111 01110101 xf
  • Dockerfile使用教程

    第一 创建一个spring boot项目 第二 创建 xff1a Dockerfile文件 xff08 在项目根目录下 xff09 第三 复制粘贴 FROM java 8 设置基础镜像 FROM openjdk 8 jdk alpine 指
  • C++ 用socket封装成http

    HttpSocket h interface for the CHttpSocket class if defined AFX HTTPSOCKET H F49A8F82 A933 41A8 AF47 68FBCAC4ADA6 INCLUD
  • echarts更换主题

    前言 本篇文章基于上一篇文章 xff1a vue工程整合echarts xff0c 因此这里主要讲的是主题是如何配置的 xff0c 其他代码在这里不再赘述 官网中已经说明了echarts除了有默认的主题外 xff0c 并且内置了dark主题
  • Java final修饰符详解

    final 在 Java 中的意思是最终 xff0c 也可以称为完结器 xff0c 表示对象是最终形态的 xff0c 不可改变的意思 final 应用于类 方法和变量时意义是不同的 xff0c 但本质是一样的 xff0c 都表示不可改变 x