在Java中,我们使用带有变量的final关键字来指定其不被更改的值。
但我看到你可以改变类的构造函数/方法的值。同样,如果变量是静态的,那么这是一个编译错误。
代码如下:
import java.util.ArrayList;
import java.util.List;
class Test {
private final List foo;
public Test()
{
foo = new ArrayList();
foo.add("foo"); // Modification-1
}
public static void main(String[] args)
{
Test t = new Test();
t.foo.add("bar"); // Modification-2
System.out.println("print - " + t.foo);
}
}
以上代码工作正常,没有错误。
现在将变量更改为静态:
private static final List foo;
现在它是一个编译错误。期末考试是怎么进行的呢?
我想在这里写一个更新的和深入的回答。
Final关键字可以在几个地方使用。
类
final类意味着没有其他类可以扩展这个final类。当Java运行时(JRE)知道一个对象引用的类型是final类(比如F)时,它就知道该引用的值只能是F类型。
Ex:
F myF;
myF = new F(); //ok
myF = someOther; //someOther cannot be in type of a child class of F.
//because F cannot be extended.
因此,当它执行该对象的任何方法时,该方法不需要在运行时使用虚拟表进行解析。即不能应用运行时多态性。所以运行时不会担心这个。这意味着它节省了处理时间,从而提高了性能。
方法
任何类的final方法意味着任何扩展该类的子类都不能重写该final方法。因此,此场景中的运行时行为也与前面提到的类的行为完全相同。
字段,局部变量,方法参数
如果将上述任何类型指定为final,则意味着该值已经最终确定,因此不能更改该值。
Ex:
对于字段,为本地参数
final FinalClass fc = someFC; //need to assign straight away. otherwise compile error.
final FinalClass fc; //compile error, need assignment (initialization inside a constructor Ok, constructor can be called only once)
final FinalClass fc = new FinalClass(); //ok
fc = someOtherFC; //compile error
fc.someMethod(); //no problem
someOtherFC.someMethod(); //no problem
对于方法参数
void someMethod(final String s){
s = someOtherString; //compile error
}
这仅仅意味着不能更改最终参考值的值。也就是说,只允许一次初始化。在这个场景中,在运行时,由于JRE知道值不能更改,它将所有这些最终确定的值(最终引用的值)加载到L1缓存中。因为它不需要一次又一次地从主存中加载。否则它会加载到L2缓存,并从主存中进行时间到时间的加载。所以这也是一种性能提升。
因此,在上述三种情况下,当我们没有在可以使用的地方指定最后的关键字时,我们不需要担心,编译器优化会为我们做这件事。编译器优化还为我们做了很多其他的事情。:)
以下是使用final的不同上下文。
最终变量一个最终变量只能赋值一次。如果变量是引用,这意味着不能将该变量重新绑定到引用另一个对象。
class Main {
public static void main(String args[]){
final int i = 20;
i = 30; //Compiler Error:cannot assign a value to final variable i twice
}
}
Final变量可以稍后赋值(声明时不强制赋值),但只能赋一次。
Final类不能扩展(继承)
final class Base { }
class Derived extends Base { } //Compiler Error:cannot inherit from final Base
public class Main {
public static void main(String args[]) {
}
}
Final方法不能被子类覆盖。
//Error in following program as we are trying to override a final method.
class Base {
public final void show() {
System.out.println("Base::show() called");
}
}
class Derived extends Base {
public void show() { //Compiler Error: show() in Derived cannot override
System.out.println("Derived::show() called");
}
}
public class Main {
public static void main(String[] args) {
Base b = new Derived();;
b.show();
}
}
“最终变量只能赋值一次”
*反思* -“哇哦,等等,拿着我的啤酒”。
在两种情况下冻结final字段:
构造函数结束。
当反射设置字段的值时。(想要多少次就有多少次)
让我们犯法吧
public class HoldMyBeer
{
final int notSoFinal;
public HoldMyBeer()
{
notSoFinal = 1;
}
static void holdIt(HoldMyBeer beer, int yetAnotherFinalValue) throws Exception
{
Class<HoldMyBeer> cl = HoldMyBeer.class;
Field field = cl.getDeclaredField("notSoFinal");
field.setAccessible(true);
field.set(beer, yetAnotherFinalValue);
}
public static void main(String[] args) throws Exception
{
HoldMyBeer beer = new HoldMyBeer();
System.out.println(beer.notSoFinal);
holdIt(beer, 50);
System.out.println(beer.notSoFinal);
holdIt(beer, 100);
System.out.println(beer.notSoFinal);
holdIt(beer, 666);
System.out.println(beer.notSoFinal);
holdIt(beer, 8888);
System.out.println(beer.notSoFinal);
}
}
输出:
1
50
100
666
8888
“final”字段已经被分配了5个不同的“final”值(注意引号)。它可以一直被赋予不同的值……
为什么?因为反射就像chucknorris,如果它想改变一个初始化的final字段的值,它就会这样做。有人说他自己就是那个将新值推入堆栈的人:
Code:
7: astore_1
11: aload_1
12: getfield
18: aload_1
19: bipush 50 //wait what
27: aload_1
28: getfield
34: aload_1
35: bipush 100 //come on...
43: aload_1
44: getfield
50: aload_1
51: sipush 666 //...you were supposed to be final...
60: aload_1
61: getfield
67: aload_1
68: sipush 8888 //ok i'm out whatever dude
77: aload_1
78: getfield