C# 与.Net不同,为什么Java中的实例可以访问静态成员
总之,与C#不同,我发现Java中的实例可以访问静态成员。好的,我对此没有任何意见。但事实上这会引起一个问题。让我举个例子来解释一下 假设我们已经有两个班了C# 与.Net不同,为什么Java中的实例可以访问静态成员,c#,java,C#,Java,总之,与C#不同,我发现Java中的实例可以访问静态成员。好的,我对此没有任何意见。但事实上这会引起一个问题。让我举个例子来解释一下 假设我们已经有两个班了 public class A { public void m1() { ... } } publc class B { public void m2() { A a= new A(); a.m1(); } } 已经
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的字节码中根本不存在。结果:异常。您必须区分源代码和字节码 不幸的是,在源代码中,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的当前状态,也不太熟悉它们的发展方向。很可能这只是几年前允许的,但很难去掉,因为它可能会破坏许多现有的应用程序。在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代码进行这种不兼容更改的理由。
有关更多信息,请查看,因为我知道您的问题是“为什么”允许这样做?对吗?基本上,对于代码进化来说,应该允许这种更改 实际上,您所做的更改被称为二进制不兼容更改。Java语言规范清楚地列出了可能破坏代码的不兼容更改。是的,这是一个二进制不兼容的更改,但编译器无法知道是否有任何客户端应用程序正确使用它?因此,它至少会给您一个警告,作为一名java开发人员,您应该知道这种二进制不兼容的更改及其影响。作为代码的作者,您知道您的代码可能会被其他库使用,这会阻止您对代码库进行此类不兼容的更改(否则会破坏客户端库)。Java编译器没有办法知道这一点,对吗?因此,java允许