97

我对覆盖与隐藏在 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非常明显,没有什么特别之处。然后他们testInstanceMethod()从对 的引用中调用myCat,因此很明显,当时执行的方法是 的实例中的方法Cat

据我所知,调用隐藏的行为就像覆盖一样,那么为什么要区分呢?如果我使用上面的类运行此代码:

Cat.testClassMethod();

我会得到: Cat 中的类方法。 但是如果我testClassMethod()从 Cat 中删除,那么我会得到: Animal 中的类方法。

这告诉我,在子类中编写一个与父类具有相同签名的静态方法几乎可以实现覆盖。

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

4

17 回答 17

112

覆盖基本上支持后期绑定。因此,在运行时决定调用哪个方法。它适用于非静态方法。

隐藏适用于所有其他成员(静态方法、实例成员、静态成员)。它基于早期绑定。更清楚地说,要调用或使用的方法或成员是在编译时决定的。

在您的示例中,第一次调用Animal.testClassMethod()是对static方法的调用,因此很确定将调用哪个方法。

在第二次调用中myAnimal.testInstanceMethod(),您调用了一个非静态方法。这就是你所说的运行时多态性。直到运行时才决定调用哪个方法。

如需进一步说明,请阅读Overriding Vs Hiding

于 2012-05-15T04:21:58.947 回答
19

静态方法被隐藏,非静态方法被覆盖。当调用不是限定“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
于 2012-05-15T04:28:22.533 回答
14

这是覆盖和隐藏之间的区别,

  1. 如果父类和子类中的方法都是实例方法,则称为覆盖。
  2. 如果父类和子类中的方法都是静态方法,则称为隐藏。
  3. 一种方法在父级中不能是静态的,在子级中不能作为实例。反之亦然。

在此处输入图像描述

于 2012-05-15T04:25:32.727 回答
3

如果我正确理解您的问题,那么答案是“您已经在压倒一切”。

“这向我展示了在子类中编写一个与父类同名的静态方法几乎可以实现覆盖。”

如果您在子类中编写的方法与超类中的方法名称完全相同,它将覆盖超类的方法。@Override 注解不需要覆盖方法。但是,它确实使您的代码更具可读性,并强制编译器检查您是否实际上覆盖了一个方法(例如,没有拼错子类方法)。

于 2012-05-15T04:17:51.360 回答
3

覆盖仅发生在实例方法中。当引用变量的类型是 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.
于 2012-05-15T04:28:53.890 回答
2
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隐藏。(子类的静态方法和超类的静态方法都可以被调用,这取决于用于调用该方法的引用的类型。)

于 2013-05-09T21:08:35.480 回答
1

在此代码片段中,我使用“私有”访问修饰符而不是“静态”来向您展示隐藏方法和覆盖方法之间的区别。

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
于 2015-11-16T14:17:18.093 回答
1

我认为这还没有完全解释清楚。请看下面的例子。

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
于 2020-12-03T11:19:34.747 回答
0

基于我最近的 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
  }

在第二个主要中,我们使用 Object 类作为静态类型,所以当我们在 Point 对象中调用 equal 方法时,它正在等待 Point 类作为参数到达,但 Object 来了。所以 Object 类的 equals 方法开始运行,因为我们有一个 equals(Object o) 。在这种情况下,Point 的 equals 类不会覆盖,而是隐藏了 Object 类的 equals 方法

于 2015-09-10T09:16:59.343 回答
0
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())
于 2016-04-23T10:10:38.523 回答
0

链接的 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();
于 2016-09-26T14:38:45.773 回答
0

除了上面列出的示例之外,这里还有一个小示例代码来阐明隐藏和覆盖之间的区别:

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;
    }
}
于 2017-09-16T12:34:45.530 回答
0

在运行时,无论方法调用是在父类方法还是子类方法中定义,始终为实例执行被覆盖方法的子版本。以这种方式,除非使用语法 ParentClassName.method() 显式调用父方法,否则永远不会使用父方法。或者,如果在父类中定义了对该方法的调用,则在运行时始终执行隐藏方法的父版本。

于 2018-07-27T13:39:50.650 回答
0

在方法覆盖中,方法解析是由 JVM 基于运行时对象完成的。而在方法隐藏中,方法解析是由编译器在引用的基础上完成的。因此,

如果代码可以写成,

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

输出如下:
Animal 中的类方法。

于 2019-04-07T11:27:55.643 回答
0

之所以称为隐藏,是因为当子类具有相同的静态方法时,编译器会隐藏超类方法的实现。

编译器对重写的方法没有受限的可见性,只有在运行时才决定使用哪一个。

于 2019-09-03T22:44:30.963 回答
0

这是覆盖和隐藏之间的区别:

动物 a = new Cat();

a.testClassMethod() 将调用父类中的方法,因为它是方法隐藏的示例。要调用的方法由引用变量的类型决定,并在编译时决定。

a.testInstanceMethod() 将调用子类中的方法,因为它是方法覆盖的示例。要调用的方法由运行时调用该方法的对象决定。

于 2020-07-16T12:00:30.560 回答
-1

java中的静态方法隐藏是如何发生的?Cat 类正在扩展 Animal 类。所以在 Cat 类中会有两个静态方法(我的意思是 Child 类的静态方法和 Parent 类的静态方法)但是 JVM 如何隐藏 Parent 静态方法?它是如何处理堆和堆栈的?

于 2015-07-09T04:00:22.017 回答