1. final 关键字
final关键字主要的含义就是“无法改变”。final可以用于数据、方法和类。
1. 数据
final作用于数据上表示数据本身不可以改变。1
2
3
4
5
6
7
8final int a = 10;
a = 11; // 错误
static final int C = 2; // 编译期常量
final List<String> list = new ArrayList<String>();
list.add("a"); // 正确
list.add("b"); // 正确
list = new ArrayList<String>(); // 错误,list不能指向其他对象
如上,final作用于引用表示引用值本身不能变,即不能指向其他对象。
final修饰参数
在方法体的匿名内部类中,参数需要用final修饰。1
2
3
4
5
6
7
8
9
10
11interface adder{void addXYZ();}
public class outer{
public adder getAdder(final int x){
final int y = 10;
return new adder(){
int z = 10;
void addXYZ(){return x + y + z;}
}
}
}
因为方法体中的变量是分配在栈上的,生命周期只在方法体内,而内部类对象分配在堆上。如果内部类如果保存外部类的引用,会导致错误。所以为了解决这个问题,java内部会拷贝外部变量。
1 | interface adder{void addXYZ();} |
为了让内外含义保持一致,方法体内的局部变量x,y
需要用final修饰。
引申:为什么java不能实现和其他语言一样的函数闭包呢?
2. 方法
final作用于方法有两个方面。
- 不能在继承类中重写覆盖
- 内联调用
内联调用是为了减少函数调用的开销(压栈、跳转、出栈等),在调用的地方直接用函数代码代替。不过当方法很大时,程序代码会膨胀,性能没有提高。
现在很多虚拟机会检测这种情况,进行优化。所以final主要是为了禁止继承类覆盖。
3. 类
final 类不能被继承。
2. 初始化和类的加载
1 | class Animal { |
如上例所示,步骤如下:
- new Dog()触发加载Dog类时,发现继承了基类Animal,于是继续加载基类Animal
- 基类静态初始化,进而子类Dog静态初始化
- 在堆上分配内存,清空为0(默认初始化)
- 基类执行定义初始化和构造器
- 子类执行定义初始化和构造器