[英]Unlike .Net Why the static member can be accessed by the instance in 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允许它? 谢谢。
您必须区分源代码和字节代码。
在源代码中,不幸的是Java允许通过实例引用访问静态成员。 因此,您可以轻松地编写a.m1()
,其中a
是变量(如您的示例所示)。 但是,这是非常糟糕的样式,因此不建议使用。
静态绑定在编译时解析。 因此,编译器将对类的引用直接将对静态成员的访问写到字节码中。 因此,在分析结果字节码时,您将再也找不到实例引用(关于静态成员)。
而这种区别就是您例外的原因。 类B的字节代码仍然包含对动态链接方法的调用,该调用在类A的字节代码中根本不存在。结果:异常。
您之所以不能使用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的当前状态或前进方向并不熟悉。 这很可能只是几年前允许的,但是很难删除,因为它可能会破坏许多现有应用程序。
据我了解,您的问题是“为什么”? 对? 基本上,对于代码演化,应允许进行此类更改。
实际上,您所做的更改被称为二进制不兼容更改。 Java语言规范明确列出了这种不兼容的更改,可能会破坏代码。 是的,这是二进制不兼容的更改,但是编译器无法知道是否有任何正在使用它的客户端应用程序对吗? 因此,它最多会给您一个警告,作为Java开发人员,您应该知道这种二进制不兼容的更改及其影响。 作为代码的编写者,您知道您的代码可能会被其他库使用,并且应该阻止您对代码库进行此类不兼容的更改(否则将破坏客户端库)。 Java编译器没有办法知道这个权利吗? 因此,java允许您更改方法定义,因为这对于代码演化绝对是必不可少的,并且还因为编译器不知道代码的分布和使用方式。 我希望我能解释允许对Java代码进行这种不兼容更改的原因。 欲了解更多信息,请点击这里
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.