final关键字的含义
final是java关键字中的一个,可以用来声明类、成员变量、成员方法。表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,以及用来定义常量。
final声明的成员变量
final声明的成员变量,只允许一次赋值。编译器会检查会保证final声明的成员变量不会被第二次赋值。以下情况都会报compile错误:
//Case 1: 构造函数内对final成员变量多次赋值。
private final String fValue;
public FinalTest() {
fValue="test";
fValue="test1"; //Compile Error: Variable 'fValue' might already have been assigned to
}
//Case 2: final成员变量未被初始化
public class FinalTest {
private final String fValue;//Compile Error:Variable 'fValue' might not have been intialized
}
//Case 3: 尝试final成员变量在非构造函数的方法中赋值
public class FinalTest {
private final String fValue;
public FinalTest(String fValue) {
this.fValue = fValue;
}
private void setFvalue(String newValue){
fValue=newValue; //Compile Error: Cannot assign value to a final Variable 'fValue'
}
}
final声明的方法参数
方法参数可以分为两种:
对于基本类型,声明为final的方法参数,其值在方法调用期间不可以被改动;
对于引用类型,声明为final的方法参数,其“引用”在方法调用期间不可以被改动,但是要特别注意,引用所指向的对象本身的成员变量是可以变的:
![](https://img.haomeiwen.com/i9625857/bec62d5ecfd9d208.png)
?为什么要设计final可以声明方法参数,在众多答案中,我认同以下说法:
当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量。
java 源码中也有这种使用方式如:
private void registerInitialReferences( final ORB orb )
{
// Register the Dynamic Any factory
Closure closure = new Closure() {
public java.lang.Object evaluate() {
return new DynAnyFactoryImpl( orb ) ;
}
} ;
Closure future = ClosureFactory.makeFuture( closure ) ;
orb.getLocalResolver().register( ORBConstants.DYN_ANY_FACTORY_NAME,
future ) ;
}
final声明的本地变量
方法内或者代码块内声明的变量为本地变量。
同方法参数中提到的:
对于基本类型,声明为final的方法参数,其值在变量生命周期内不可以被改动;
对于引用类型,声明为final的方法参数,其“引用”在变量生命周期内不可以被改动,但是要特别注意,引用所指向的对象本身的成员变量是可以变的:
final方法
特别简单,就是不允许子类重写该方法。
final类
不允许继承该类。
java中有很多final类如String, Integer,Double,Long, Math, Matcher等等,都是其功能独立且完整,不需要进一步扩展的类。
final的其他用法
When an anonymous inner class is defined within the body of a method, all variables declared final in the scope of that method are accessible from within the inner class
为什么要设计final关键字
- 安全的角度
- 性能的角度
final的其他知识点
- final/finally/finalize区别
final 用来保证重点代码或属性不会被修改。
finally 保证重点代码一定会被执行,比如在try,catch,finally里关闭jdbc connection等。
finalize 在对象回收前会调用,一般在这个方法里做一些资源的回收。不推荐使用。 更多详细说明参见<link href="https://www.jianshu.com/writer#/notebooks/32522137/notes/38577312">Java高效开发实例2 - 避免使用finalize()</link>
- final的类是否就是immutable的
在一些博文里是这样介绍final的:在java中,final的含义在不同的场景下有细微的差别,但总体上来说,它指的是“这是不可变的”。
那么final就等于不可变么?
答案为:并不是。
- final声明的类,指它不能被继承,与该类的对象是否可变没有一点关系。
- final声明的变量,要看它是基本类型,还是引用类型。如果是基本类型,可以说该变量的值在生命周期内是不可变的。而如果是引用类型,final只保证变量的引用不变,引用指向的对象的状态是可以改变的。
- 不可变指是的对象的状态一旦初始化,就不会变了。比如String a = new String("test"); 因为String 对象并没有对外提供可以修改其实际字符串值的接口,另外它也不允许被继承,避免了子类可以修改该属性的可能。
所以要要自定义一个不可变的类,需要遵循以下原则(参见effective java如何定义不可变类):
- 将类本身声明为final,避免通过继承来修改类的属性。
- 将类的属性声明为private final,并且 不要生成setter方法。
- 通常构造对象时,成员变量使用尝试拷贝来初始化,而不是直接赋值。这是一种防御措施,因为你无法确定输入对象不被其他人修改。
- 如果确实需要实现getter方法,或者其他可能会返回内部状态的方法使用copy-on-write原则,创建私有的copy.
-
final和static的区别
- final可以声明类、成员变量、本地变量、方法参数、成员方法; static只能声明内部静态类、成员变量、成员方法,静态块,静态导包(import static some.package);
- final修饰变量是指在对象的生命周期内不可变。 static的含义是多个对象内共享。是完全不同的概念。
-
匿名内部类中使用的外部局部变量为什么只能是final变量?
java的内部类具体可以分为四种,具体分类可以参考文档:
http://www.cnblogs.com/shen-hua/p/5440285.html
只有匿名内部类使用外部局部亦是时需要是final变量,否则会有compile error如下:
![](https://img.haomeiwen.com/i9625857/27c3be56a114b697.png)
原因:
匿名内部类在使用外部类的成员变量时,实际上是次该成员变量作为匿名内部类构造函数的参数传为内部类的。
在内部类的生命周期内,需要确保该变量不会被修改(基础类型的变量值不会被修改,引用类型的引用不会被修改)。因此必须是final的。
- java字节码中可以看出哪些类、变量或者方法是final的么?
对于final类、成员变量、或者方法,从字节码文件的access_flags里可以找到:
ACC_FINAL 0x0010 //类:Declared final; no subclasses allowed.
//成员方法:Declared final; cannot be overwritten.
//成员变量:Declared final; can be only assigned in constructer.
对于本地变量,由compile保证本地变量一旦被赋值,这个引用就不会变。因此运行时不需要对final的本地变量特殊处理。字节码中也不需要对本地变量进行特殊标识。
参考资源
网友评论