簡體   English   中英

覆蓋與隱藏 Java - 困惑

[英]Overriding vs Hiding Java - Confused

我對覆蓋與隱藏在 Java 中的不同之處感到困惑。 任何人都可以提供有關這些差異的更多詳細信息嗎? 我閱讀了Java 教程,但示例代碼仍然讓我感到困惑。

更清楚地說,我理解覆蓋很好。 我的問題是,我看不出隱藏有什么不同,除了一個在實例級別而另一個在類級別。

查看Java教程代碼:

public class Animal {
    public static void testClassMethod() {
        System.out.println("Class" + " method in Animal.");
    }
    public void testInstanceMethod() {
        System.out.println("Instance " + " method in Animal.");
    }
}

然后我們有一個子類Cat

public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("The class method" + " in Cat.");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method" + " in Cat.");
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        Animal myAnimal = myCat;
        Animal.testClassMethod();
        myAnimal.testInstanceMethod();
    }
}

然后他們說:

該程序的輸出如下:

Animal中的類方法。

Cat 中的實例方法。

對我來說,調用類方法的事實testClassMethod()直接從Animal類執行方法Animal類是很明顯的,沒有什么特別的存在。 然后他們從對myCat的引用調用testInstanceMethod() ,所以很明顯,然后執行的方法是Cat實例中的方法。

從我看來,調用隱藏的行為就像覆蓋一樣,那么為什么要區分呢? 如果我使用上面的類運行此代碼:

Cat.testClassMethod();

我會得到: Cat 中的類方法。 但是如果我從 Cat 中刪除testClassMethod() ,那么我會得到: Animal 中的類方法。

這表明在子類中編寫一個與父類具有相同簽名的靜態方法幾乎可以覆蓋。

希望我能弄清楚我在哪里感到困惑,有人可以解釋一下。 首先十分感謝!

覆蓋基本上支持后期綁定。 因此,在運行時決定調用哪個方法。 它用於非靜態方法。

隱藏適用於所有其他成員(靜態方法、實例成員、靜態成員)。 它基於早期綁定。 更清楚的是,要調用或使用的方法或成員是在編譯時決定的。

在您的示例中,第一次調用Animal.testClassMethod()是對static方法的調用,因此很確定將調用哪個方法。

在第二次調用myAnimal.testInstanceMethod() ,您調用了一個非靜態方法。 這就是你所說的運行時多態性。 直到運行時才決定調用哪個方法。

如需進一步說明,請閱讀Overriding Vs Hiding

靜態方法被隱藏,非靜態方法被覆蓋。 當調用沒有限定“something()”與“this.something()”時,差異是顯着的。

我似乎真的不能把它放在文字上,所以這里有一個例子:

public class Animal {

    public static void something() {
        System.out.println("animal.something");
    }

    public void eat() {
        System.out.println("animal.eat");
    }

    public Animal() {
        // This will always call Animal.something(), since it can't be overriden, because it is static.
        something();
        // This will call the eat() defined in overriding classes.
        eat();
    }

}


public class Dog extends Animal {

    public static void something() {
        // This method merely hides Animal.something(), making it uncallable, but does not override it, or alter calls to it in any way.
        System.out.println("dog.something");
    }

    public void eat() {
        // This method overrides eat(), and will affect calls to eat()
        System.out.println("dog.eat");
    }

    public Dog() {
        super();
    }

    public static void main(String[] args) {
        new Dog();
    }

}

輸出:

animal.something
dog.eat

這就是覆蓋和隱藏的區別,

  1. 如果父類和子類中的方法都是實例方法,則稱為覆蓋。
  2. 如果父類和子類中的方法都是靜態方法,則稱為隱藏。
  3. 一種方法不能在父級中是靜態的,也不能在子級中作為實例。 反之亦然。

在此處輸入圖片說明

如果我正確理解你的問題,那么答案是“你已經是壓倒性的”。

“這告訴我,在子類中編寫一個與父類同名的靜態方法幾乎可以覆蓋。”

如果您在子類中編寫與超類中的方法同名的方法,它將覆蓋超類的方法。 覆蓋方法不需要@Override 注釋。 然而,它確實使您的代碼更具可讀性,並強制編譯器檢查您是否確實覆蓋了一個方法(例如,沒有拼錯子類方法)。

覆蓋僅發生在實例方法中。 當引用變量的類型是 Animal 並且對象是 Cat 時,從 Cat 調用實例方法(這是覆蓋)。 對於同一個 acat 對象,使用了 Animal 的類方法。

public static void main(String[] args) {
    Animal acat = new Cat();
    acat.testInstanceMethod();
    acat.testClassMethod();

}

輸出是:

The instance method in Cat.
Class method in Animal.
public class First {

    public void Overriding(int i) {  /* will be overridden in class Second */ }

    public static void Hiding(int i) {  /* will be hidden in class Second
                                           because it's static */ }
}


public class Second extends First {

    public void Overriding(int i) {  /* overridden here */  }

    public static void Hiding(int i) {  /* hides method in class First
                                           because it's static */ } 
}

