覆盖与隐藏Java - 困惑

时间:2012-05-15 04:12:53

标签: java inheritance methods override

我对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();
    }
}

然后他们说:

  

该程序的输出如下:

     

动物中的类方法。

     

Cat。

中的实例方法

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

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

Cat.testClassMethod();

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

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

希望我清楚地说明了我困惑的地方,有人可以解释一下。非常感谢提前!

16 个答案:

答案 0 :(得分:91)

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

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

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

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

答案 1 :(得分:17)

隐藏静态方法,重写非静态方法。 当调用不符合“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();
    }

}

输出:

animal.something
dog.eat

答案 2 :(得分:12)

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

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

答案 3 :(得分:3)

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

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

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

答案 4 :(得分: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.

答案 5 :(得分: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 } 
}

记忆规则很简单:扩展类的方法 不能将静态变为无效和 不能将void改为static。 这将导致编译错误。

但如果 void Name 更改为 void Name ,则覆盖它。

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

答案 6 :(得分:1)

在此代码段中,我使用了#private;#private;访问修饰符而不是'静态'向您展示隐藏方法和重写方法之间的区别。

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

答案 7 :(得分: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())

答案 8 :(得分:0)

链接的java教程页面解释了覆盖和隐藏

的概念
  

具有相同签名(名称,加上其参数的数量和类型)的子类中的实例方法和作为超类中的实例方法的返回类型将覆盖超类的方法。

     

如果子类定义的静态方法与超类中的静态方法具有相同的签名,则子类中的方法会隐藏超类中的方法。

隐藏静态方法和覆盖实例方法之间的区别具有重要意义:

  1. 被调用的重写实例方法的版本是子类中的版本。
  2. 被调用的隐藏静态方法的版本 取决于它是从超类还是从子类调用。
  3. 回到你的榜样:

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

答案 9 :(得分: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;
    }
}

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

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

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

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

答案 12 :(得分:0)

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

对于被覆盖的方法,编译器没有可见性的限制,只有在运行时才能确定使用哪种方法。

答案 13 :(得分:0)

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

动物a = new Cat();

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

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

答案 14 :(得分:0)

我认为这还没有完全解释。 请参见以下示例。

<detectOfflineLinks>false</detectOfflineLinks>

输出如下。

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

答案 15 :(得分:-1)

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