45

谁能告诉我以下行在Java上下文中的含义是什么:

最终变量仍然可以被操纵,除非它是不可变的

据我所知,通过将任何变量声明为最终变量,您无法再次更改它,那么上面一行中不可变一词的含义是什么?

4

11 回答 11

98

这意味着如果您的最终变量是引用类型(即不是像 int 这样的原始变量),那么它只是不能更改的引用。它不能引用不同的对象,但如果类允许,它所引用的对象的字段仍然可以更改。例如:

final StringBuffer s = new StringBuffer();

StringBuffer 的内容仍然可以任意改变:

s.append("something");

但是你不能说:

s = null;

或者

s = anotherBuffer;

另一方面:

final String s = "";

字符串是不可变的——根本没有任何方法可以让你改变一个字符串(除非你使用反射——然后下地狱)。

于 2009-08-08T21:19:03.273 回答
16

如果您有对 Java 对象的最终引用,您仍然可以操作它,但不能更改它的引用。例如,这段代码是完全合法的:

import javax.swing.JLabel;

class Test1 {
    private final static JLabel l = new JLabel("Old text");
    public static void main(String[] args) {
        System.err.println(l.getText());
        l.setText("New Text");
        System.err.println(l.getText());
    }
}

但是你不能说:

l = new JLabel("Newest Text");

在第一次分配给 l 之后。请注意,您可以这样做:

import javax.swing.JLabel;

class Test1 {
    public static void main(String[] args) {
        final JLabel l;
        String s = getArbitaryString(); // Assume this method returns a string
        l = new JLabel(s);
        System.err.println(l.getText());
    }
}

这是可以做到的,因为当 l 被声明时,它没有被分配给任何甚至不是 null 的东西。因此,您只能为它分配一次。

原语也是如此。您可以像这样为其赋值:

class Test1 {
    public static void main(String[] args) {
        final int i;
        i = 2;
    }
}

但是现在您无法进一步操作它,因为您对原始类型唯一可以做的就是为它们分配值。

于 2009-08-08T21:17:15.490 回答
4

您不能更改最终变量引用的对象或值。您只能分配一次最终变量。

这对您是否可以更改对象的状态没有影响。对象本身仍然可以被操作,除非它的编码方式禁止这种操作。不可变对象是其状态不能改变的对象。

于 2009-08-08T21:20:56.497 回答
3

正如其他人所说,这意味着您可以操纵变量指向的对象,但不能更改引用(即为变量分配另一个对象)。

设计上可变的对象,例如 aList可以更改(您可以向它们添加元素),而如果您有一个不可变的对象,例如 aString或者Integer您将无法更改它(该类String支持的所有操作都返回 a新实例,并且不要修改实际对象)。

于 2009-08-08T21:20:29.010 回答
0

即使该方法可以更改引用指向的对象的状态,您也可以对其调用任何方法。例如

final MyClass myClass = new MyClass();
myClass.setVar(something);

这很好,因为myClass它本身并没有改变,即你没有在做myClass = myClass1;.

于 2010-02-27T14:03:40.297 回答
0

那个总是杀了我的人?

如果您希望最终变量实际上像您认为的那样安全,您需要大量额外的代码来返回 String[] 的副本。

于 2010-02-27T14:51:13.440 回答
0

您可以操作例如 StringBuffer 类型的可变最终变量,但不能操作不可变类型的最终变量。

如果是可变变量,则不会在每次更改其值时创建新对象。但是在不可变类型的情况下,每当您更改值时,都会创建新对象,因此当您将其设为最终类型时,您无法修改它。

于 2012-08-23T18:24:15.780 回答
0

是的,最终变量可以修改。

    final StringBuffer s = new StringBuffer();
    // won't work
    s = new StringBuffer();
    //this works
    s.append("hai");

您不能更改引用,但可以修改对象的字段。更多细节

于 2015-02-23T04:47:41.510 回答
0

以下是我几年前创建的代码,用于关闭最终关闭然后重新打开,以便您可以修改引用/值,它仅适用于变量,但它确实有效。

你也可以用方法句柄做类似的事情,但是除非你正在编写某种形式的自动对象解析器/生成器,否则我会避免像瘟疫一样做任何事情。

public static void setValueOnField(Object instance, Field field, Object value)
        throws NoSuchFieldException, IOException, IllegalArgumentException,
        IllegalAccessException {
    try (Accessor<Field> access = open(field)) {
        field.set(instance, value);
    }
}


  public static class Accessor<T extends AccessibleObject & Member>
            implements Closeable {
        private final boolean isAccessible;
        private final boolean isFinal;
        private final int modifiers;
        private final T accessibleObject;

        private Accessor(T accessibleObject) throws IOException {
            super();
            if (accessibleObject == null) {
                throw new IOException(
                        "Error preparing field for accesibility: Field is null");
            }
            try {
                this.accessibleObject = accessibleObject;
                this.modifiers = accessibleObject.getModifiers();
                this.isAccessible = accessibleObject.isAccessible();
                this.isFinal = Modifier.isFinal(modifiers);
                if (!this.isAccessible) {
                    accessibleObject.setAccessible(true);
                }
                if (this.isFinal) {
                    getModifiersField(accessibleObject).setInt(
                            accessibleObject, modifiers & ~Modifier.FINAL);
                }
            } catch (Exception e) {
                throw new IOException("Error preparing field for accesibility",
                        e);
            }

        }

        @Override
        public void close() throws IOException {

            if (!this.isAccessible) {
                accessibleObject.setAccessible(false);
            }
            if (this.isFinal) {
                try {
                    getModifiersField(accessibleObject).setInt(
                            accessibleObject, modifiers);
                } catch (Exception e) {
                    throw new IOException("Error setting field to final", e);
                }
            }
        }

        public T getAccessibleObject() {
            return accessibleObject;
        }

        private static Field getModifiersField(AccessibleObject toFetch) {
            Field field;
            try {
                field = toFetch.getClass().getField("modifiers");
                field.setAccessible(true);
                return field;
            } catch (Exception e) {
                throw new RuntimeException(
                        "Error occured getting modifiers field", e);
            }
        }
    }
于 2019-11-11T11:19:59.817 回答
0

有两个东西最终变量和最终参考变量。

如果我们使用具有原始数据类型的 final 关键字,我们将无法更改任何内容。但是,如果我们将 final keywprd 与非原始数据一起使用,我们可以更改它的属性,例如:

  1. 如果您在变量的原始类型(int、float、char 等)中使用 final 关键字,那么一旦初始化 final 变量,您就无法更改它的值。所以,我们应该初始化它。

  2. 如果您在非原始变量中使用 final 关键字(通过非原始变量在 Java 中始终引用对象),则可以更改对象的成员。这意味着我们可以更改对象的属性,但不能更改为引用任何其他对象。

https://javagoal.com/final-keyword-in-java/

于 2020-08-04T03:42:42.430 回答
-2

您仍然可以使用反射更改“最终”变量。

于 2009-08-08T21:17:42.573 回答