記憶的規則很簡單:擴展類中的方法不能將 static 更改為 void,也不能將 void 更改為 static。 它會導致編譯錯誤。

但是,如果將void Name更改為void Name它將被覆蓋。

如果static Name更改為static Name則它正在隱藏。 (子類的靜態方法和超類的靜態方法都可以被調用,這取決於用於調用該方法的引用的類型。)

在此代碼片段中,我使用“私有”訪問修飾符而不是“靜態”來向您展示隱藏方法和覆蓋方法之間的區別。

class Animal {
// Use 'static' or 'private' access modifiers to see how method hiding work.
private void testInstancePrivateMethod(String source) {
    System.out.println("\tAnimal: instance Private method calling from "+source);
}
public void testInstanceMethodUsingPrivateMethodInside() {
    System.out.println("\tAnimal: instance Public method with using of Private method.");
    testInstancePrivateMethod( Animal.class.getSimpleName() );
}

// Use default, 'protected' or 'public' access modifiers to see  how method overriding work.
protected void testInstanceProtectedMethod(String source) {
    System.out.println("\tAnimal: instance Protected method calling from "+source);
}
public void testInstanceMethodUsingProtectedMethodInside() {
    System.out.println("\tAnimal: instance Public method with using of Protected method.");
    testInstanceProtectedMethod( Animal.class.getSimpleName() );
  } 
}  


public class Cat extends Animal {
private void testInstancePrivateMethod(String source) {
    System.out.println("Cat: instance Private method calling from " + source );
}
public void testInstanceMethodUsingPrivateMethodInside() {
    System.out.println("Cat: instance Public method with using of Private method.");
    testInstancePrivateMethod( Cat.class.getSimpleName());
    System.out.println("Cat: and calling parent after:");
    super.testInstanceMethodUsingPrivateMethodInside();
}

protected void testInstanceProtectedMethod(String source) {
    System.out.println("Cat: instance Protected method calling from "+ source );
}
public void testInstanceMethodUsingProtectedMethodInside() {
    System.out.println("Cat: instance Public method with using of Protected method.");
    testInstanceProtectedMethod(Cat.class.getSimpleName());
    System.out.println("Cat: and calling parent after:");
    super.testInstanceMethodUsingProtectedMethodInside();
}

public static void main(String[] args) {
    Cat myCat = new Cat();
    System.out.println("----- Method hiding -------");
    myCat.testInstanceMethodUsingPrivateMethodInside();
    System.out.println("\n----- Method overriding -------");
    myCat.testInstanceMethodUsingProtectedMethodInside();
}
}

輸出:

----- Method hiding -------
Cat: instance Public method with using of Private method.
Cat: instance Private method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Private method.
   Animal: instance Private method calling from Animal

----- Method overriding -------
Cat: instance Public method with using of Protected method.
Cat: instance Protected method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Protected method.
Cat: instance Protected method calling from Animal

我認為這還沒有完全解釋清楚。 請看下面的例子。

class Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Animal");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Animal");
    }
}


public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Cat");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Cat");
    }

    public static void main(String[] args) {
        Animal myCat = new Cat();
        Cat myCat2 = new Cat();
        myCat.testClassMethod();
        myCat2.testClassMethod();
        
        
        myCat.testInstanceMethod();
        myCat2.testInstanceMethod();
    }
}

輸出如下。

The static method in Animal
The static method in Cat
The instance method in Cat
The instance method in Cat

基於我最近的 Java 研究

  • 方法覆蓋,當子類在子類中具有相同簽名的相同方法時。
  • 方法隱藏,當子類具有相同的方法名稱,但不同的參數時。 在這種情況下,您不是覆蓋父方法,而是隱藏它。

OCP Java 7 書籍第 70-71 頁中的示例:

public class Point {
  private int xPos, yPos;
  public Point(int x, int y) {
        xPos = x;
        yPos = y;
  }

  public boolean equals(Point other){
  .... sexy code here ...... 
  }

  public static void main(String []args) {
   Point p1 = new Point(10, 20);
   Point p2 = new Point(50, 100);
   Point p3 = new Point(10, 20);
   System.out.println("p1 equals p2 is " + p1.equals(p2));
   System.out.println("p1 equals p3 is " + p1.equals(p3));
   //point's class equals method get invoked
  }
}

但是如果我們寫以下主要內容:

  public static void main(String []args) {
   Object p1 = new Point(10, 20);
   Object p2 = new Point(50, 100);
   Object p3 = new Point(10, 20);
   System.out.println("p1 equals p2 is " + p1.equals(p2));
   System.out.println("p1 equals p3 is " + p1.equals(p3));
   //Object's class equals method get invoked
  }

在第二個 main 中,我們使用 Object 類作為靜態類型,所以當我們調用 Point 對象中的 equal 方法時,它正在等待一個 Point 類作為參數到達,但 Object 來了。 所以 Object 類的 equals 方法正在運行,因為我們在那里有一個 equals(Object o)。 在這種情況下,Point 的類 equals不會覆蓋,而是隱藏了 Object 類的 equals 方法

