覆盖与隐藏Java - 困惑

问题描述 投票:70回答:14

我对Overriding与隐藏Java有何不同感到困惑。任何人都可以提供有关这些差异的更多细节吗我读了Java Tutorial,但示例代码仍让我感到困惑。

为了更清楚,我理解压倒一切。我的问题是我没有看到隐藏是有什么不同的,除了一个是在实例级别而另一个在类级别。

查看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中的实例方法

对我来说,直接从Animal类调用类方法testClassMethod(),在Animal类中执行该方法这一事实非常明显,没有什么特别之处。然后他们从对myCat的引用中调用testInstanceMethod(),所以再次非常明显的是,然后执行的方法是Cat实例中的方法。

从我所看到的隐藏行为就像覆盖一样,为什么要做出这种区分。如果我使用上面的类运行此代码:

Cat.testClassMethod();

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

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

希望我清楚地表明我的困惑,有人可以解决问题。首先十分感谢!

java inheritance methods override
14个回答
94
投票

超越基本上支持后期绑定。因此,将在运行时决定调用哪个方法。它用于非静态方法。隐藏适用于所有其他成员(静态方法,实例成员,静态成员)。它基于早期绑定。更清楚的是,在编译期间决定要调用或使用的方法或成员。

在你的例子中,第一次调用Animal.testClassMethod()是对static方法的调用,因此,非常确定将调用哪种方法。

在第二次调用myAnimal.testInstanceMethod()中,它调用非静态方法。这就是你所说的运行时多态。直到运行时才调用哪种方法。

如需进一步说明,请阅读this


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

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中的类方法。


-1
投票

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


18
投票

静态方法是隐藏的,非静态方法被覆盖。当调用不符合“something()”vs“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();
    }

}

OUTPUT:

animal.something
dog.eat

13
投票

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

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


3
投票

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

“这告诉我,在子类中编写一个静态方法,与父级同名,几乎可以覆盖。”

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


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 overrided 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) {  // overrided here  }

public static void Hiding(int i) {  // hidden
                                    // because it's static } 
}

记忆规则很简单:扩展类的方法不能将static更改为void,也不能将void更改为static。这将导致编译错误。

但是如果void Name改为void,则命名为Overriding。

如果静态名称更改为静态名称,则为隐藏名称。 (当编译器在超类的对象中看到静态方法时,它不检查子类中的方法。)


1
投票

在这段代码中,我使用'private'访问修饰符而不是'static'来显示隐藏方法和覆盖方法之间的区别。

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

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(Object o)。在这种情况下,Point的类等于dates't覆盖,但隐藏了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())
© www.soinside.com 2019 - 2024. All rights reserved.