final是java中常见的关键字之一,从字面意思来看,使用final修饰的内容通常都是确定好的内容,不需要再改变值。我们从final可以修饰的对象来了解final。
1.final修饰变量
final可以修饰成员变量和局部变量,但是两者用法有一些细微区别。
- final修饰成员变量
我们知道成员变量根据是否使用static修饰符修饰分为类变量和实例变量。
使用final修饰类变量时,只能在:
- 声明处
- 静态代码块
这两个地方对类变量进行初始化,其他任何地方对它进行赋值都是非法的。
使用final修饰实例变量时,只能在:
- 声明处
- 代码块
- 构造器中
- 这三个地方进行初始化,因为final类型的值初始化以后不可变,所以使用final修饰的实例变量无法提供对应的setter方法。
- final修饰局部变量
局部变量有三种场景:
- 代码块中
- 方法内部
- 方法的形参
用final修饰形参时,通过调用该方法时给这个参数赋值且后续不可变,修饰其他两种情况时,可以在声明处完成初始化工作,也可以在其他地方给这个变量进行赋值,但也只能赋值一次。
宏变量的概念
java中有一个概念叫做“宏变量”,就是说有一个变量的值在编译时就可以确定下来,编译器就会将代码中所有用到这个变量的地方直接替换成对应的值。
final一个重要的用途就是用来定义宏变量。
对于final修饰的变量,不管是成员变量还是局部变量,只要满足以下三个条件,这个变量就可以称为直接量,也就是一个宏变量:
(1)使用final修饰;
(2)在定义final时指定了初始值;
(3)在编译时可以确定下来。
可能大家会对第三条有疑问,什么情况才算是编译时可以确定下来。我们可以举个简单的例子:
package com.ljw.field;
/**
* Created by liujiawei on 2018/7/20.
*/
public class TestFinal {
public static void main(String[] args) {
final String s = "123";
final String s1 = "12" + "3";
final String s2 = "12" + String.valueOf(3);
System.out.println(s == s1);
System.out.println(s == s2);
}
}
我们可以看到,s,s1,s2三个变量的内容实际上都是“123”,但是我们从运行结果可以看到:
通过==判断进行比较,s和s1是相同的地址,s和s2比较的结果得到的是false,通过+连接符赋值和直接赋值是等价的,但是调用方法以后会这个变量就不会在编译时被确定下来,总结下来就是:
如果只是通过连接符进行连接,没有访问变量,也没有调用方法的话,这种变量同样可以在编译时就被确定下来。
2.final修饰方法
使用final修饰方法代表不希望这个方法被子类重写,但是方法依然可以被重载。
3.final修饰类
使用final修饰的类不能被其它类继承,所以final修饰的类都没有子类。
不可变类(immutable)
java中还有不可变类的说法,就是说一个类的实例变量创建以后不可以被改变,基本数据的包装类和String类都是不可变类。
在介绍String,StringBuilder和StringBuffer的时候,我们说过String是一个不可变类,因为不管是通过+操作符或者concat()对字符串进行拼接,每次返回的都是一个新的string对象,最主要的还是类本身对实例变量没有提供修改的setter方法。
参照这几个不可变类的规则,可以对不可变类进行简单的总结:
- 实例变量使用private 和 final进行修饰;
- 只提供getter方法;
- 通过构造器来对实例变量进行初始化;
- 有必要的话,可以重写Object类的equals()和hashCode()方法。
5.final总结
使用final关键字主要就是两个目的:设计和效率,比如方法不想被重写,类不想被继承,这就是处于设计的目的使用fianl,比如通过final定义宏变量,方法使用fianl也会使调用专为内嵌调用,这就是处于效率的考虑。