当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么可以覆盖Java中的最终常量?》经验,为你挑选了2个好方法。

考虑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";

编辑,因为原来重读了.



1> Bill K..:

你隐藏它,它是"范围"的一个特征.每当你在较小的范围内,你可以重新定义你喜欢的所有变量,外部范围变量将是"阴影"

顺便说一句,如果您愿意,可以再次进行范围调整:

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        String KEY = "c";
        return KEY;
    }
}

现在KEY将返回"c";

编辑,因为原来重读了.


要扩展一点,除了实例方法之外,您不能覆盖任何内容.变量和类(静态)方法被隐藏或隐藏,不被覆盖.

2> André..:

尽管您正在隐藏变量,但知道您可以在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"?

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有