2012-05-15 2 views
57

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

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

Глядя на код учебник 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."); 
    } 
} 

Тогда мы имеем подкласс кота:

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.

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

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

Cat.testClassMethod(); 

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

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

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

ответ

80

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

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

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

Для уточнения уточните, пожалуйста, this.

+1

Благодарим за быстрый ответ, это разъясняет это! Я заметил, что в примере JavaRanch они использовали переменную, чтобы вызвать метод класса, а не использовать класс, который упрощает его понимание. Думаю, что в учебнике Java они использовали класс напрямую, потому что использование экземпляра для вызова статического метода, вероятно, не является хорошей практикой, но они должны были использовать _myAnimal.testClassMethod() _ вместо _Animal.testClassMethod() _. – Lostlinkpr

+0

+1 за то, что он может правильно записать его словами, а не примером! :) – WhyNotHugo

+0

@ Kazekage Gaara Есть ли разница между перегрузкой и скрытием? – gstackoverflow

3

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

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

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

+1

Этот ответ не решает экземпляр против статических методов в отношении переопределяя/скрытия. –

5

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

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

enter image description here

+2

Вы вырезали и вставляли этот стол непосредственно из учебника, который сказал ОП, не помогли ему понять. – wolfcastle

+0

Таблица делает это очень ясным, в примерах не учитывались не все случаи. – tutak

11

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

Замещение происходит только с методами экземпляра. Если тип ссылочной переменной 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 } 
} 

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

Но если пустота Имя изменено ничтожного Имени этого переопределение.

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

0

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

0

на основе моего недавнего Java изучает

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

Пример из ОСР 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, он ожидает появления класса Point в качестве параметра, но Object coming. Таким образом, класс Object равен методу get run, потому что у нас есть equals (Object o). В этом случае класс Point равен dosen't переопределяет, но скрывает метод класса Object равным.

0

В этом фрагменте кода я использую «частный» модификатор доступа вместо «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
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 Tutorial страница объясняет концепцию переопределение и скрытие

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

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

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

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