前面介绍了多态的相关用法,可以看到一个子类从父类继承之后,便能假借父类的名义到处晃悠。这种机制在正常情况之下没啥问题,但有时为了预防意外发生,往往只接受当事人来处理,不希望它的儿子乃至孙子来瞎掺和。可是犹记得几种开放性修饰符,只能控制某个实体能否被外部访问,从未听说可决定某个类能否被其它类所继承。
毫无疑问,是否开放与能否继承是两种不同的概念,不管是被public修饰的公共类,还是被private修饰的私有类,它们默认都是允许继承的。要想让某个类不能被其它类继承,还得在类名前面额外添加一个关键字final,表示这个类已经是最终的类,请不要再去派生子类了。相对多态概念而言,final也可以理解为终态,即最终的状态,终态当然是不可改变的,否则就不叫终态了。
仍旧以鸡类为例,长大以后的鸡很容易区分是公鸡还是母鸡,不管是从鸡冠还是羽毛还是叫声,都能迅速分辨公鸡母鸡。但是对于小鸡来说,区分它的性别可不容易,要知道有一种职业叫做“小鸡性别鉴定师”,年薪高达4万英镑(折合人民币40万左右)。所以,与其花费九牛二虎之力去分辨小鸡的性别,不如直接忽略它们的区别,反正看起来都是一群毛茸茸的小家伙么。既然小鸡不再区别性别,那么小鸡类就无需派生什么公小鸡、母小鸡之类,如此一来,新定义的小鸡类必须是最终状态的类,不可被其它类继承。在一个类定义的最前面添加final修饰符,该类就变成了终态类,于是保持终态的小鸡类Chick的定义代码示例如下:

//定义一个小鸡类。因为小鸡的性别难以辨别,所以不再定义性别字段,小鸡类也不允许被继承
final public class Chick { // 定义一个名称属性
public String name; // 定义一个叫唤方法
public void call() {
System.out.println("叽叽喳喳");
}
}

上面的Chick类,与普通类的区别仅仅是多了个final,正因为有了final,它才成为无儿无女的终类。

关键字final除了用于修饰类,还能用来修饰类的成员属性和成员方法。当一个成员属性戴上了final的帽子,它就必须在变量声明的同时一块赋值,并且这个初始值也是该属性的终值。凡是被final修饰的成员属性,只能进行初始化赋值,事后不能再次给它赋值了。要是一个成员方法也戴上final的帽子,意味着该方法是最终方法,不可在子类中重写,即使它是public类型也无济于事。
总的来说,final存在的意义是为了维护某个实体的纯洁性,不允许外部肆意篡改该实体。下列是final可作用的实体及其产生的影响:
1、一旦某个类被final修饰,则该类无法再派生出任何子类。
2、一旦某个成员属性被final修饰,则该属性不能再次赋值了。
3、一旦某个成员方法被final修饰,则该方法禁止被子类重载。
接下来尝试给鸡类增加几个终态的final成员,原先在公鸡类中使用数字0表示雄性,在母鸡类中使用数字1表示雌性,显然数字取值很容易搞混淆。现在利用两个终态的整型变量MALE和FEMALE分别保存0和1,由于终态属性无法被再次修改,因此这两个变量形同常量。具体的性别常量定义代码如下所示:

	// 以下利用final修饰成员属性和成员方法
public final int MALE = 0; // 雄性
public final int FEMALE = 1; // 雌性

假如Chicken原来有个canSwim方法,考虑到鸡类都不会游泳,那么该方法肯定返回false。故而canSwim方法完全可以披上final的护身符,不管公鸡类还是母鸡类,都不能重写该方法。于是包含终态属性和终态方法的鸡类定义代码变成了下面这样:

//定义一个鸡类
public class Chicken { // 定义一个名称属性
public String name;
// 定义一个性别属性
public int sex; // 定义一个叫唤方法
public void call() {
System.out.println("半夜鸡叫");
} // 以下利用final修饰成员属性和成员方法
public final int MALE = 0; // 雄性
public final int FEMALE = 1; // 雌性 // 定义一个能否游泳的方法
public final boolean canSwim() {
return false;
}
}

对于外部来说,访问终态属性和终态方法的方式没有改变,仍然是以“实例名称.成员名称”的形式。下面是外部调用新鸡类的代码例子:

		// 创建一个鸡类的实例
