谁能告诉我以下行在Java上下文中的含义是什么:
最终变量仍然可以被操纵,除非它是不可变的
据我所知,通过将任何变量声明为最终变量,您无法再次更改它,那么上面一行中不可变一词的含义是什么?
这意味着如果您的最终变量是引用类型(即不是像 int 这样的原始变量),那么它只是不能更改的引用。它不能引用不同的对象,但如果类允许,它所引用的对象的字段仍然可以更改。例如:
final StringBuffer s = new StringBuffer();
StringBuffer 的内容仍然可以任意改变:
s.append("something");
但是你不能说:
s = null;
或者
s = anotherBuffer;
另一方面:
final String s = "";
字符串是不可变的——根本没有任何方法可以让你改变一个字符串(除非你使用反射——然后下地狱)。
如果您有对 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;
}
}
但是现在您无法进一步操作它,因为您对原始类型唯一可以做的就是为它们分配值。
您不能更改最终变量引用的对象或值。您只能分配一次最终变量。
这对您是否可以更改对象的状态没有影响。对象本身仍然可以被操作,除非它的编码方式禁止这种操作。不可变对象是其状态不能改变的对象。
正如其他人所说,这意味着您可以操纵变量指向的对象,但不能更改引用(即为变量分配另一个对象)。
设计上可变的对象,例如 aList
可以更改(您可以向它们添加元素),而如果您有一个不可变的对象,例如 aString
或者Integer
您将无法更改它(该类String
支持的所有操作都返回 a新实例,并且不要修改实际对象)。
即使该方法可以更改引用指向的对象的状态,您也可以对其调用任何方法。例如
final MyClass myClass = new MyClass();
myClass.setVar(something);
这很好,因为myClass
它本身并没有改变,即你没有在做myClass = myClass1;
.
那个总是杀了我的人?
如果您希望最终变量实际上像您认为的那样安全,您需要大量额外的代码来返回 String[] 的副本。
您可以操作例如 StringBuffer 类型的可变最终变量,但不能操作不可变类型的最终变量。
如果是可变变量,则不会在每次更改其值时创建新对象。但是在不可变类型的情况下,每当您更改值时,都会创建新对象,因此当您将其设为最终类型时,您无法修改它。
是的,最终变量可以修改。
final StringBuffer s = new StringBuffer();
// won't work
s = new StringBuffer();
//this works
s.append("hai");
您不能更改引用,但可以修改对象的字段。更多细节
以下是我几年前创建的代码,用于关闭最终关闭然后重新打开,以便您可以修改引用/值,它仅适用于变量,但它确实有效。
你也可以用方法句柄做类似的事情,但是除非你正在编写某种形式的自动对象解析器/生成器,否则我会避免像瘟疫一样做任何事情。
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);
}
}
}
有两个东西最终变量和最终参考变量。
如果我们使用具有原始数据类型的 final 关键字,我们将无法更改任何内容。但是,如果我们将 final keywprd 与非原始数据一起使用,我们可以更改它的属性,例如:
如果您在变量的原始类型(int、float、char 等)中使用 final 关键字,那么一旦初始化 final 变量,您就无法更改它的值。所以,我们应该初始化它。
如果您在非原始变量中使用 final 关键字(通过非原始变量在 Java 中始终引用对象),则可以更改对象的成员。这意味着我们可以更改对象的属性,但不能更改为引用任何其他对象。
您仍然可以使用反射更改“最终”变量。