在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的初始化,所以条件是满足的。该语句执行foo的单独初始化。注意final != immutable,它只意味着引用不能改变。

foo = new ArrayList();

当你将foo声明为static final时,变量必须在类加载时初始化,并且不能依赖实例化(也就是调用构造函数)来初始化foo,因为静态字段必须在没有类实例的情况下可用。不能保证在使用静态字段之前已经调用了构造函数。

当你在静态final场景下执行你的方法时,Test类在实例化t之前被加载,此时foo没有实例化,这意味着它还没有初始化,所以foo被设置为所有对象的默认值,即null。此时,我假设您的代码在尝试向列表中添加项时抛出NullPointerException。

其他回答

当你让它成为静态final时,它应该在静态初始化块中初始化

    private static final List foo;

    static {
        foo = new ArrayList();
    }

    public Test()
    {
//      foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }

这是一个很常见的面试问题。通过这些问题,面试官试图了解你对对象行为的理解程度,包括构造函数、方法、类变量(静态变量)和实例变量。 如今,面试官们会问另一个最受欢迎的问题:java 1.8的最终版本是什么?我将在最后在java 1.8中解释这个有效的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 void setFoo(List foo) {
       //this.foo = foo; Results in compile time error.
    }
}

在上面的例子中,我们为'Test'定义了一个构造函数,并给了它一个'setFoo'方法。

关于构造函数:通过使用new关键字,每次创建对象只能调用构造函数一次。不能多次调用构造函数,因为构造函数不是这样设计的。

关于方法:一个方法可以被你想调用多少次就调用多少次(甚至从来没有调用过),而且编译器知道它。

场景1

private final List foo;  // 1

foo is an instance variable. When we create Test class object then the instance variable foo, will be copied inside the object of Test class. If we assign foo inside the constructor, then the compiler knows that the constructor will be invoked only once, so there is no problem assigning it inside the constructor. If we assign foo inside a method, the compiler knows that a method can be called multiple times, which means the value will have to be changed multiple times, which is not allowed for a final variable. So the compiler decides constructor is good choice! You can assign a value to a final variable only one time.

场景2

private static final List foo = new ArrayList();

foo is now a static variable. When we create an instance of Test class, foo will not be copied to the object because foo is static. Now foo is not an independent property of each object. This is a property of Test class. But foo can be seen by multiple objects and if every object which is created by using the new keyword which will ultimately invoke the Test constructor which changes the value at the time of multiple object creation (Remember static foo is not copied in every object, but is shared between multiple objects.)

场景3

t.foo.add("bar"); // Modification-2

以上修改-2来自你的问题。在上面的例子中,你没有改变第一个被引用的对象,但是你在foo中添加了允许的内容。如果你试图给foo引用变量赋值一个新的ArrayList(),编译器会报错。 如果你已经初始化了一个final变量,那么你不能改变它来引用一个不同的对象。(在本例中是ArrayList)

Final类不能被子类化 Final方法不能被覆盖。(此方法在超类中) Final方法可以被重写。(请按语法方式阅读。这个方法在一个子类中)

现在让我们看看在java 1.8中什么是有效的最终版本?

public class EffectivelyFinalDemo { //compile code with java 1.8
    public void process() {
        int thisValueIsFinalWithoutFinalKeyword = 10; //variable is effectively final
        
        //to work without final keyword you should not reassign value to above variable like given below 
        thisValueIsFinalWithoutFinalKeyword = getNewValue(); // delete this line when I tell you.
        
        class MethodLocalClass {
            public void innerMethod() {
                //below line is now showing compiler error like give below
                //Local variable thisValueIsFinalWithoutFinalKeyword defined in an enclosing scope must be final or effectively final
                System.out.println(thisValueIsFinalWithoutFinalKeyword); //on this line only final variables are allowed because this is method local class
                // if you want to test effectively final is working without final keyword then delete line which I told you to delete in above program.  
            }
        }
    }

    private int getNewValue() {
        return 0;
    }
}

如果你没有使用final关键字,上面的程序将在java 1.7或<1.8中抛出错误。effective final是方法局部内部类的一部分。我知道你很少在本地类中使用这样有效的final,但是对于面试,我们必须做好准备。

