栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么可以覆盖Java中的最终常量?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么可以覆盖Java中的最终常量?

尽管事实上您正在隐藏变量,但是知道可以在java中更改final字段非常有趣,因为您可以在此处阅读:

Java 5-“最终”不再是最终的

挪威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

在声明时为基本类型的final字段,如果类型为基本类型或字符串,则该值将内联。

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.FinalFieldChange.change(FinalFieldChange.java:8)的java.lang.reflect.Field.set(本机方法)处是最终的。
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

现在我们进入JDK5.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

反思,我希望有一个漏洞爬到JDK中。但是,我们俩都认为这不太可能,尤其是这样的基本错误。经过一番搜索,我发现了《
JSR-133:Java内存模型和线程规范》。大部分规范很难读,并且使我想起了我的大学时代(我以前常常写过这样的书;-)。但是,JSR-133非常重要,因此所有Java程序员都必须阅读它。(祝好运)

从第25页的第9章“最终字段语义”开始。具体来说,请阅读第9.1.1节“最终字段的构造后修改”。允许更新最终字段很有意义。例如,我们可以放宽对JDO中非最终字段的要求。

如果我们仔细阅读第9.1.1节,我们会看到我们仅应在构建过程中修改最终字段。用例是对一个对象进行反序列化,然后在构造该对象后,在传递它之前初始化最终字段。一旦使对象可用于另一个线程,就不应使用反射更改最终字段。结果将不可预测。

它甚至说:如果在字段声明中将final字段初始化为编译时常量,则可能不会观察到对final字段的更改,因为在编译时将使用final字段替换为compile-
time常量。这解释了为什么我们的iq字段保持不变,但国家/地区有所变化。

奇怪的是,JDK 5与JDK 1.2.x略有不同,因为您不能修改静态的final字段。

import java.lang.reflect.Field;public class FinalStaticFieldChange {    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版本“ 1.2.2_014”:stringValue =原始值objValue =新值

java版本“ 1.5.0”异常线程“ main”
IllegalAccessException:字段在FinalStaticFieldChange.changeStaticField(12)的FinalStaticFieldChange.main(16)处的java.lang.reflect.Field.set(Field.java:656)是最终的

因此,JDK 5就像JDK 1.2.x,只是有所不同?

结论

您知道JDK 1.3.0何时发布吗?我很难找到答案,所以我下载并安装了它。readme.txt文件的日期为2000/06/02
13:10。因此,它已经有4年多了(天哪,就像昨天一样)。在我开始编写Java(tm)专家通讯之前的几个月,就发布了JDK
1.3.0!我认为可以肯定地说,很少有Java开发人员可以记住JDK1.3.0之前的细节。啊,怀旧不再是过去了!您还记得第一次运行Java并收到以下错误:“无法初始化线程:找不到类java
/ lang / Thread”吗?



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/609884.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号