在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的不同上下文。
最终变量一个最终变量只能赋值一次。如果变量是引用,这意味着不能将该变量重新绑定到引用另一个对象。
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();
}
}
我只能回答你的问题,在这种情况下,你不能改变foo的参考值。你只需要在同一个引用中放入值,这就是为什么你可以在foo引用中添加值。这个问题发生在你不能很好地理解参考值和原始值之间的区别。引用值也是在堆内存中存储对象地址(即value)的值。
public static void main(String[] args)
{
Test t = new Test();
t.foo.add("bar"); // Modification-2
System.out.println("print - " + t.foo);
}
但是在这种情况下,您可以看到,如果尝试编写以下代码,将会出现编译时错误。
public static void main(String[] args)
{
Main main = new Main();
main.foo=new ArrayList<>();//Cannot assign a value to final variable 'foo'
System.out.println("print - " + main.foo);
}
final是Java中限制用户的保留关键字,它可以应用于成员变量、方法、类和局部变量。Final变量通常在Java中使用static关键字声明,并被视为常量。例如:
public static final String hello = "Hello";
当我们在变量声明中使用final关键字时,存储在该变量中的值不能在后面更改。
例如:
public class ClassDemo {
private final int var1 = 3;
public ClassDemo() {
...
}
}
注意:声明为final的类不能扩展或继承(也就是说,不能有超类的子类)。同样值得注意的是,声明为final的方法不能被子类重写。
使用final关键字的好处将在本文中讨论。
我想在这里写一个更新的和深入的回答。
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缓存,并从主存中进行时间到时间的加载。所以这也是一种性能提升。
因此,在上述三种情况下,当我们没有在可以使用的地方指定最后的关键字时,我们不需要担心,编译器优化会为我们做这件事。编译器优化还为我们做了很多其他的事情。:)