前面介绍了多态的相关用法,可以看到一个子类从父类继承之后,便能假借父类的名义到处晃悠。这种机制在正常情况之下没啥问题,但有时为了预防意外发生,往往只接受当事人来处理,不希望它的儿子乃至孙子来瞎掺和。可是犹记得几种开放性修饰符,只能控制某个实体能否被外部访问,从未听说可决定某个类能否被其它类所继承。
毫无疑问,是否开放与能否继承是两种不同的概念,不管是被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. excel写入操作

    字典列表类型数据写入excel. #导入xlwt库 import xlwt import os # 步骤1:获取excel文件的绝对路径 dirPath = os.path.join(os.getcw ...

  2. Java后期拓展(一)之Redis

    1.NoSQL数据库简介 2.Redis的介绍及安装启动 3.Redis的五大数据类型 4.Redis的相关配置 5.Redis的Java客户端Jedis 6.Redis的事务 7.Redis的持久化 ...

  3. http://www.layui.com/doc/modules/laydate.html实时通信\日期、==插件

    8520**ali chengyouli http://www.layui.com/doc/modules/laydate.html实时通信\日期.==插件

  4. [SQL]LeetCode262.行程和用户 | Trips and Users

    SQL架构 Create table If Not Exists Trips (Id )) Create table If Not Exists Users (Users_Id ), Role ENU ...

  5. [Swift]LeetCode673. 最长递增子序列的个数 | Number of Longest Increasing Subsequence

    Given an unsorted array of integers, find the number of longest increasing subsequence. Example 1: I ...

  6. [Swift]LeetCode722. 删除注释 | Remove Comments

    Given a C++ program, remove comments from it. The program source is an array where source[i] is the  ...

  7. [Swift]LeetCode973. 最接近原点的 K 个点 | K Closest Points to Origin

    We have a list of points on the plane.  Find the K closest points to the origin (0, 0). (Here, the d ...

  8. MiniProfiler监控Asp.Net MVC5和EF性能

    1. 安装依赖包 在web项目打开nuget包管理器搜索 MiniProfiler.Mvc5和MiniProfiler.EF6安装. 2. 在Global.asax中添加配置代码 protected ...

  9. linux终端文本编辑神器vi的使用

    vi —— 终端中的编辑器 目标 vi 简介 打开和新建文件 三种工作模式 常用命令 分屏命令 常用命令速查图 01. vi 简介 1.1 学习 vi 的目的 在工作中,要对 服务器 上的文件进行 简 ...

  10. 【T-SQL】系列文章全文目录(2017-06-26更新)

    本系列[T-SQL]主要是针对T-SQL的总结. SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]0 ...