考虑下面的接口在Java:

public interface I {
    public final String KEY = "a";
}

和以下类别:

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

    public String getKey() {
        return KEY;
    }
}

为什么是它可能对A类来复盖界面我的最后定?

试试自己:

A a = new A();
String s = a.getKey(); // returns "b"!!!
有帮助吗?

解决方案

尽管事实上,你是阴影的变量是相当有兴趣知道,你可以改变的最后领域中java为你可以读 在这里,:

Java5-"最后的"不是最后的了

Narve Saetre从马基纳网络在挪威的给我注意昨天, 一提的是,令人遗憾的是,我们可以改变处理一个 最后的阵列。我误解了他,并开始耐心地解释 我们不能让一系列恒定的,而这是没有办法的 保护内容的一个阵列。"不",他说,"我们可以改变一个 最后处理使用的反思"。

我试图Narve的代码样本,并且令人难以置信的是,Java5允许我 修改的最后处理,甚至一个处理一个原始现场!我知道, 它使用的是允许在某一点,但是,这是后来不允许, 所以我跑一些测试与旧版本。第一,我们需要一个 类的最后领域:

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;
  }
}

JAVA1.1.x

在JAVA1.1.x,我们不能够接入的私人领域使用 反射。我们可以,但是,创造另一个人与公众 领域,然后汇编我们的类害,并交换人 课程。有没有检查访问在运行时,如果我们运行 对不同类的一个,我们编制的反对。然而,我们不能重新绑定最后的领域,在运行时使用 类交换或者反映。

The JAVA1.1.8JavaDocs为java。郎。反映。领域有以下 说:

  • 如果这个领域对象强制执行Java语言的访问控制和潜在领域是无法访问的方法,该方法将引发 IllegalAccessException.
  • 如果潜在领域是最终方法,该方法引发的一个IllegalAccessException.

JAVA1.2.x

在JAVA1.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);
  }
}

当我跑这JAVA1.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

最终场的一个原始在宣言》时,值是内联, 如果该类是原始的或一串。

JAVA1.3.x和1.4.x

在JAVA1.3.x、太阳收紧入一点,并且阻止我们 修改最终场的反映。这也是情况 JAVA1.4.x.如果我们试图运行FinalFieldChange类重新绑定 最后的领域,在运行时使用的反思,我们会得到:

java version"1.3.1_12":例外线"主要" IllegalAccessException:场是最终的 在java。郎。反映。领域。设置(地方法) 在FinalFieldChange.改变(FinalFieldChange.java:8) 在FinalFieldChange.主(FinalFieldChange.java:12)

java version"1.4.2_05"例外线"主要" IllegalAccessException:场是最终的 在java。郎。反映。领域。设置(现场。java:519) 在FinalFieldChange.改变(FinalFieldChange.java:8) 在FinalFieldChange.主(FinalFieldChange.java:12)

JAVA5.x

现在我们得到JAVA5.x.该FinalFieldChange类具有相同的输出 作为在JAVA1.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

反,我希望这一错误蹑手蹑脚入JAVA.但是, 我们都觉得那是不可能的,尤其是这样一个根本的错误。之后,一些搜索时,我发现JSR-133:Java存储器模型和 线规范。大部分的说明书是很难阅读, 让我想起我的大学天(I用来写喜欢这;-) 然而,JSR-133是如此重要的是,它应该读一读 所有Java程序员。(好运气)

开始第9章最后一段的语义上,第25页。具体地说, 阅读第9.1.1后建筑的修改的最后一个领域。它的 有意义的允许更新最后的领域。例如,我们可以 放松的要求有领域的非最终在被创建.

如果我们阅读第9.1.1仔细,我们看到,我们应该只修改 最后领域的一部分,我们的建设进程。利用情况 我们deserialize的一个目的,那么一旦我们建造的 目的,我们初始化最后的领域之前,通过它。一旦我们 已对象可用来另外一个线程,我们不应该改变 最后领域使用的反思。该结果将不会是可以预测的。

它甚至可以这样说:如果最终场初始化为一个编译时间 恒定在该领域的宣言,变化最后的领域不可能 可以观察到,由于采用这一最后的领域是取代在编译 时间与编译的时间不变。这就解释了为什么我们的智商场 保持相同,但国家的变化。

奇怪的是,JAVA5略有不同JAVA1.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();
  }
}

当我们执行这与JAVA1.2.x和JAVA5.x,我们得到如下的 输出:

java version"1.2.2_014":stringValue=原始价值objValue=new 值

java version"升级到1.5.0"例外线"主要"IllegalAccessException:场是最终在java。郎。反映。领域。设置(现场。java:656)在 FinalStaticFieldChange.changeStaticField(12) FinalStaticFieldChange.主(16)

因此,JAVA5像JAVA1.2.x,只是不同的?

结论

你知道当JAVA1.3.0被释放?我在努力找出来,所以我 下载并安装它。的readme.txt 文件的日期 2000/06/02 13:10.因此,它是超过4岁(我的天啊, 感觉像是昨天).JAVA1.3.0发布之前几个月我 开始写Java(tm)专家的通讯!我认为它会 可以安全地说,很少Java开发可以记住细节 预JDK1.3.0.啊,怀旧不是有什么用!做你 记得Java第一次获得这个错误:"无法初始化主题:找不到类java/lang/线"?

其他提示

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

顺便说一句,如果您愿意,可以再次进行搜索:

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 static void main(String args[])
    {
        A t = new A();
        System.out.println(t.KEY);
        System.out.println(((I) t).KEY);
    }
}

如您所见,这将打印“B”和“A”。您甚至可以分配给它,因为A.KEY变量未定义为final。

 A.KEY="C" <-- this compiles.

但是 -

public class C implements I{

    public static void main (String args[])
    {
        C t = new C();
        c.KEY="V"; <--- compiler error ! can't assign to final

    }
}

你不应该问你的恒定的以这种方式,使用静态的参考,而不是:

I.KEY //returns "a"
B.KEY //returns "b"

作为设计考虑因素,

public interface I {
    public final String KEY = "a";
}

静态方法总是返回父键。

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

    public String getKey() {
        return KEY; // returns "b"
    }

    public static String getParentKey(){
        return KEY; // returns "a"
    }
}

就像Jom注意到的那样。使用重新定义的接口成员的静态方法的设计可能是一个严重的问题。通常,尽量避免对常量使用相同的名称。

静态字段和方法附加到声明它们的类/接口(尽管接口不能声明静态方法,因为它们是完全需要实现的抽象类)。

所以,如果你有一个带有public static(vartype)(varname)的接口, 该字段附加到该接口。

如果你有一个类实现该接口,编译器技巧会将(this。)varname转换为InterfaceName.varname。但是,如果您的类重新定义了varname,则会将一个名为varname的新常量附加到您的类,编译器现在知道将(。。)varname转换为NewClass.varname。这同样适用于方法:如果新类没有重新定义方法,则(this。)methodName被翻译成SuperClass.methodName,否则,(this。)methodName被翻译成CurrentClass.methodName。

这就是为什么你会遇到警告“应该以静态方式访问x字段/方法”的原因。编译器告诉你,虽然它可能会使用这个技巧,但它更喜欢你使用ClassName.method / fieldName,因为它更易于显示。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top