这是一个最喜欢的面试问题。通过这个问题,面试官试图了解你对于构造函数、方法、类变量(静态变量)和实例变量的行为理解程度。
现在,面试官们还在问另一个最喜欢的问题——什么是Java 1.8中的“有效final”。
最后我会解释一下Java 1.8中的effectively final
。
import java.util.ArrayList;
import java.util.List;
class Test {
private final List foo;
public Test() {
foo = new ArrayList();
foo.add("foo");
}
public void setFoo(List foo) {
}
}
在以上示例中,我们为“Test”定义了一个构造函数并提供了一个“setFoo”方法。
关于构造函数: 构造函数只能通过使用“new”关键字每个对象创建时调用一次。您不能多次调用构造函数,因为构造函数不是为此而设计的。
关于方法: 方法可以被调用任意次数(甚至从未),编译器也知道这一点。
场景1
private final List foo; // 1
foo
是一个实例变量。当我们创建
Test
类的对象时,实例变量
foo
将被复制到
Test
类的对象内部。如果我们在构造函数内赋值
foo
,那么编译器知道构造函数只会被调用一次,所以在构造函数内赋值没有问题。
如果我们在方法内赋值
foo
,那么编译器知道方法可以被多次调用,这意味着值将不得不被多次更改,而
final
变量不允许这样。因此编译器决定使用构造函数!
您只能为final
变量赋值一次。
场景2
private static final List foo = new ArrayList();
foo
现在是一个
静态变量。当我们创建
Test
类的实例时,
foo
不会被复制到对象中,因为
foo
是静态的。现在
foo
不是每个对象的独立属性,而是
Test
类的一个属性。但是,
foo
可以被多个对象看到,并且如果使用
new
关键字创建每个对象(这将最终调用
Test
构造函数),则会在多个对象创建时更改值(请记住
static foo
不会在每个对象中复制,而是在多个对象之间共享)。
场景3
t.foo.add("bar")
以上的Modification-2
来自您的问题。在上述情况中,您没有更改第一个引用对象,而是在foo
中添加内容是可以的。如果您尝试将new ArrayList()
分配给foo
引用变量,则编译器会抱怨。
规则如果您已经初始化了一个final
变量,则不能将其更改为引用其他对象。(在这种情况下是ArrayList
)
final类不能被子类化
final方法不能被覆盖。(此方法位于超类中)
final方法可以覆盖。 (以语法方式阅读。此方法位于子类中)
现在让我们看看在Java 1.8中什么是有效的final?
public class EffectivelyFinalDemo {
public void process() {
int thisValueIsFinalWithoutFinalKeyword = 10;
thisValueIsFinalWithoutFinalKeyword = getNewValue();
class MethodLocalClass {
public void innerMethod() {
System.out.println(thisValueIsFinalWithoutFinalKeyword);
}
}
}
private int getNewValue() {
return 0;
}
}
如果您没有使用final关键字,那么在Java 1.7或<1.8中上述程序将会抛出错误。实际上,"effectively final"是方法局部内部类的一部分。我知道您很少会在方法本地类中使用这种"effectively final",但是为了面试,我们必须要做好准备。