Переопределение и скрытие Java - Confused

Я запутался в том, как Overriding отличается от Hiding в Java. Кто-нибудь может предоставить более подробную информацию о том, как они отличаются? Я прочитал Учебник по Java, но пример кода все еще оставил меня в замешательстве

Чтобы быть более ясным, я хорошо понимаю Overriding. Моя проблема в том, что я не вижу, чтобы скрытие отличалось, за исключением того факта, что один находится на уровне экземпляра, а другой - на уровне класса.

Глядя на учебный код 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.

Метод экземпляра в кат.

Для меня тот факт, что вызов метода класса testClassMethod() непосредственно из класса Animal, выполняет метод в классе Animal, довольно очевиден, ничего особенного там нет. Затем они вызывают testInstanceMethod() из ссылки на myCat, так что, опять же, довольно очевидно, что выполняемый метод является тем же, что и в случае с Cat.

Из того, что я вижу, скрытие вызова ведет себя как переопределение, так зачем делать это различие. Если я запускаю этот код, используя классы выше:

Cat.testClassMethod();

Я получу: метод класса в Cat. Но если я удалю testClassMethod() из Cat, то получу: Метод класса в Animal.

Что показывает мне, что написание статического метода с той же сигнатурой, что и у родителя, в подклассе в значительной степени выполняет переопределение.

Надеюсь, я проясню, где я в замешательстве, и кто-то может пролить свет. Большое спасибо заранее!

Ответ 1

Перегрузка в основном поддерживает позднюю привязку. Поэтому, какой метод будет вызываться, определяется во время выполнения. Это для нестатических методов. Скрытие для всех остальных участников (статические методы, члены экземпляра, статические члены). Он основан на раннем связывании. Более четко, метод или член, который будет вызываться или используется, определяется во время компиляции.

В вашем примере первый вызов Animal.testClassMethod() - это вызов метода static, следовательно, он уверен в том, какой метод будет вызываться.

Во втором вызове myAnimal.testInstanceMethod() он вызывает нестатический метод. Это то, что вы называете полиморфизмом во время выполнения. Это не определено до тех пор, пока время выполнения не будет вызвано.

Для дальнейшего уточнения прочитайте this.

Ответ 2

Статические методы скрыты, нестатические методы переоцениваются. Разница заметна, когда вызовы не квалифицируются "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

Ответ 3

В этом разница между переопределениями и скрытием,

  • Если оба метода в родительском классе и дочернем классе являются методом экземпляра, он вызывает переопределения.
  • Если оба метода в родительском классе и дочернем классе являются статическим методом, он вызывает скрытие.
  • Один метод не может быть статичным в родительском и в качестве экземпляра в дочернем. и наоборот.

enter image description here

Ответ 4

Если я правильно понимаю ваш вопрос, тогда ответ будет "вы уже переопределяете".

"Что показывает мне, что запись статического метода с тем же именем, что и в родительском, в подклассе в значительной степени делает переопределение."

Если вы пишете метод в подклассе с точно таким же именем, как метод в суперклассе, он переопределит метод суперкласса. Аннотации @Override не требуется переопределять метод. Однако он делает ваш код более читабельным и заставляет компилятор проверить, что вы фактически переопределяете метод (и, например, не пропустили метод подкласса).

Ответ 5

Переопределение происходит только с помощью методов экземпляра. Когда тип ссылочной переменной 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.

Ответ 6

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

public class Second extends First {

public void Overriding(int i) {  // overrided here  }

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

Правило для запоминания прост: метод расширения класса не может изменить статическую на void и не может изменить void на static. Это приведет к ошибке компиляции.

Но если void Name изменено на void Name it Overriding.

И если статическое имя изменилось на статическое имя, оно скрывается. (Когда компилятор видит статический метод в объекте суперкласса, тогда он не проверяет метод в подклассе.)

Ответ 7

В этом фрагменте кода я использую '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

Ответ 8

Основываясь на моих последних 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 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 class equals method get invoked
  }

Во втором основном мы используем класс Object как статический тип, поэтому, когда мы вызываем равный метод в объекте Point, он ожидает появления класса Point в качестве параметра, но Object coming. Таким образом, класс Object равен методу get run, потому что у нас есть equals (Object o). В этом случае класс Point равен dosen't переопределяет, но скрывает класс Object равно методу.

Ответ 9

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

Ответ 10

Страница связанного java-учебника объясняет концепцию переопределения и скрытия

Метод экземпляра в подклассе с той же сигнатурой (имя, плюс число и тип его параметров) и возвращаемый тип как метод экземпляра в суперклассе переопределяет метод суперкласса.

Если подкласс определяет статический метод с той же сигнатурой, что и статический метод в суперклассе, то метод в подклассе скрывает тот, что находится в суперклассе.

Различие между скрытием статического метода и переопределением метода экземпляра имеет важные последствия:

  • Версия метода переопределенного экземпляра, который вызывается, является таковой в подклассе.
  • Версия скрытого статического метода, который вызывается , зависит от того, вызвана ли она из суперкласса или подкласса.

Возвращаясь к вашему примеру:

Animal myAnimal = myCat;

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

 /* invokes child class instance method (non-static - it 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 overriding) */
  myAnimal.testInstanceMethod();

Ответ 11

В дополнение к приведенным выше примерам, здесь приведен небольшой пример кода, чтобы прояснить различие между скрытием и переопределением:

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

Ответ 12

Во время выполнения дочерняя версия переопределенного метода всегда выполняется для экземпляра независимо от того, установлен ли вызов метода в методе родительского или дочернего класса. Таким образом, родительский метод никогда не используется, если не указан явный вызов родительского метода, используя синтаксис ParentClassName.method(). Кроме того, во время выполнения родительская версия скрытого метода всегда выполняется, если вызов метода определен в родительском классе.

Ответ 13

В переопределении метода разрешение метода выполняется JVM на основе объекта времени выполнения. Принимая во внимание, что при скрытии метода разрешение метода выполняется компилятором на основе ссылки. Таким образом,

Если код был бы написан как,

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

Результат будет следующим:
Метод класса в Animal.

Ответ 14

Это называется скрытием, потому что компилятор скрывает реализацию метода суперкласса, когда подкласс имеет тот же статический метод.

Компилятор не имеет ограниченной видимости для переопределенных методов и только во время выполнения определяет, какой из них используется.

Ответ 15

Как скрывается статический метод в java? Класс Cat расширяет класс животных. Таким образом, в классе Cat будут использоваться как статические методы (я имею в виду статический метод дочернего класса и статический метод родительского класса) Но как JVM скрывает родительский статический метод? Как это имеет дело с кучей и стеком?