Ghi đè và ẩn Java - Nhầm lẫn


88

Tôi bối rối về việc ghi đè khác với ẩn trong Java như thế nào. Bất cứ ai có thể cung cấp thêm chi tiết về cách chúng khác nhau? Tôi đã đọc Hướng dẫn Java nhưng mã mẫu vẫn khiến tôi bối rối.

Nói rõ hơn, tôi hiểu rõ việc ghi đè. Vấn đề của tôi là tôi không thấy ẩn có bất kỳ sự khác biệt nào, ngoại trừ thực tế là một cái ở cấp cá thể trong khi cái kia ở cấp lớp.

Nhìn vào mã hướng dẫn 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.");
    }
}

Sau đó, chúng tôi có một lớp con 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();
    }
}

Sau đó, họ nói:

Kết quả từ chương trình này như sau:

Phương thức lớp trong Animal.

Phương thức instance trong Cat.

Đối với tôi, việc gọi một phương thức lớp testClassMethod()trực tiếp từ Animallớp thực thi phương thức trong Animallớp là khá rõ ràng, không có gì đặc biệt ở đó. Sau đó, họ gọi testInstanceMethod()từ tham chiếu tới myCat, vì vậy một lần nữa khá rõ ràng rằng phương thức được thực thi sau đó là phương thức trong trường hợp của Cat.

Theo những gì tôi thấy, ẩn cuộc gọi hoạt động giống như ghi đè, vậy tại sao lại có sự khác biệt đó? Nếu tôi chạy mã này bằng các lớp ở trên:

Cat.testClassMethod();

Tôi sẽ nhận được: Phương thức lớp trong Cat. Nhưng nếu tôi xóa testClassMethod()khỏi Cat, thì tôi sẽ nhận được: Phương thức lớp trong Animal.

Điều này cho tôi thấy rằng việc viết một phương thức tĩnh, với cùng chữ ký như trong lớp cha, trong một lớp con thực hiện ghi đè khá nhiều.

Hy vọng rằng tôi đang làm rõ nơi tôi đang bối rối và ai đó có thể làm sáng tỏ. Cảm ơn rất nhiều trước!


Câu trả lời:


103

Ghi đè về cơ bản hỗ trợ ràng buộc muộn. Do đó, tại thời điểm chạy, phương thức nào sẽ được gọi. Nó dành cho các phương thức không tĩnh.

Ẩn dành cho tất cả các thành viên khác (phương thức tĩnh, thành viên thể hiện, thành viên tĩnh). Nó dựa trên sự ràng buộc ban đầu. Rõ ràng hơn, phương thức hoặc thành viên được gọi hoặc sử dụng được quyết định trong thời gian biên dịch.

Trong ví dụ của bạn, cuộc gọi đầu tiên, Animal.testClassMethod()là một cuộc gọi đến một staticphương thức, do đó bạn khá chắc chắn rằng phương thức nào sẽ được gọi.

Trong lần gọi thứ hai myAnimal.testInstanceMethod(), bạn gọi một phương thức không tĩnh. Đây là những gì bạn gọi là đa hình thời gian chạy. Nó không được quyết định cho đến thời gian chạy phương thức nào sẽ được gọi.

Để làm rõ hơn, hãy đọc Ghi đè Vs Ẩn .


3
Cảm ơn bạn đã trả lời nhanh chóng, điều này làm rõ nó! Tôi nhận thấy rằng trong ví dụ JavaRanch, họ đã sử dụng biến để gọi phương thức lớp thay vì sử dụng trực tiếp lớp, điều này làm cho nó dễ hiểu hơn. Tôi đoán trong hướng dẫn Java, họ đã sử dụng lớp trực tiếp vì sử dụng một thể hiện để gọi một phương thức tĩnh có lẽ không tốt, nhưng họ nên sử dụng myAnimal.testClassMethod () thay vì Animal.testClassMethod () .
Lostlinkpr

+1 để có thể diễn đạt nó thành lời đúng cách, thay vì bằng ví dụ! :)
WhyNotHugo

@Kazekage Gaara Có sự khác biệt giữa quá tải và ẩn không?
gstackoverflow

1
Tất nhiên tôi đồng ý với câu trả lời, nhưng làm thế nào về private methods? Không thể có chúng overriddenvì lớp con không biết về sự tồn tại của chúng .. Vì vậy chúng có thể là hiddenthay thế.
Paschalis

Câu trả lời xuất sắc! Mặc dù bạn có thể thêm ví dụ ở coderanch vì lợi ích của sự hoàn chỉnh :)
Shubham Mittal

19

Các phương thức tĩnh bị ẩn, các phương thức không tĩnh được ghi đè. Sự khác biệt là đáng chú ý khi các cuộc gọi không đủ điều kiện "something ()" so với "this.something ()".

Tôi thực sự không thể diễn đạt thành lời, vì vậy đây là một ví dụ:

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

}

ĐẦU RA:

animal.something
dog.eat