public class Parent {

  public static void show(){
    System.out.println("Parent");
  }
}

public class Child extends Parent{

  public static void show(){
    System.out.println("Child");
  }
}

public class Main {

public static void main(String[] args) {
    Parent parent=new Child();
    parent.show(); // it will call parent show method
  }
}

// We can call static method by reference ( as shown above) or by using class name (Parent.show())

鏈接的java教程頁面解釋了覆蓋和隱藏的概念

子類中的實例方法具有與超類中的實例方法相同的簽名(名稱,加上其參數的數量和類型)和返回類型,將覆蓋超類的方法。

如果子類定義了與超類中的靜態方法具有相同簽名的靜態方法,則子類中的方法隱藏超類中的方法。

隱藏靜態方法和覆蓋實例方法之間的區別具有重要意義:

  1. 被調用的重寫實例方法的版本是子類中的版本。
  2. 被調用的隱藏靜態方法的版本取決於它是從超類還是從子類調用。

回到你的例子:

Animal myAnimal = myCat;

 /* invokes static method on Animal, expected. */
 Animal.testClassMethod(); 

 /* invokes child class instance method (non-static - it's overriding) */
 myAnimal.testInstanceMethod();

上面的語句還沒有顯示隱藏。

現在更改如下代碼以獲得不同的輸出:

  Animal myAnimal = myCat;
  
  /* Even though myAnimal is Cat, Animal class method is invoked instead of Cat method*/
  myAnimal.testClassMethod();
  
  /* invokes child class instance method (non-static - it's overriding) */
  myAnimal.testInstanceMethod();

除了上面列出的示例之外,這里還有一個小示例代碼來闡明隱藏和覆蓋之間的區別:

public class Parent {

    // to be hidden (static)
    public static String toBeHidden() {
        return "Parent";
    }

    // to be overridden (non-static)
    public String toBeOverridden() {
        return "Parent";
    }

    public void printParent() {
        System.out.println("to be hidden: " + toBeHidden());
        System.out.println("to be overridden: " + toBeOverridden());
    }
}

public class Child extends Parent {

    public static String toBeHidden() {
        return "Child";
    }

    public String toBeOverridden() {
        return "Child";
    }

    public void printChild() {
        System.out.println("to be hidden: " + toBeHidden());
        System.out.println("to be overridden: " + toBeOverridden());
    }
}

public class Main {

    public static void main(String[] args) {
        Child child = new Child();
        child.printParent();
        child.printChild();
    }
}

child.printParent()的調用輸出:
隱藏:父級
被覆蓋:孩子

child.printChild()的調用輸出:
被隱藏:孩子
被覆蓋:孩子

從上面的輸出(尤其是粗體標記的輸出)可以看出,方法隱藏與覆蓋的行為不同。

Java 只允許對方法進行隱藏和覆蓋。 相同的規則不適用於變量。 不允許覆蓋變量,因此只能隱藏變量(靜態或非靜態變量之間沒有區別)。 下面的示例顯示了如何覆蓋getName()方法並隱藏變量name

public class Main {

    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.name); // prints Parent (since hiding)
        System.out.println(p.getName()); // prints Child (since overriding)
    }
}

class Parent {
    String name = "Parent";

    String getName() {
        return name;
    }
}

class Child extends Parent {
    String name = "Child";

    String getName() {
        return name;
    }
}

在運行時,無論方法調用是在父類方法還是子類方法中定義,都會為實例執行覆蓋方法的子版本。 以這種方式,除非使用語法 ParentClassName.method() 引用對父方法的顯式調用,否則永遠不會使用父方法。 或者,如果在父類中定義了對方法的調用,則在運行時始終執行隱藏方法的父版本。

在方法覆蓋中,方法解析是由 JVM 基於運行時對象完成的。 而在方法隱藏中,方法解析是由編譯器在引用的基礎上完成的。 因此,

如果代碼被寫成,

public static void main(String[] args) {
        Animal myCat = new Cat();        
        myCat.testClassMethod();        
    }

輸出如下:
Animal中的類方法。

之所以稱為隱藏,是因為當子類具有相同的靜態方法時,編譯器隱藏了超類方法的實現。

編譯器對重寫方法的可見性沒有限制,只有在運行時才決定使用哪個方法。

這是覆蓋和隱藏之間的區別:

動物 a = new Cat();

a.testClassMethod() 會調用父類中的方法,因為它是方法隱藏的一個例子。 要調用的方法由引用變量的類型決定,並在編譯時決定。

a.testInstanceMethod() 將調用子類中的方法,因為它是方法覆蓋的示例。 要調用的方法由用於在運行時調用該方法的對象確定。

java中的靜態方法隱藏是如何發生的? Cat 類是對 Animal 類的擴展。 所以在 Cat 類中將有兩個靜態方法(我的意思是 Child 類的靜態方法和 Parent 類的靜態方法)但是 JVM 如何隱藏 Parent 靜態方法? 它是如何處理堆和堆棧的?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM