45

根据教程

修饰符与修饰符static结合使用final,也用于定义常量。修饰符表示该final字段的值不能改变。

只有当所涉及的类型是原始的时,我才会同意这一点。对于引用类型,例如Point2D位置属性不存在的类的实例final(即,我们可以更改其位置),此类变量的属性public static final Point2D A = new Point2D(x,y);仍然可以更改。这是真的?

4

15 回答 15

67

是的,它可以改变。只有引用不能更改,但其内部字段可以。以下代码显示了它:

public class Final {
    static final Point p = new Point();
    public static void main(String[] args) {
        p = new Point(); // Fails
        p.b = 10; // OK
        p.a = 20; // Fails
    }
}

class Point {
    static final int a = 10;
    static int b = 20;
}

Groovy(另一种JVM语言)有一个名为@Immutable的注解,它会阻止对象在构造后更改为内部状态。

于 2013-01-16T12:42:54.850 回答
16

没错,还是可以改的。在这种情况下,“静态最终”是指引用本身,不能更改。但是,如果它所引用的对象是可变的,那么它所引用的对象就可以改变。

一个不可变的对象,例如一个字符串,将是一个常量。

于 2013-01-16T12:41:34.170 回答
10
public static final Point2D A = new Point2D(x,y);

这里的引用 A最终的,而不是类中的值Point2D

在定义 A static final 后你不能这样做:

//somewhere else in code
A = new Point2D(x1,y1);
于 2013-01-16T12:41:41.813 回答
6

public static final Point2D A = new Point2D(x,y); 仍然可以更改。这是真的?

A 的引用无法更改,但如果属性不是最终的,则可以更改其真实关联对象的值。

class Point2D{
  private int x;
  private int y;
  <getter & setter>
} 
class Constant{
  Public static final Point2D A = new Point2D(1,2);
  public static void main(String[] args){
     A.setX(10); // this will work 
     A = new Point2D(10,20);// this will not work
  }
}

如果Point2D's属性是最终的,那么Point2D class将是immutable.

或者您可以发送可克隆对象。

喜欢 -

 private static final Point2D A = new Point2D(x,y);
 public static getA(){
     return A.clone();
 }
于 2013-01-16T12:41:30.750 回答
6

这是真的。final修饰符不是以任何可能的方式传递的。

这只意味着参考不会改变。引用的内容(对象的字段)可能会随着时间而改变。

于 2013-01-16T12:41:51.110 回答
2

对这一点的引用(A在你的情况下)不能改变。只有对象的状态可以改变。所以你不能创建一个新的Point2D并将它分配给变量。

于 2013-01-16T12:41:18.867 回答
2

只有引用是最终的,被引用的对象是可以改变的(除非它是一个不可变的对象,比如 Integer 之类的)。所以是的,它仅对于给定的“常数”值是常数。

于 2013-01-16T12:42:23.073 回答
1

你可以在这里找到一个非常相似的问题,并有很好的解释:

HERE: 为什么可以修改最终对象?

顺便说一句,我开始考虑反射机制....

理论上......我相信你可以获得拥有类实例..然后获取类成员,找到当前实例并检查它是否是最终的......

我没有检查它,我什至不知道它是否可能 - 这只是一个想法(也许?)

public class Final {
    static final Point p = new Point();

    public static void main(String[] args) throws MyImmutableException {
        p = new Point(); // Fails
        p.setB(10); // OK
        p.setA(20); // Fails - throws MyImmutableException
    }
}       

public class Point() {
    int a = 10;
    int b = 20;

    public setA(int a) {
        this.a = a;
    }
    public setB(int b) throws MyImmutableException {
        detectIsFinal()
        this.b = b;
    }

    private void detectIsFinal() throws MyImmutableException {
        int mod = this.getClass().getModifiers()
        if (Modifier.isFinal(mod)) {
            throw new MyImmutableException();
        }
    }   
}

public class MyImmutableException extends Exception { 
    public MyImmutableException() {super(); }
}

我在想你还能做什么......再次它只是一个!!!!!!!!! 伪代码!!!我不确定它是否会起作用:P

