与.Net不同为什么Java中的实例可以访问静态成员

时间:2013-12-03 06:57:30

标签: c# java

全部,与C#不同,我发现Java中的实例可以访问静态成员。好的,我对此没有任何意见。但实际上它会导致问题。让我用一个例子解释一下。

假设我们已经存在两个类。

public class A
{
     public void m1()
     {   
         ...
     } 
} 

publc class B
{
     public void m2()
     {
         A a= new A();
         a.m1();
     }
}

已经编译和部署了类。它总是很好。但出于某种原因。有人更改了类A,如下所示。

    public class A
    {
         public static void m1()
         {   
             ...
         } 
    }

所以他刚刚编译了A并进行了部署。类B不需要更改。因为代码是一样的。 如果没有编译类B,则会发生异常。

Exception in thread "main" java.lang.IncompatibleClassChangeError: 
Expecting non-static method A.main() at B.main(b.java:34)

虽然从实例变量调用静态成员时没有意义。但Java允许我们这样做。我只是无法理解为什么Java允许它?感谢。

3 个答案:

答案 0 :(得分:3)

您必须区分源代码和字节代码。

在源代码中,遗憾的是,Java允许通过实例引用访问静态成员。因此,您可以轻松编写a.m1(),其中a是一个变量(如您的示例所示)。然而,这是非常糟糕的风格,因此不推荐。

静态绑定在编译时解析。因此,编译器将对类的引用直接写入静态成员写入字节代码。因此,在分析生成的字节代码时,您将不再找到实例引用(关于静态成员)。

这种区别是你例外的原因。类B的字节代码仍然包含对动态链接方法的调用,该方法在类A的字节代码中根本不存在。后果:异常。

答案 1 :(得分:3)

你不能参加C#的原因是因为语言设计师认为这是不好的做法,可能会让人感到困惑。

采用以下Java示例:

class A {
    public static void Foo() {
        System.out.println("A::foo");
    }
    public void Bar() {
        System.out.println("A::bar");
    }
}

class B extends A {
    public static void Foo() {
        System.out.println("B::foo");
    }
    public void Bar() {
        System.out.println("B::bar");
    }
}

public static void main(String[] args) {
    A a = new B();
    a.foo();  // A::foo
    a.bar();  // B::bar
    a = null; // Oh-oh
    a.foo();  // A::foo
}

如您所见,即使将a设置为null,您仍然可以使用实例变量来调用静态方法。对于新手程序员和周一早上,这看起来很混乱。从我收集的内容来看,大多数Java开发人员也反对这种做法。

我不熟悉Java的当前状态或它们正朝着哪个方向发展。很可能这只是几年前允许的东西,但很难取出,因为它可能会破坏许多现有的应用程序。

答案 2 :(得分:0)

据我所知,你的问题是'为什么'是允许的?对?基本上对于代码进化,应该允许这种变化。

实际上,您所做的更改被称为二进制不兼容更改。 Java语言规范清楚地列出了可能破坏代码的这种不兼容的更改。是的,这是一个二进制不兼容的更改,但编译器无法知道是否有任何客户端应用程序正在使用它?因此,它最多会给你一个警告,作为一个java开发人员,你应该知道这种二进制不兼容的更改及其影响。作为代码的作者,您知道您的代码可能被其他库使用,并且不应该阻止您对代码库进行此类不兼容的更改(否则会破坏客户端库)。 Java编译器无法知道这一点吗?因此,java允许您更改方法定义,因为这对于代码演变是绝对必要的,也因为编译器不知道您的代码是如何分发和使用的。我希望我能解释允许对java代码进行这种不兼容的更改背后的基本原理。 有关详细信息,请查看here