说来惭愧,我已经很久没有更新博客了。原因大概有二:其一是 主观上 笔者近来有些浮躁,难以静下心了进行总结与反思;其二则是 客观上 近几个月从公司的项目到个人的项目都非常的琐碎繁杂,已经完全透支了笔者的写作热情。
好在最近的历练的确使我有了方方面面的提升,以后会尽可能多地把技术分享带给大家。话不多说,本篇讨论的只有 Java 中的一个关键字——final。
final,顾名思义,是为 最终的、无法改变的。final可能被使用的情况有三种:数据、方法 和 类,下面笔者将分别说明这三种情况的差异。
final修饰数据
数据分为两种类型:基本类型(boolean、byte、char、short、int、long、float、double)和对象。当final用于修饰基本类型时,代表其数值自初始化赋值后恒定不变。当final用于修饰对象引用时,代表其引用无法被改变。 即当x引用为一个Object时(final Object x = new Object();),不能再将x改为指向另一个对象。
值得注意的是,当final修饰对象引用时,虽然无法将引用“重定向”,但仍可以修改当前引用对象的成员变量,如使用set()方法等。另外,在Java中 数组也是对象,当final用于修饰数组引用时,数组内部元素仍可以被改变。
final修饰方法
当final被用于修饰一个方法时,代表该方法是无法被 重写(override)的。任何被private修饰的方法,都隐式地指定为final。
我们都知道可以直接使用子类对象来代替父类对象作为参数传递,而不用担心发生类型转换的问题,这被称为 向上转型。实际上,在程序运行时,计算机会根据参数的类型选择相应的方法,如下。
1 | public void test(Father object){ |
当程序员向test方法中传入Son对象时,计算机不会运行Father类中的doSomething()方法,而是 运行Son类中的doSomething()方法。因此,当运行一个被final修饰的方法时,计算机不需要判断当前对象的类型,这会带来微小的性能提升。但是,开发者往往无法预计当前编写的类在之后的漫长时间中会起到何种作用,所以不要单纯为了微小的性能提升而使用final修饰方法,除非开发者 明确禁止此方法被覆盖。
final修饰方法还有另外一个情况——用于修饰方法参数,这在 匿名内部类 中较为常见。这代表开发者无法在方法中修改此参数引用指向的对象。Java中每个类文件被编译成单独的class文件,内部类和外部类处于同样的等级。如果内部类方法中可以使用并修改外部类变量,则 内部类必须始终持有外部类的引用,这将可能导致外部类无法被回收掉。而Java这种规则,实际上是将参数拷贝一份拿来使用,不会影响外部类的垃圾回收工作。
final修饰类
当某个类被定义为final时,代表该类不可被继承。由于类不可被继承,自然类中的任何方法都不可被重写(override)。也就是说,final类中的所有方法都被隐式地指定为final。