也许具有注释知识的人会受到启发并使其发挥作用。不幸的是,这周我没有更多的时间。也许稍后我会尝试制作 POC。

public class Final {
    @Immutable
    static final Point p = new Point();

    public static void main(String[] args)  {
        p = new Point(); // Fails
        p.setB(10); // Fails
        p.setA(20); // OK
    }
}       

public class Point() {
    int a = 10;
    @ImmutableOnThisInstance
    int b = 20;

    @DetectIsImmutable
    public setA(int a) {            
        this.a = a;
    }

    @DetectIsImmutable
    public setB(int b) {
        this.b = b;
    }      
}


class Immutable {
    ?????????????????
}

class DetectIsImmutable {
    /**
     * Just a pseudocode - I don't know how to work with ANNOTATIONS :) :) :)
     * SORRY :)
     * @throws MyImmutableException
     */
    private void detectMethod() throws MyImmutableException {
        Immutable instance = CLASS_THAT_IS_ANNOTATED.getClass().getAnnotation(Immutable.class)
        if (instance != null) {
            // get parent Method invocation name (from stacktrace list)
            String methodName = .............;
            if (methodName.startsWith("set")) {
                // check id we have variable with this settername
                String fieldName = ...; // cut "set" get rest of it, make first letterSmall 
                // find this field in object fields
                Field f = this.getClass().getDeclaredField(fieldName);
                if (f.getAnnotation(ImmutableOnThisInstance.class) != null) {
                    throw MyImmutableException();
                }
            }
        }
    }           

}
于 2013-01-22T19:35:06.010 回答
1

是的。

当然,您也可以稍后更改 final 字段的值,如别处所述

于 2013-01-16T18:50:39.247 回答
0

正如其他已经提到的,它是对您的 Point2D 对象的引用,它是静态最终的,而不是属性 x 和 y。如果要确保不能更改 Point2D 对象的位置,则应在 Point2D 类中设置属性 x 和 y 静态和最终(初始化)。

于 2013-01-16T14:09:59.520 回答
0

它仍然是真实的。

有一种方法可以证明您引用的声明是真实的。static final描述了一个变量,这个变量确实不能改变,常数也是如此。变量是指向对象的指针,并且该对象可以更改。但这并不能阻止变量成为常数。

这是一种不太强大的真实方式,您可以const在 C++ 中实现,但它确实如此。

于 2013-01-16T12:42:30.303 回答
0

是的,你是对的。引用不能改变,但被引用的对象可以。这样的事情是完全合法的:

public static final List<Object> someList = new ArrayList<Object>();

// ...

someList.add(someThing); // <-- this would be illegal if the referenced object was also constant
于 2013-01-16T12:42:32.027 回答
0

您可以更改 Point2D 的属性,但不能创建它的新实例。

我还应该提到 Point2D 是抽象的,因此您必须创建一个子类的实例来扩展它。

于 2013-01-16T12:43:08.797 回答
0

当引用变量被声明为 final 时,一旦它引用了一个对象,您就不能重新分配一个新对象给它。但是,您可以更改最终引用变量指向的对象的状态。看下面的例子..

class A
{
    int i = 10;
}

public class UseOfFinalKeyword
{
    public static void main(String[] args)
    {
        final A a = new A();  //final reference variable

        a.i = 50;
        //you can change the state of an object to which final reference variable is pointing

        a = new A();  //compile time error

        //you can't re-assign a new object to final reference variable
    }
}

有关更多信息,请点击链接:java 中的 final 关键字

于 2015-03-21T14:04:15.750 回答
0
static final Point2D A = new Point2D(x,y);

它所说的只是类的引用Point2D不能改变。

            _____________
           |             |
A----------|--->(x,Y)    |
           |             | 
           |_____________|Heap

所以你不能通过指向不同的对象来改变 A 但是你当然可以改变 (x,y) 的值或者 Point Object 的内容

如果你想让你的对象也保持不变,你必须使 Point 对象不可变。

于 2013-01-29T06:09:00.267 回答