Chicken chicken = new Chicken();
// Chicken类的MALE属性是个终属性,首次初始化后就不能再做修改
System.out.println("MALE="+chicken.MALE);
// Chicken类的FEMALE属性是个终属性,首次初始化后就不能再做修改
System.out.println("FEMALE="+chicken.FEMALE);
// Chicken类的canSwim方法是个终方法,子类不能重写该方法
System.out.println("Chicken canSwim="+chicken.canSwim());

  

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(五十三)关键字final的用法的更多相关文章

  1. Java开发笔记(十三)利用关系运算符比较大小

    前面在<Java开发笔记(九)赋值运算符及其演化>中提到,Java编程中的等号“=”表示赋值操作,并非数学上的等式涵义.Java通过等式符号“==”表示左右两边相等,对应数学的等号“=”: ...

  2. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  3. Java开发笔记(五十五)关键字static的用法

    前面介绍嵌套类的时候讲到了关键字static,用static修饰类,该类就变成了嵌套类.从嵌套类的用法可知,其它地方访问嵌套类之时,无需动态创建外层类的实例,直接创建嵌套类的实例就行.其实static ...

  4. Java开发笔记(五十八)简单接口及其实现

    前面介绍了抽象方法及抽象类的用法,看似解决了不确定行为的方法定义,既然叫唤动作允许声明为抽象方法,那么飞翔.游泳也能声明为抽象方法,并且鸡类涵盖的物种不够多,最好把这些行为动作扩展到鸟类这个群体,于是 ...

  5. Java开发笔记(五十九)Java8之后的扩展接口

    前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接 ...

  6. Java开发笔记(七十五)异常的处理:扔出与捕捉

    前面介绍的几种异常(不包含错误),编码的时候没认真看还发现不了,直到程序运行到特定的代码跑不下去了,程序员才会恍然大悟:原来这里的代码逻辑有问题.像这些在运行的时候才暴露出来的异常,又被称作“运行时异 ...

  7. Java开发笔记(二十三)数组工具Arrays

    数组作为一种组合形式的数据类型,必然要求提供一些处理数组的简便办法,包括数组比较.数组复制.数组排序等等.为此Java专门设计了Arrays工具,该工具包含了几个常用方法,方便程序员对数组进行加工操作 ...

  8. Java开发笔记(三十三)字符包装类型

    正如整型int有对应的包装整型Integer那样,字符型char也有对应的包装字符型Character.初始化字符包装变量也有三种方式,分别是:直接用等号赋值.调用包装类型的valueOf方法.使用关 ...

  9. Java开发笔记(四十五)成员属性与成员方法

    前面介绍了许多数据类型,除了基本类型如整型int.双精度型double.布尔型boolean之外,还有高级一些的如包装整型Integer.字符串类型String.本地日期类型LocalDate等等,那 ...

随机推荐

  1. php调用第三方接口

    方式一 $url = 'http://ip.taobao.com/service/getIpInfo.php?ip='.$realip;$data = file_get_contents(" ...

  2. Django ORM 知识概要

    相关命令 python3 manage.py makemigrations 根据模型生成相关迁移文件 python3 manage.py migrate 根据迁移文件,将表结构更新到数据库中,并在Dj ...

  3. python爬虫第四天

        昨天学到了正则表达式基础知识 :原子 今天开始学习第二个基础知识:元字符 元字符     就是正则表达式中含有特殊含义的一些字符 常见的元字符及含义   符号 含义 . 匹配除换行符以外 的任 ...

  4. emWin实现ATM机界面设计,含uCOS-III和FreeRTOS两个版本

    第1期:ATM机配套例子:V6-900_STemWin提高篇实验_ATM机(uCOS-III)V6-901_STemWin提高篇实验_ATM机(FreeRTOS) 例程下载地址:http://foru ...

  5. 基于LinkedList实现桶排序

    需要考虑以下问题: 1.桶的大小,这里我们可以根据输入的元素的个数来确定桶的大小. 2.怎么样确定当前元素进入哪一个桶,这里我们使用到的是通过一个哈希函数来进行计算. int index = (ele ...

  6. You need to use a Theme.AppCompat theme (or descendant) with this activity 问题解决

    You need to use a Theme.AppCompat theme (or descendant) with this activity 问题解决 问题代码 void initCommit ...

  7. [Swift]LeetCode600. 不含连续1的非负整数 | Non-negative Integers without Consecutive Ones

    Given a positive integer n, find the number of non-negativeintegers less than or equal to n, whose b ...

  8. 10.Git分支-分支管理(git branch命令)、分支开发工作流

    1.分支管理  git branch 不仅可以创建和删除分支,还可以做一些其他工作. 1.不带参数的 git branch ,得到本地仓库当前的分支列表.并且会显示,当期所在的分支,也就是HEAD所指 ...

  9. logback.xml sql语句输出

    在使用springBoot框架之后,日志配置文件变成了logback.xml,输出sql语句的方法为: <!-- 打印sql语句 --> <logger name="com ...

  10. IdentityServer4之Implicit(隐式许可) —— oidc-client-js前后端分离

    IdentityServer4之Implicit(隐式许可) —— oidc-client-js前后端分离 参考 官方文档:oidc-client-js:oidc-client是一个JavaScrip ...