java中的final关键字用于限制用户。java final关键字可以在许多上下文中使用。Final可以是:

变量 方法 类

final关键字可以与变量一起应用,没有值的final变量称为空白final变量或未初始化final变量。它只能在构造函数中初始化。空白的final变量也可以是静态的,它只会在静态块中初始化。

Java final变量:

如果你让任何变量作为final,你不能改变final变量的值(它将是常量)。

final变量示例

有一个最终变量的速度限制,我们将改变这个变量的值,但它不能改变,因为最终变量一旦赋值就永远不能改变。

class Bike9{  
    final int speedlimit=90;//final variable  
    void run(){  
        speedlimit=400;  // this will make error
    }  

    public static void main(String args[]){  
    Bike9 obj=new  Bike9();  
    obj.run();  
    }  
}//end of class  

Java最终类:

如果将任何类设置为final类,则不能扩展它。

final类示例

final class Bike{}  

class Honda1 extends Bike{    //cannot inherit from final Bike,this will make error
  void run(){
      System.out.println("running safely with 100kmph");
   }  

  public static void main(String args[]){  
      Honda1 honda= new Honda();  
      honda.run();  
      }  
  }  

Java final方法:

如果您将任何方法设置为final,则不能重写它。

final方法示例 (Honda中的run()不能覆盖Bike中的run())

class Bike{  
  final void run(){System.out.println("running");}  
}  

class Honda extends Bike{  
   void run(){System.out.println("running safely with 100kmph");}  

   public static void main(String args[]){  
   Honda honda= new Honda();  
   honda.run();  
   }  
}  

共享: http://www.javatpoint.com/final-keyword

这是一个非常好的面试问题。有时他们甚至会问你final对象和immutable对象的区别是什么。

1)当有人提到一个最终对象时,这意味着引用不能改变,但它的状态(实例变量)可以改变。

2)不可变对象的状态不能改变,但其引用可以改变。 例:

    String x = new String("abc"); 
    x = "BCG";

可以修改Ref变量x指向另一个字符串,但是不能修改abc的值。

3)实例变量(非静态字段)在调用构造函数时初始化。所以你可以在构造函数中初始化变量的值。

4)“但是我看到你可以改变类的构造函数/方法的值”。—您不能在方法中更改它。

5)静态变量在类加载过程中初始化。所以你不能在构造函数内部初始化,它甚至必须在构造函数之前完成。因此,需要在声明过程中为静态变量赋值。

如果你让foo是静态的,你必须在类构造函数中初始化它(或者你定义它的内联),就像下面的例子。

类构造函数(不是实例):

private static final List foo;

static
{
   foo = new ArrayList();
}

内联:

private static final List foo = new ArrayList();

这里的问题不是最终修饰符如何工作,而是静态修饰符如何工作。

final修饰符强制在调用构造函数完成之前对引用进行初始化(即必须在构造函数中初始化它)。

当你内联初始化一个属性时,它会在你为构造函数定义的代码运行之前初始化,所以你会得到以下结果:

如果foo是静态的,foo = new ArrayList()将在你为你的类定义的静态{}构造函数执行之前执行 如果foo不是静态的,foo = new ArrayList()将在运行构造函数之前执行

如果不内联初始化属性,final修饰符强制初始化它,并且必须在构造函数中初始化。如果你也有一个静态修饰符,你必须初始化属性的构造函数是类的初始化块:static{}。

您在代码中得到的错误来自于这样一个事实,即静态{}在加载类时运行,在您实例化该类的对象之前。因此,在创建类时,你还没有初始化foo。

可以将静态{}块视为Class类型对象的构造函数。在这里必须初始化静态final类属性(如果不是内联完成的话)。

注:

final修饰符仅对基本类型和引用保证const性。

当你声明一个final对象时,你得到的是该对象的最终引用,但对象本身不是常量。

当你声明一个final属性时,你真正实现的是,一旦你声明了一个特定目的的对象(就像你已经声明的final List),这个对象将被用于该目的:你不能将List foo更改为另一个List,但你仍然可以通过添加/删除项来改变你的List(你正在使用的List将是相同的,只是它的内容被改变了)。