覆盖vs隐藏Java-困惑


88

我对重写与隐藏在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中的class方法。 但是,如果我testClassMethod()从Cat中删除,则将得到: Animal中的类方法。

这说明在子类中编写具有与父代相同的签名的静态方法几乎可以重写。

希望我能弄清楚我的困惑之处,并且有人可以阐明。首先十分感谢!


Answers:


103

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

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

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

在第二个调用中myAnimal.testInstanceMethod(),您将调用一个非静态方法。这就是所谓的运行时多态。直到运行时才确定要调用哪种方法。

要进一步说明,请阅读覆盖与隐藏


3
谢谢您的快速解答,这可以澄清它!我注意到在JavaRanch示例中,他们使用变量来调用类方法,而不是直接使用类,这使它更易于理解。我想在Java教程他们使用了类直接,因为使用一个实例来调用一个静态方法可能不是很好的做法,但他们应该用myAnimal.testClassMethod()代替Animal.testClassMethod()
Lostlinkpr 2012年

+1是为了能够正确地用语言写下来,而不是通过示例!:)
WhyNotHugo 2012年

@Kazekage Gaara重载和隐藏之间有区别吗?
gstackoverflow

1
我当然同意答案,但是private methods呢?他们不能overridden,因为子类不知道它们的存在。因此,他们可能会hidden代替。
Paschalis 2014年

优秀的答案!虽然为了完整起见,您可以在coderanch上添加示例:)
Shubham Mittal

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

1
好吧,那如果我叫“狗哈士奇= new dog();”会怎样?并调用husky.Animal();它会打印出animal.somethingdog.something吗?我猜它的错误是说**这将始终称为Animal.something()
amarnath harish

@amarnathharish你不能做.Animal(),记住Animal()是一个构造函数。
Dude156

而且,对于任何想知道的人来说,进一步澄清的是,something()inAnimal() 始终调用Animal的something()原因是因为对静态方法的调用是在编译时而不是运行时解决的。这意味着in的静态方法调用Animal()始终是隐式调用Animal.something()。如果您考虑一下,这将非常直观:对静态方法的调用必须在类名(即className.staticMethodName())之前,除非该调用位于同一类中。
Dude156

13

这是替代和隐藏之间的区别,

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

在此处输入图片说明


3
您直接从OP所说的教程中剪切并粘贴了该表,这对他没有帮助。
Wolfcastle,2012年

该表非常清楚,在示例中并未考虑所有情况。
tutak

3

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

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

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


1
这个答案无法解决关于覆盖/隐藏的实例方法与静态方法。
保罗·贝洛拉

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.

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 NameOverriding。

如果static Name更改为static Name隐藏。(子类的静态方法和超类之一都可以被调用,这取决于用于调用该方法的引用的类型。)


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

不知道为什么它没有得到支持。
amarnath harish

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方法


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())

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();

我试图了解隐藏一词的含义。什么藏着什么?父类中的静态方法是否被隐藏,因为(最不希望的)静态方法被调用了?还是因为未调用Child类中的静态方法而将其隐藏了?
蝎子

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;
    }
}

0

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


0

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

如果代码原本是这样写的,

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

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


0

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

编译器对覆盖的方法没有任何限制的可见性,并且仅在运行时才确定使用哪个方法。


0

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

动物a = new Cat();

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

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


-1

Java中如何隐藏静态方法?猫类是动物类的延伸。因此,在Cat类中将同时具有静态方法(我是说Child类的静态方法和Parent类的静态方法),但是JVM如何隐藏Parent静态方法?在堆和堆栈中如何处理?


这不是答案。这是所提问题的延伸。它本身可能是一个单独的问题,也可能是对该问题的评论的一部分。
Sri9911 '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.