[英]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.