考虑Java中的以下接口:
public interface I { public final String KEY = "a"; }
以下课程:
public class A implements I { public String KEY = "b"; public String getKey() { return KEY; } }
为什么A类可能出现并覆盖接口I的最终常量?
试试自己:
A a = new A(); String s = a.getKey(); // returns "b"!!!
Bill K.. 36
你隐藏它,它是"范围"的一个特征.每当你在较小的范围内,你可以重新定义你喜欢的所有变量,外部范围变量将是"阴影"
顺便说一句,如果您愿意,可以再次进行范围调整:
public class A implements I { public String KEY = "b"; public String getKey() { String KEY = "c"; return KEY; } }
现在KEY将返回"c";
编辑,因为原来重读了.
你隐藏它,它是"范围"的一个特征.每当你在较小的范围内,你可以重新定义你喜欢的所有变量,外部范围变量将是"阴影"
顺便说一句,如果您愿意,可以再次进行范围调整:
public class A implements I { public String KEY = "b"; public String getKey() { String KEY = "c"; return KEY; } }
现在KEY将返回"c";
编辑,因为原来重读了.
尽管您正在隐藏变量,但知道您可以在java中更改最终字段非常有趣,您可以在此处阅读:
Java 5 - "final"不再是最终版
来自挪威Machina Networks的Narve Saetre昨天给我发了一张纸条,提到很遗憾我们可以把把手改成最后一个阵列.我误解了他,开始耐心地解释说我们不能使数组不变,并且没有办法保护数组的内容."不,"他说,"我们可以用反射来改变最终手柄."
我尝试了Narve的示例代码,令人难以置信的是,Java 5允许我修改最终句柄,甚至是原始字段的句柄!我知道它曾经在某个时候被允许,但是它被禁止了,所以我用旧版本的Java运行了一些测试.首先,我们需要一个包含final字段的类:
public class Person { private final String name; private final int age; private final int iq = 110; private final Object country = "South Africa"; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + ", " + age + " of IQ=" + iq + " from " + country; } }JDK 1.1.x.
在JDK 1.1.x中,我们无法使用反射访问私有字段.但是,我们可以使用公共字段创建另一个Person,然后针对该类编译我们的类,并交换Person类.如果我们针对与我们编译的类不同的类运行,则在运行时没有访问检查.但是,我们无法使用类交换或反射在运行时重新绑定最终字段.
用于java.lang.reflect.Field的JDK 1.1.8 JavaDocs具有以下内容:
如果此Field对象强制执行Java语言访问控制,并且基础字段不可访问,则该方法将抛出IllegalAccessException.
如果基础字段是final,则该方法抛出IllegalAccessException.
JDK 1.2.x
在JDK 1.2.x中,这改变了一点.我们现在可以使用setAccessible(true)方法访问私有字段.现在在运行时检查字段的访问,因此我们无法使用类交换技巧来访问私有字段.但是,我们现在可以突然重新定位最终的领域!看看这段代码:
import java.lang.reflect.Field; public class FinalFieldChange { private static void change(Person p, String name, Object value) throws NoSuchFieldException, IllegalAccessException { Field firstNameField = Person.class.getDeclaredField(name); firstNameField.setAccessible(true); firstNameField.set(p, value); } public static void main(String[] args) throws Exception { Person heinz = new Person("Heinz Kabutz", 32); change(heinz, "name", "Ng Keng Yap"); change(heinz, "age", new Integer(27)); change(heinz, "iq", new Integer(150)); change(heinz, "country", "Malaysia"); System.out.println(heinz); } }当我在JDK 1.2.2_014中运行它时,我得到以下结果:
Ng Keng Yap, 27 of IQ=110 from Malaysia Note, no exceptions, no complaints, and an incorrect IQ result. It seems that if we set a声明时基元的最终字段,如果类型是基元或字符串,则内联值.
JDK 1.3.x和1.4.x
在JDK 1.3.x中,Sun稍微加强了访问,并阻止我们用反射修改最终字段.JDK 1.4.x也是如此.如果我们尝试运行FinalFieldChange类以在运行时使用反射重新绑定最终字段,我们将得到:
java版本"1.3.1_12":异常线程"main"IllegalAccessException:字段在FinalFieldChange.main(FinalFieldChange.change)的FinalFieldChange.change(FinalFieldChange.java:8)的java.lang.reflect.Field.set(Native Method)中是final的(FinalFieldChange.的java:12)
java版本"1.4.2_05"异常线程"main"IllegalAccessException:字段在FinalFieldChange.main的FinalFieldChange.change(FinalFieldChange.java:8)的java.lang.reflect.Field.set(Field.java:519)中是最终的( FinalFieldChange.java:12)
JDK 5.x
现在我们来到JDK 5.x. FinalFieldChange类与JDK 1.2.x中的输出相同:
Ng Keng Yap, 27 of IQ=110 from Malaysia When Narve Saetre mailed me that he managed to change a final field in JDK 5 using反思,我希望有一个bug进入JDK.但是,我们都觉得不太可能,尤其是这样一个根本的错误.经过一番搜索,我找到了JSR-133:Java内存模型和线程规范.大多数规范都是难以阅读的,并且让我想起了我的大学时代(我曾经这样写过;-)但是,JSR-133非常重要,应该要求所有Java程序员阅读.(祝好运)
从第9章最终字段语义开始,第25页.具体来说,请阅读第9.1.1节"最终字段的构造后修改".允许更新最终字段是有意义的.例如,我们可以放松在JDO中将字段设置为非final的要求.
如果我们仔细阅读第9.1.1节,我们会发现只应修改最终字段作为构建过程的一部分.用例是我们反序列化一个对象的地方,然后一旦我们构造了该对象,我们在传递它之前初始化最终的字段.一旦我们将对象提供给另一个线程,我们就不应该使用反射来更改最终字段.结果是不可预测的.
它甚至说:如果在字段声明中将final字段初始化为编译时常量,则可能无法观察到对final字段的更改,因为在编译时将该final字段的使用替换为编译时常量.这解释了为什么我们的iq字段保持不变,但国家/地区发生了变化
奇怪的是,JDK 5与JDK 1.2.x略有不同,因为您无法修改静态最终字段.
import java.lang.reflect.Field; public class FinalStaticFieldChange { /** Static fields of type String or primitive would get inlined */ private static final String stringValue = "original value"; private static final Object objValue = stringValue; private static void changeStaticField(String name) throws NoSuchFieldException, IllegalAccessException { Field statFinField = FinalStaticFieldChange.class.getDeclaredField(name); statFinField.setAccessible(true); statFinField.set(null, "new Value"); } public static void main(String[] args) throws Exception { changeStaticField("stringValue"); changeStaticField("objValue"); System.out.println("stringValue = " + stringValue); System.out.println("objValue = " + objValue); System.out.println(); } }当我们使用JDK 1.2.x和JDK 5.x运行它时,我们得到以下输出:
java version"1.2.2_014":stringValue =原始值objValue = new Value
java version"1.5.0"异常线程"main"IllegalAccessException:在FinalStaticFieldCieldCield(12)的FinalStaticFieldChange.changeStaticField(12)的java.lang.reflect.Field.set(Field.java:656)中,Field为final
那么,JDK 5就像JDK 1.2.x,只是不同?
结论
你知道JDK 1.3.0何时发布?我很难找到,所以我下载并安装了它.readme.txt文件的日期为2000/06/02 13:10.所以,它已经超过4岁了(天哪,感觉就像昨天一样).JDK 1.3.0在我开始撰写Java(tm)专家通讯前几个月发布!我认为可以肯定的是,很少有Java开发人员能够记住JDK1.3.0之前的细节.啊,怀旧不是以前的样子!你还记得第一次运行Java并得到这个错误:"无法初始化线程:找不到类java/lang/Thread"?