1
ok, sau đó điều gì sẽ xảy ra nếu tôi gọi `dog husky = new dog (); ' và gọi husky.Animal();nó sẽ in động vật.something hay dog.something ? tôi đoán nó sai khi nói rằng ** ** Đây sẽ luôn luôn gọi Animal.something ()
Amarnath Harish

@amarnathharish Bạn không thể làm .Animal(), hãy nhớ Animal()là một hàm tạo.
Dude156

Và như làm rõ thêm cho bất cứ ai tự hỏi, lý do something()Animal() luôn gọi của động vật của something()là do một cuộc gọi đến một phương pháp tĩnh được giải quyết trong thời gian biên dịch chứ không phải chạy theo thời gian. Điều này có nghĩa là cuộc gọi phương thức tĩnh Animal()luôn được gọi ngầm Animal.something(). Điều này khá trực quan nếu bạn nghĩ về nó: một lệnh gọi đến một phương thức tĩnh phải được đặt trước bởi một tên lớp (tức là className.staticMethodName()) trừ khi lệnh gọi nằm trong cùng một lớp.
Dude156

13

Đây là sự khác biệt giữa ghi đè và ẩn,

  1. Nếu cả hai phương thức trong lớp cha và lớp con đều là một phương thức thể hiện, thì nó được gọi là ghi đè.
  2. Nếu cả hai phương thức trong lớp cha và lớp con đều là phương thức tĩnh, nó được gọi là phương thức ẩn.
  3. Một phương thức không thể là static trong cha và như một instance trong con. và ngược lại.

nhập mô tả hình ảnh ở đây


3
Bạn cắt và dán bảng đó trực tiếp từ hướng dẫn mà OP nói là không giúp anh ta hiểu được.
lâu đài sói,

Bảng này làm cho nó rất rõ ràng, trong các ví dụ không phải tất cả các trường hợp đều được xem xét.
tutak

3

Nếu tôi hiểu đúng câu hỏi của bạn thì câu trả lời là "bạn đã ghi đè".

"Điều này cho tôi thấy rằng việc viết một phương thức tĩnh, có cùng tên như trong lớp cha, trong một lớp con thực hiện ghi đè khá nhiều."

Nếu bạn viết một phương thức trong một lớp con có tên giống hệt như một phương thức trong lớp cha, nó sẽ ghi đè phương thức của lớp cha đó. Chú thích @Override không bắt buộc phải ghi đè một phương thức. Tuy nhiên, nó làm cho mã của bạn dễ đọc hơn và buộc trình biên dịch phải kiểm tra xem bạn có đang thực sự ghi đè một phương thức (và không viết sai chính tả phương thức lớp con không).


1
Câu trả lời này không giải quyết được các phương thức instance so với static liên quan đến ghi đè / ẩn.
Paul Bellora

3

Ghi đè chỉ xảy ra với các phương thức cá thể. Khi kiểu của biến tham chiếu là Animal và đối tượng là Cat thì phương thức instance được gọi từ Cat (đây là ghi đè). Đối với cùng một đối tượng acat, phương thức lớp Animal được sử dụng.

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

}

Đầu ra là:

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 */ } 
}

Quy tắc ghi nhớ rất đơn giản: một phương thức trong một lớp mở rộng không thể thay đổi static thành void và không thể thay đổi void thành static. Nó sẽ gây ra lỗi biên dịch.

Nhưng nếu void Nameđược thay đổi thành void NameGhi đè.

Và nếu static Nameđược đổi thành static Nameđó là Ẩn. (Cả phương thức tĩnh của lớp con cũng như phương thức của lớp cha đều có thể được gọi, tùy thuộc vào loại tham chiếu được sử dụng để gọi phương thức.)


1

Trong đoạn mã này, tôi sử dụng công cụ sửa đổi quyền truy cập 'riêng tư' thay vì 'tĩnh' để cho bạn thấy sự khác biệt giữa phương pháp ẩn và phương pháp ghi đè.

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

Đầu ra:

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

tự hỏi tại sao nó không nhận được câu trả lời ủng hộ..much được đánh giá cao.
amarnath harish

0

Dựa trên các nghiên cứu Java gần đây của tôi

  • ghi đè phương thức , khi lớp con có cùng một phương thức với cùng một chữ ký trong lớp con.
  • Phương thức ẩn , khi lớp con có cùng tên phương thức, nhưng khác tham số. Trong trường hợp này, bạn không ghi đè phương thức cha mà là ẩn nó.

Ví dụ từ sách OCP Java 7, trang 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
  }
}

nhưng nếu chúng ta viết chính sau:

  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
  }

Trong main thứ hai, chúng ta sử dụng lớp Object làm kiểu tĩnh, vì vậy khi chúng ta gọi phương thức bằng trong đối tượng Point, nó đang đợi một lớp Point đến dưới dạng tham số, nhưng Object sẽ đến. Vì vậy, lớp Object bằng phương thức đang chạy, bởi vì chúng ta có một bằng (Đối tượng o) ở đó. Trong trường hợp này, lớp của Point = dosen không ghi đè, nhưng ẩn phương thức Equals của lớp Object .


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

Trang hướng dẫn java được liên kết giải thích khái niệm ghi đè và ẩn

Một phương thức thể hiện trong lớp con có cùng chữ ký (tên, cộng với số và kiểu tham số của nó) và kiểu trả về như một phương thức thể hiện trong lớp cha sẽ ghi đè phương thức của lớp cha.

Nếu một lớp con định nghĩa một phương thức tĩnh có cùng chữ ký với một phương thức tĩnh trong lớp cha, thì phương thức trong lớp con sẽ ẩn một phương thức trong lớp cha.

Sự khác biệt giữa ẩn một phương thức tĩnh và ghi đè một phương thức thể hiện có ý nghĩa quan trọng:

  1. Phiên bản của phương thức phiên bản ghi đè được gọi là phiên bản trong lớp con.
  2. Phiên bản của phương thức tĩnh ẩn được gọi phụ thuộc vào việc nó được gọi từ lớp cha hay lớp con.

Quay lại ví dụ của bạn:

Animal myAnimal = myCat;

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

 /* invokes child class instance method (non-static - it's overriding) */
 myAnimal.testInstanceMethod();

Câu lệnh trên không hiển thị ẩn.

Bây giờ hãy thay đổi mã như bên dưới để có đầu ra khác:

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

Tôi đang cố gắng hiểu bản thân từ ẩn nghĩa là gì. Cái gì đang che giấu cái gì? Phương thức static trong lớp Parent có bị ẩn vì (ít được mong đợi nhất) nó được gọi không? Hay phương thức tĩnh trong lớp Con bị ẩn, vì nó không được gọi?
scorpion

0

Ngoài các ví dụ được liệt kê ở trên, đây là một mã mẫu nhỏ để làm rõ sự khác biệt giữa ẩn và ghi đè:

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

Lời gọi của các child.printParent()đầu ra:
được ẩn: Parent
được ghi đè: Child

Lệnh gọi child.printChild()kết quả đầu ra:
được ẩn: Con
bị ghi đè: Con

Như bạn có thể thấy từ các đầu ra ở trên (đặc biệt là các đầu ra được đánh dấu đậm), phương thức ẩn hoạt động khác với ghi đè.

Java chỉ cho phép ẩn và ghi đè các phương thức. Quy tắc tương tự không áp dụng cho các biến. Không được phép ghi đè biến, do đó chỉ có thể ẩn các biến (không có sự khác biệt giữa biến tĩnh hoặc không tĩnh). Ví dụ dưới đây cho thấy phương thức getName()bị ghi đè và biến namebị ẩn như thế nào :

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

Trong thời gian chạy, phiên bản con của một phương thức được ghi đè luôn được thực thi cho một phiên bản bất kể việc gọi phương thức là defi ned trong phương thức lớp cha hay lớp con. Theo cách này, phương thức cha không bao giờ được sử dụng trừ khi một lệnh gọi rõ ràng đến phương thức cha được tham chiếu, sử dụng cú pháp ParentClassName.method (). Ngoài ra, trong thời gian chạy, phiên bản cha của một phương thức ẩn luôn được thực thi nếu lệnh gọi phương thức được định nghĩa trong lớp cha.


0

Trong phương pháp Ghi đè , việc phân giải phương thức được thực hiện bởi JVM trên cơ sở đối tượng thời gian chạy. Trong khi ẩn phương thức, việc phân giải phương thức được thực hiện bởi trình biên dịch trên cơ sở tham chiếu. Vì vậy,

Nếu mã đã được viết là,

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

Đầu ra sẽ như sau:
Phương thức lớp trong Animal.


0

Nó được gọi là ẩn vì trình biên dịch ẩn việc thực hiện phương thức siêu lớp, khi lớp con có cùng phương thức tĩnh.

Trình biên dịch không có khả năng hiển thị hạn chế đối với các phương thức bị ghi đè và chỉ trong thời gian chạy, nó mới quyết định phương thức nào được sử dụng.


0

Đây là sự khác biệt giữa ghi đè và ẩn:

Animal a = new Cat ();

a.testClassMethod () sẽ gọi phương thức trong lớp cha vì nó là một ví dụ về ẩn phương thức. Phương thức được gọi được xác định bởi kiểu của biến tham chiếu và được quyết định tại thời điểm biên dịch.

a.testInstanceMethod () sẽ gọi phương thức trong lớp con vì nó là một ví dụ về ghi đè phương thức. Phương thức được gọi được xác định bởi đối tượng được sử dụng để gọi phương thức trong thời gian chạy.


-1

Làm thế nào là ẩn phương thức tĩnh xảy ra trong java? Lớp Mèo đang mở rộng lớp Động vật. Vì vậy, trong lớp Cat sẽ có cả phương thức tĩnh (ý tôi là phương thức tĩnh của lớp Con và phương thức tĩnh của lớp cha) Nhưng làm thế nào để JVM ẩn phương thức tĩnh của Parent? Nó xử lý như thế nào trong Heap và Stack?


Đây không phải là một câu trả lời. Đó là phần mở rộng của câu hỏi được hỏi. Bản thân nó có thể là một câu hỏi riêng biệt hoặc một phần nhận xét về câu hỏi.
Sri9911
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.