当前位置: 首页 > 知识库问答 >
问题:

Java中的“最终”关键词是如何工作的?(我仍然可以修改对象。)

米裕
2023-03-14

在Java中,我们使用final关键字和变量来指定其值不可更改。但我看到您可以更改类的构造函数/方法中的值。同样,如果变量是statice,那么这是一个编译错误。

代码如下:

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);
  }
}

上面的代码工作良好,没有错误。

现在将变量更改为static:

private static final List foo;

现在是编译错误。这个final是如何工作的?

共有3个答案

庾鸿飞
2023-03-14

Final关键字有多种使用方法

  • 不能对最终类进行子类化。
  • 子类不能重写final方法
  • 最终变量只能初始化一次

其他用法:

  • 当匿名内部类在方法主体中定义时,该方法范围内声明为final的所有变量都可以从内部类内访问

一个静态类变量将从JVM的一开始就存在,并且应该在类中初始化。如果执行此操作,将不会出现错误消息。

鲁博赡
2023-03-14

这是一个很受欢迎的面试问题。通过这些问题,面试官试图了解您对对象在构造函数、方法、类变量(静态变量)和实例变量方面的行为理解程度。

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'定义了一个构造函数,并给它一个'set foo'方法。

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

关于方法:一个方法可以被调用任意多次(甚至从来没有),编译器知道这一点。

方案1

private final List foo;  // 1

foo是一个实例变量。当我们创建test类对象时,实例变量foo将被复制到test类的对象中。如果我们在构造函数内部分配foo,那么编译器知道构造函数只会被调用一次,所以在构造函数内部分配它没有问题。如果我们在方法内部赋值foo,编译器知道一个方法可以被多次调用,这意味着该值必须被多次更改,这对于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

上面的修改-2来自您的问题。在上面的情况下,您不是在更改第一个引用的对象,而是在foo中添加允许的内容。如果您试图将new ArrayList()分配给foo引用变量,编译器会抱怨。
规则如果您初始化了final变量,则不能将其更改为引用其他对象。(本例中为arraylist)

final类不能被子类化
不能重写final方法。(此方法在超类中)
最终方法可以重写。(用语法的方式读这个。这个方法在一个子类中)

公羊喜
2023-03-14

始终允许初始化final变量。编译器确保您只能执行一次。

请注意,对final变量中存储的对象调用方法与final的语义无关。换句话说:final只是关于引用本身,而不是关于被引用对象的内容。

Java没有对象不变的概念;这是通过仔细地设计对象来实现的,是一个远非微不足道的努力。

 类似资料:
  • 问题内容: 在Java中,我们使用带有变量的关键字来指定其值不被更改。但是我看到你可以在类的构造函数/方法中更改值。同样,如果变量是,则为编译错误。 这是代码: 上面的代码工作正常,没有错误。 现在将变量更改为: 现在是编译错误。这真的如何运作? 问题答案: 你总是允许初始化一个变量。编译器确保你只能执行一次。 请注意,对存储在final变量中的对象的调用方法与的语义无关。换句话说:仅与引用本身有

  • 我试图理解关键字的用途,显然它允许我们对泛型进行反思。 然而,当我把它排除在外时,它的工作也一样好。有人想解释一下这什么时候会有实际的不同吗?

  • 问题内容: 让我们从一个简单的测试用例开始: 任何人都在乎猜测什么将作为输出打印(在底部显示,以免立即破坏惊喜)。 问题是: 为什么原始和包装的整数表现不同? 为什么反射访问与直接访问返回不同的结果? 最困扰我的人-为什么String表现得像原始的而不是像? 结果(java 1.5): 问题答案: 内联编译时常量(在javac编译时)。参见JLS,尤其是15.28定义了常量表达式,而13.4.9讨

  • 从https://projects.spring.io/spring-framework/,我有一个spring framework hellpworld程序。我删除了注释。然而,程序仍然可以像以前一样运行。为什么?这里的角色是什么?

  • 问题内容: 关键字如何使变量不可变?维基百科说没有。 问题答案: 在Java中,术语 final 是指引用,而不 可变 是指对象。将修饰符分配给引用意味着它不能更改为指向另一个对象,但是如果对象是可变的,则可以对其进行修改。 例如: 就像Wikipedia文章中提到的那样,如果您来自C ++,则必须分离出into 和不可变的概念。

  • 我正试图将eclipse中的java项目导出为可运行的jar,但由于某种原因,可运行的jar无法工作。如果双击可执行jar,它什么也不做。我尝试将所需库提取并打包到生成的JAR中。 所以我也尝试导出一些更简单的项目,那些工作很好。最大的区别是我的真实项目有文件:图像和xml文件。 在代码中引用它们,如下所示: