Phương thức nào không thể kế thừa trong Java?

Kế thừa Java định nghĩa mối quan hệ is-a giữa một lớp cha và các lớp con của nó. Điều này có nghĩa là một Đối tượng của lớp con có thể được sử dụng ở bất cứ nơi nào có thể sử dụng đối tượng của lớp cha. Kế thừa lớp trong Java được sử dụng để xây dựng các Lớp mới từ các Lớp hiện có. Quan hệ thừa kế mang tính bắc cầu. nếu Lớp x mở rộng Lớp y, thì Lớp z, mở rộng Lớp x, cũng sẽ kế thừa từ Lớp y

Ví dụ: một Lớp xe hơi có thể kế thừa một số thuộc tính từ một Lớp xe nói chung. Ở đây chúng tôi thấy rằng Lớp cơ sở là Lớp xe và lớp con là Lớp xe cụ thể hơn. Một lớp con phải sử dụng mệnh đề mở rộng để dẫn xuất từ ​​một siêu lớp phải được viết trong tiêu đề của định nghĩa lớp con. Lớp con kế thừa các thành viên của lớp cha và do đó thúc đẩy tái sử dụng mã. Bản thân lớp con có thể thêm hành vi và thuộc tính mới của nó. java. lang thang. Lớp đối tượng luôn đứng đầu bất kỳ hệ thống phân cấp kế thừa Lớp nào

Điều gì là không thể khi sử dụng Kế thừa lớp Java?

  1. Các thành viên riêng của lớp cha không được kế thừa bởi lớp con và chỉ có thể được truy cập gián tiếp

  2. Vì các hàm tạo và khối khởi tạo không phải là thành viên của Lớp nên chúng không được kế thừa bởi lớp con

  3. Một lớp con chỉ có thể mở rộng một lớp cha

  4. Các thành viên có khả năng truy cập mặc định trong lớp cha cũng không được kế thừa bởi các lớp con trong các gói khác, vì các thành viên này chỉ có thể truy cập bằng tên đơn giản của chúng trong các lớp con trong cùng gói với lớp cha

cái này và siêu từ khóa

Hai từ khóa this và super giúp bạn đặt tên rõ ràng cho trường hoặc Phương thức mà bạn muốn. Sử dụng từ khóa này và siêu bạn có toàn quyền kiểm soát việc gọi một Phương thức hoặc trường trong cùng một Lớp hay gọi từ lớp cha trực tiếp. từ khóa này được sử dụng làm tham chiếu đến Đối tượng hiện tại, là một thể hiện của Lớp hiện tại. Từ khóa super cũng tham chiếu đến Đối tượng hiện tại, nhưng là một thể hiện của lớp cha của lớp hiện tại

từ khóa này tham chiếu đến Đối tượng hiện tại và rất hữu ích trong các tình huống khi một biến cục bộ ẩn hoặc che khuất một trường có cùng tên. Nếu một Phương thức cần chuyển Đối tượng hiện tại sang một Phương thức khác, thì nó có thể làm như vậy bằng cách sử dụng tham chiếu này. Lưu ý rằng tham chiếu này không thể được sử dụng trong ngữ cảnh tĩnh, vì mã tĩnh không được thực thi trong ngữ cảnh của bất kỳ Đối tượng nào

Trong số các khái niệm quan trọng nhất trong lập trình hướng đối tượng là các khái niệm về kế thừa và đa hình. Kế thừa lớp là cơ chế theo đó một lớp có được (kế thừa) các phương thức và biến của các lớp cha của nó. Hãy xem xét một ví dụ về hình thức thừa kế tự nhiên. Giống như ngựa kế thừa các thuộc tính và hành vi liên quan đến động vật có vú và động vật có xương sống, một lớp con Java kế thừa các thuộc tính và hành vi của các lớp cha của nó

Phương thức nào không thể kế thừa trong Java?

Như thể hiện trong sơ đồ trên, gốc của hệ thống phân cấp, luôn được hiển thị ở trên cùng, lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 chứa các thuộc tính chung nhất, chẳng hạn như còn sống và có thể di chuyển. Tất cả các loài động vật chia sẻ các thuộc tính này. Lớp động vật có xương sống là một loại động vật chuyên biệt hơn một chút, trong đó động vật có xương sống có xương sống. Tương tự, lớp động vật có vú là một chuyên ngành sâu hơn so với động vật có xương sống ở chỗ động vật có vú là loài máu nóng và nuôi con non. Cuối cùng, lớp ngựa là một chuyên ngành sâu hơn so với lớp động vật có vú, trong đó tất cả các con ngựa đều có bốn chân. Một số động vật có vú, chẳng hạn như con người và chim cánh cụt, không có bốn chân. Do đó, nhờ vị trí của lớp trong hệ thống phân cấp này, chúng ta có thể suy ra rằng ngựa là động vật có xương sống bốn chân, di chuyển, sống, có máu nóng và nuôi con non.

Chúng tôi đã cố tình sử dụng một ví dụ từ thế giới tự nhiên để chỉ ra rằng khái niệm kế thừa trong Java được lấy cảm hứng từ đối tác của nó trong thế giới tự nhiên. Nhưng chính xác thì khái niệm kế thừa áp dụng cho Java (và cho các ngôn ngữ hướng đối tượng khác) như thế nào?

Sử dụng một phương pháp kế thừa

Trong Java, các phương thức đối tượng công khai và được bảo vệ cũng như các biến đối tượng của một lớp cha được kế thừa bởi tất cả các lớp con của nó. Điều này có nghĩa là các đối tượng thuộc các lớp con có thể sử dụng các biến và phương thức kế thừa như của chính chúng.

Ví dụ: tất cả các lớp Java đều là lớp con của lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2, đây là lớp tổng quát nhất trong hệ thống phân cấp lớp của Java. Một phương thức public được định nghĩa trong lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2 là phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Bởi vì mọi lớp trong hệ thống phân cấp Java đều là lớp con của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2, nên mọi lớp đều kế thừa phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Do đó,
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 có thể được sử dụng với bất kỳ đối tượng Java nào

Giả sử chúng ta định nghĩa một lớp Sinh viên như sau

public class Student {
    protected String name;
    public Student(String s) {
        name = s;
    }
    public String getName() {
        return name;
    }
}

Phương thức nào không thể kế thừa trong Java?

Hình trên cho thấy mối quan hệ giữa lớp này và lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2. Là một lớp con của Object, lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 kế thừa phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Do đó, đối với một đối tượng Sinh viên nhất định, chúng ta có thể gọi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 của nó như sau

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2

Cái này hoạt động ra sao?

Lưu ý trong ví dụ này rằng biến stu được khai báo là kiểu

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 và được gán một thể hiện của lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9. Khi biểu thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
76 được thực thi, đầu tiên Java sẽ tìm trong lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 để tìm định nghĩa của phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Không tìm thấy cái nào ở đó, nó sẽ tìm kiếm trong hệ thống phân cấp lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 (Hình. số 8. 2) cho đến khi tìm thấy định nghĩa công khai hoặc được bảo vệ của phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Trong trường hợp này, nó tìm thấy một phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 trong lớp Đối tượng và nó thực thi triển khai của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 đó

Ghi đè một phương thức kế thừa

Việc triển khai mặc định của

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 trả về tên của lớp đối tượng và địa chỉ (e. g.
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
54) nơi đối tượng được lưu trữ trong bộ nhớ. Tuy nhiên, loại kết quả này quá chung chung và không đặc biệt hữu ích

Phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 được thiết kế để ghi đè—nghĩa là được định nghĩa lại trong các lớp con của Đối tượng. Ghi đè
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 trong một lớp con cung cấp một biểu diễn chuỗi tùy chỉnh của các đối tượng trong lớp con đó

Để ghi đè

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 cho lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9, hãy thêm định nghĩa phương thức sau vào lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}

Với sự thay đổi này, hệ thống phân cấp lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 đã sửa đổi được hiển thị trong hình bên dưới. Lưu ý rằng cả Object và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 đều chứa các triển khai của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Bây giờ khi biểu thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
76 được gọi, đầu ra sau đây, nhiều thông tin hơn, sẽ được tạo

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
7

Trong trường hợp này, khi Java gặp lệnh gọi phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
76, nó sẽ gọi phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 mà nó tìm thấy trong lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9

Phương thức nào không thể kế thừa trong Java?

Liên kết tĩnh và liên kết động

Cơ chế mà Java sử dụng trong các ví dụ này được gọi là liên kết động, trong đó liên kết giữa lệnh gọi phương thức và triển khai phương thức chính xác được thực hiện trong thời gian chạy. Trong liên kết động, một cuộc gọi phương thức được liên kết với việc triển khai đúng phương thức trong thời gian chạy bởi Máy ảo Java (JVM)

Liên kết động tương phản với liên kết tĩnh, cơ chế mà trình biên dịch Java giải quyết mối liên kết giữa lệnh gọi phương thức và triển khai phương thức chính xác khi chương trình được biên dịch. Để liên kết động hoạt động, JVM cần duy trì một số loại biểu diễn của hệ thống phân cấp lớp Java, bao gồm các lớp do lập trình viên định nghĩa. Khi JVM gặp một lệnh gọi phương thức, nó sẽ sử dụng thông tin về hệ thống phân cấp lớp để liên kết lệnh gọi phương thức với việc triển khai đúng phương thức đó

Trong Java, tất cả các lệnh gọi phương thức đều sử dụng liên kết động ngoại trừ các phương thức được khai báo là cuối cùng hoặc riêng tư. Các phương thức cuối cùng không thể bị ghi đè, vì vậy việc khai báo một phương thức là cuối cùng có nghĩa là trình biên dịch Java có thể liên kết nó với cách triển khai chính xác. Tương tự, các phương thức riêng tư không được kế thừa và do đó không thể ghi đè trong một lớp con. Trên thực tế, các phương thức riêng tư là các phương thức cuối cùng và trình biên dịch có thể thực hiện liên kết tại thời điểm biên dịch

đa hình

Cơ chế liên kết động của Java, còn được gọi là liên kết muộn hoặc liên kết thời gian chạy, dẫn đến cái được gọi là tính đa hình. Tính đa hình là một tính năng của các ngôn ngữ hướng đối tượng, theo đó cùng một lệnh gọi phương thức có thể dẫn đến các hành vi khác nhau tùy thuộc vào loại đối tượng mà lệnh gọi phương thức được thực hiện. Thuật ngữ đa hình có nghĩa là, theo nghĩa đen, có nhiều (đa) hình dạng (hình thái). Đây là một ví dụ đơn giản

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
5

Biến

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17 được khai báo là kiểu
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2. Đây là loại tĩnh hoặc khai báo của nó. Kiểu tĩnh của một biến không bao giờ thay đổi. Tuy nhiên, một biến cũng có kiểu thực hoặc kiểu động. Đây là loại thực tế của đối tượng đã được gán cho biến. Như bạn đã biết, một biến Đối tượng có thể được gán đối tượng từ bất kỳ lớp con Đối tượng nào. Trong câu lệnh thứ hai,
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17 được gán một đối tượng Sinh viên. Do đó, tại thời điểm này trong chương trình, kiểu thực sự của biến
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17 là Sinh viên. Khi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
51 được gọi ở dòng thứ ba, Java bắt đầu tìm kiếm phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 tại lớp Sinh viên, vì đó là kiểu thực của biến

Ở dòng thứ tư, chúng ta gán một đối tượng lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
53 khác cho
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17, do đó thay đổi kiểu thực tế của nó thành
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
53. Do đó, khi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
56() được gọi ở dòng cuối cùng, phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 bị ràng buộc với triển khai được tìm thấy trong lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
53

Do đó, chúng ta thấy rằng cùng một biểu thức,

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
51, được liên kết luân phiên với hai triển khai
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 khác nhau, dựa trên loại thực tế của đối tượng,
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17, trên đó nó được gọi. Đây là đa hình

Điểm quan trọng ở đây là tính đa hình xảy ra khi một phương thức bị ghi đè được gọi trên một biến siêu lớp, e. g. ,

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17. Trong trường hợp như vậy, việc triển khai phương thức thực tế được gọi được xác định trong thời gian chạy. Việc xác định phụ thuộc vào loại đối tượng được gán cho biến. Do đó, chúng tôi nói rằng cuộc gọi phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
51 là đa hình vì nó được liên kết với các triển khai khác nhau của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 tùy thuộc vào loại thực tế của đối tượng được liên kết với
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
17

Đa hình và thiết kế hướng đối tượng

Bây giờ chúng ta đã hiểu cách hoạt động của tính kế thừa và tính đa hình trong Java, sẽ rất hữu ích khi xem xét một ví dụ minh họa cách các cơ chế này có thể hữu ích trong việc thiết kế các lớp và phương thức.

Các phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
06 và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
07 là ví dụ về các phương thức quá tải, nghĩa là các phương thức có cùng tên nhưng danh sách tham số khác nhau. Hãy nhớ rằng chữ ký của một phương thức liên quan đến tên của nó, cộng với loại, số và thứ tự của các tham số của nó. Các phương thức có cùng tên nhưng khác tham số được gọi là quá tải

Dưới đây là chữ ký của một số phương thức print() và println() khác nhau

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
1

Về cơ bản, có một phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
08 và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
09 cho mọi loại dữ liệu nguyên thủy, cộng với các phương thức để in bất kỳ loại đối tượng nào. Khi Java gặp một biểu thức liên quan đến
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
08 hoặc
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
09, nó sẽ chọn phương thức cụ thể để gọi. Để xác định phương thức chính xác, Java dựa vào sự khác biệt trong chữ ký của các phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
08 khác nhau. Ví dụ: vì đối số của nó là một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
83, nên biểu thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
84 được liên kết với phương thức có chữ ký là
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
85 vì tham số của nó là một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
83

Sử dụng public String toString() { return "My name is " + name + " and I am a Student."; } 1 để tham khảo Superclass

Một câu hỏi có thể xảy ra với bạn là. Khi bạn ghi đè phương thức mặc định

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4, thì có thể gọi phương thức mặc định trên đối tượng Sinh viên không? . Ví dụ, giả sử rằng trong lớp Student, bạn muốn nối kết quả của cả phương thức mặc định và phương thức mới
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Biểu thức sau đây sẽ thực hiện điều đó

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
5

Từ khóa

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
1 chỉ định rằng
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 đầu tiên là từ khóa được triển khai trong siêu lớp.
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 thứ hai đơn giản đề cập đến phiên bản được triển khai trong lớp. Chúng ta sẽ xem thêm các ví dụ về cách sử dụng từ khóa super trong các phần sau

Kế thừa và Constructor

Cơ chế kế thừa của Java áp dụng cho các biến và phương thức thể hiện công khai và được bảo vệ của một lớp. Nó không áp dụng cho các hàm tạo của một lớp. Để minh họa một số ý nghĩa của tính năng ngôn ngữ này, hãy định nghĩa một lớp con của

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 được gọi là
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
15

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0

Bởi vì

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
15 là một lớp con của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9, nên nó kế thừa các phương thức và biến thể hiện công khai và được bảo vệ từ
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9. Vì vậy, một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
15 có một biến đối tượng là name và nó có một phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
200 công khai. Nhớ lại rằng một phần tử được bảo vệ, chẳng hạn như biến tên trong lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9, chỉ có thể truy cập được trong lớp và các lớp con của nó. Không giống như các phần tử công khai, nó không thể truy cập được đối với các lớp khác

Lưu ý cách chúng tôi đã triển khai hàm tạo

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
202. Bởi vì các hàm tạo của lớp cha không được kế thừa, chúng ta phải triển khai hàm tạo này trong lớp con nếu chúng ta muốn có thể gán tên của CollegeStudent trong quá trình xây dựng đối tượng. Cuộc gọi phương thức,
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
203, được sử dụng để gọi phương thức khởi tạo của lớp bậc trên và chuyển nó vào ____1204, tên của học sinh. Hàm tạo của lớp bậc trên sau đó sẽ gán
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
204 cho biến tên

Như chúng ta đã lưu ý, một lớp con không kế thừa các hàm tạo từ các lớp cha của nó. Tuy nhiên, nếu hàm tạo của lớp con không gọi hàm tạo của lớp bậc trên một cách rõ ràng, thì Java sẽ tự động gọi hàm tạo của lớp bậc trên mặc định—trong trường hợp này là

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
206. Theo hàm tạo siêu lớp mặc định, chúng tôi muốn nói đến hàm tạo không có tham số. Đối với một lớp con nằm dưới một vài lớp trong hệ thống phân cấp, việc gọi hàm tạo
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
206 tự động này sẽ được lặp lại lên trên trong toàn bộ hệ thống phân cấp của lớp. Do đó, khi một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
15 được xây dựng, Java sẽ tự động gọi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
209 và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
210. Lưu ý rằng nếu một trong các lớp cha không chứa hàm tạo mặc định, điều này sẽ dẫn đến lỗi cú pháp

Nếu bạn nghĩ về điều này, nó có ý nghĩa. Làm thế nào khác các yếu tố kế thừa của đối tượng sẽ được tạo ra? . Hàm tạo

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
15 sau đó mở rộng định nghĩa của lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9. Tương tự, để một đối tượng
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9 có các thuộc tính chung cho tất cả các đối tượng, một thể hiện
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
2 phải được tạo và sau đó mở rộng thành một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
9

Do đó, trừ khi một hàm tạo gọi một hàm tạo của lớp bậc trên một cách rõ ràng, Java sẽ tự động gọi các hàm tạo của lớp bậc trên mặc định. Nó thực hiện điều này trước khi thực thi mã trong hàm tạo của chính nó. Ví dụ: nếu bạn có hai lớp,

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
220 và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221, trong đó
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221 là lớp con của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
220, thì bất cứ khi nào bạn tạo một thể hiện của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221, trước tiên, Java sẽ gọi hàm tạo của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
220 trước khi thực thi mã trong hàm tạo của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221 để lớp cơ sở được khởi tạo. Do đó, hành vi mặc định của Java trong quá trình xây dựng
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221 tương đương với việc triển khai hàm tạo của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
221 sau đây

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
8

Ví dụ,

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
1

Nó sẽ in

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
20

Các lớp trừu tượng, giao diện và đa hình

Trong Java, có ba loại đa hình

  • Ghi đè một phương thức kế thừa
  • Thực hiện một phương pháp trừu tượng
  • Triển khai giao diện Java

Trong phần trước chúng ta đã thấy các ví dụ về loại đa hình đầu tiên. Tất cả các dạng đa hình đều dựa trên cơ chế liên kết động của Java. Trong phần này, chúng tôi sẽ phát triển một ví dụ minh họa hai loại đa hình khác và thảo luận về một số hàm ý thiết kế liên quan đến việc chọn một hoặc một cách tiếp cận khác

Triển khai một phương thức trừu tượng

Một tính năng quan trọng của tính đa hình là khả năng gọi một phương thức đa hình đã được định nghĩa một cách trừu tượng trong lớp cha. Để minh họa tính năng này, chúng tôi sẽ phát triển một hệ thống phân cấp các động vật mô phỏng tạo ra âm thanh đặc trưng của động vật, một ví dụ được sử dụng rộng rãi để minh họa tính đa hình

Như chúng ta đã biết từ thời thơ ấu, động vật có những cách nói đặc biệt. Con bò kêu “moo”; . Hãy thiết kế một hệ thống cấp bậc của các loài động vật mô phỏng đặc điểm này bằng cách in các âm thanh đặc trưng mà các loài động vật này tạo ra. Chúng tôi muốn thiết kế các lớp học của mình sao cho bất kỳ con vật nào cũng sẽ trả về một cái gì đó giống như “Tôi là một con bò và tôi đi moo,” khi chúng tôi gọi phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4. Hơn nữa, chúng tôi muốn thiết kế bộ sưu tập các lớp này sao cho nó có thể mở rộng—nghĩa là, để chúng tôi có thể tiếp tục thêm các loài động vật mới vào bầy thú của mình mà không phải thay đổi bất kỳ mã nào trong các lớp khác

Hình dưới đây cung cấp một bản tóm tắt về thiết kế chúng tôi sẽ thực hiện. Lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 là một lớp trừu tượng. Đó là lý do tại sao tên của nó được in nghiêng trong hình. Lý do mà lớp này trừu tượng là vì phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 của nó là một phương thức trừu tượng, là một định nghĩa phương thức không chứa phần triển khai. Nghĩa là, định nghĩa phương thức chỉ chứa chữ ký của phương thức, không phải phần thân của nó. Bất kỳ lớp nào chứa một phương thức trừu tượng, chính nó phải được khai báo trừu tượng

Phương thức nào không thể kế thừa trong Java?

Đây là định nghĩa của lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
21

Lưu ý cách chúng ta khai báo phương thức trừu tượng (______1231) và lớp trừu tượng. Bởi vì một hoặc nhiều phương thức của nó không được triển khai, một lớp trừu tượng không thể được khởi tạo. Đó là, bạn không thể nói

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
22

Mặc dù không cần thiết, chúng tôi cung cấp cho lớp Animal một hàm tạo. Nếu chúng ta bỏ qua điều này, Java sẽ cung cấp một hàm tạo mặc định sẽ được gọi khi các lớp con Animal được tạo

Java có các quy tắc sau về việc sử dụng các phương thức và lớp trừu tượng

  • Bất kỳ lớp nào chứa phương thức trừu tượng phải được khai báo là một lớp
  • Một lớp trừu tượng không thể được khởi tạo. Nó phải được phân lớp
  • Một lớp con của một lớp trừu tượng chỉ có thể được khởi tạo nếu nó thực hiện tất cả các phương thức trừu tượng của lớp cha. Một lớp con chỉ thực hiện một số phương thức trừu tượng phải được khai báo trừu tượng
  • Một lớp có thể được khai báo trừu tượng ngay cả khi nó không chứa các phương thức trừu tượng. Ví dụ, nó có thể chứa các biến thể hiện chung cho tất cả các lớp con của nó

Mặc dù một phương thức trừu tượng không được triển khai trong lớp cha, nhưng nó có thể được gọi trong lớp cha. Thật vậy, hãy lưu ý cách phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 gọi phương thức trừu tượng
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231. Lý do điều này hoạt động trong Java là do cơ chế liên kết động. Phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 đa hình sẽ được định nghĩa trong các lớp con Animal khác nhau. Khi phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
237 được gọi, Java sẽ quyết định gọi phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 thực tế nào dựa trên lớp con của Animal có liên quan

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
23

Trong mỗi trường hợp, lớp con mở rộng lớp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 và cung cấp phương thức khởi tạo riêng và triển khai phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 của riêng nó. Lưu ý rằng trong các hàm tạo tương ứng của chúng, chúng ta có thể tham khảo biến thể hiện loại, được kế thừa từ lớp
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0. Bằng cách khai báo loại là một biến được bảo vệ, nó được kế thừa bởi tất cả các lớp con của
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 nhưng bị ẩn khỏi tất cả các lớp khác. Mặt khác, nếu loại đã được khai báo công khai, nó sẽ được kế thừa bởi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 lớp con, nhưng nó cũng có thể được truy cập bởi mọi lớp khác, điều này sẽ vi phạm nguyên tắc ẩn thông tin

Với những định nghĩa này, giờ đây chúng ta có thể chứng minh sức mạnh và tính linh hoạt của tính kế thừa và tính đa hình. Xét đoạn mã sau

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
24

Đầu tiên chúng ta tạo một đối tượng Cow và sau đó gọi phương thức (kế thừa)

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 của nó. Nó trả về, “Tôi là một con bò và tôi đi moo. ” Sau đó, chúng tôi tạo một đối tượng Cat và gọi phương thức (kế thừa)
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 của nó, phương thức này trả về, “Tôi là một con mèo và tôi kêu meo meo. ” Nói cách khác, Java có thể xác định cách triển khai speak() thích hợp trong thời gian chạy trong từng trường hợp. Việc gọi phương thức speak() trừu tượng trong phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
237 là một dạng đa hình thứ hai

Lợi thế của đa hình ở đây là gì? . Chúng ta có thể định nghĩa và sử dụng các lớp con Animal hoàn toàn mới mà không cần định nghĩa lại hoặc biên dịch lại các lớp còn lại trong hệ thống phân cấp. Lưu ý rằng phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 trong lớp Animal không cần biết loại lớp con Animal nào sẽ thực thi phương thức speak() của nó. Phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 sẽ hoạt động chính xác cho bất kỳ lớp con nào của Animal vì mọi lớp con không trừu tượng của Animal phải triển khai phương thức speak()

Để đánh giá tốt hơn về tính linh hoạt và khả năng mở rộng của thiết kế này, có thể hữu ích khi xem xét một thiết kế thay thế không sử dụng tính đa hình. Một giải pháp thay thế như vậy là định nghĩa từng lớp con Động vật bằng phương thức nói của riêng nó. Một con Bò sẽ có phương thức moo(); . Với thiết kế này, chúng ta có thể sử dụng câu lệnh switch để chọn phương thức gọi thích hợp. Ví dụ: xem xét định nghĩa phương thức sau

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
25

Trong ví dụ này, chúng tôi giới thiệu toán tử

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
249, là toán tử boolean được tích hợp sẵn. Nó trả về true nếu đối tượng ở phía bên trái của nó là một thể hiện của lớp ở phía bên phải của nó

Phương pháp

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
250 sẽ tạo ra kết quả ít nhiều giống nhau. Nếu bạn gọi talk(
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
251), nó sẽ trả về “I am a cow and I go moo. ” Tuy nhiên, với thiết kế này, không thể mở rộng phân cấp Animal mà không viết lại và biên dịch lại phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
250

Do đó, một trong những ưu điểm chính của việc sử dụng tính đa hình là tính linh hoạt và khả năng mở rộng tuyệt vời mà nó mang lại. Chúng ta có thể định nghĩa các lớp con Animal mới và định nghĩa các phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 của chúng. Tất cả những thứ này sẽ hoạt động với phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4 trong lớp Động vật mà không cần phải sửa lại phương thức đó

Một ưu điểm khác của việc sử dụng các phương thức trừu tượng là khả năng kiểm soát mà nó mang lại cho người thiết kế hệ thống phân cấp Động vật. Bằng cách biến nó thành một lớp trừu tượng với phương thức trừu tượng

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231, bất kỳ lớp con Động vật không trừu tượng nào cũng phải triển khai phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231. Điều này mang lại khả năng dự đoán cao cho các lớp con trong hệ thống phân cấp, giúp sử dụng chúng dễ dàng hơn trong các ứng dụng

Theo các ví dụ trong phần này, hãy định nghĩa một phân lớp Động vật có tên là Lợn, lớp con này sẽ “oink. ”

Chỉ ra cách bạn sẽ phải sửa đổi phương thức

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
250 được xác định ở trên để kết hợp lớp Pig

Triển khai giao diện Java

Dạng đa hình thứ ba là kết quả thông qua việc triển khai các giao diện Java, giống như các lớp nhưng chỉ chứa các định nghĩa phương thức trừu tượng và các biến hằng (cuối cùng). Một giao diện không thể chứa các biến thể hiện

Người thiết kế giao diện chỉ định phương thức nào sẽ được triển khai bởi các lớp triển khai giao diện. Điều này tương tự như những gì chúng ta đã làm khi triển khai phương thức speak() trừu tượng trong ví dụ động vật. Sự khác biệt giữa việc triển khai một phương thức từ một giao diện và từ một lớp cha trừu tượng là một lớp con mở rộng một lớp cha trừu tượng nhưng nó thực thi một giao diện

Cơ chế giao diện của Java cho chúng ta một cách khác để thiết kế các phương thức đa hình. Để xem điều này hoạt động như thế nào, chúng tôi sẽ cung cấp một thiết kế thay thế cho hệ thống phân cấp động vật của chúng tôi. Thay vì định nghĩa speak() như một phương thức trừu tượng trong lớp cha Animal, chúng ta sẽ định nghĩa nó như một phương thức trừu tượng trong giao diện Speakable

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
26

Lưu ý sự khác biệt giữa định nghĩa Động vật này và định nghĩa trước đó. Phiên bản này không còn chứa phương thức trừu tượng

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231. Do đó, bản thân lớp không phải là lớp trừu tượng. Tuy nhiên, vì phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 không được khai báo trong lớp này nên chúng ta không thể gọi phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 trong phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
4, trừ khi chúng ta ép kiểu đối tượng này thành đối tượng Speakable

Với những định nghĩa này, giờ đây các lớp con Animal sẽ mở rộng lớp Animal và triển khai giao diện Speakable

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
27

Để triển khai giao diện Java, người ta phải cung cấp triển khai phương thức cho từng phương thức trừu tượng trong giao diện. Trong trường hợp này chỉ có một phương thức trừu tượng, phương thức speak()

Lưu ý, một lần nữa, biểu thức từ Animal. lớp toString()

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
28

chuyển đối tượng này thành đối tượng

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
262. Lý do bắt buộc phải sử dụng phương thức này là vì Động vật không nhất thiết phải có phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231. Một phương thức
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 không được định nghĩa trong lớp Animal. Tuy nhiên, lớp con Cat của Animal thực hiện phương thức sleep() như một phần của giao diện Speakable của nó. Do đó, để gọi
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
231 trên một đối tượng từ một trong các lớp con Animal, đối tượng đó phải thực sự là một Speakable và chúng ta phải thực hiện ép kiểu như được hiển thị ở đây

Nhân tiện, điều này minh họa rằng một

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
266, nhờ mở rộng lớp Animal và triển khai giao diện
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
262, vừa là một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
0 vừa là một
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
262. Nói chung, một lớp thực hiện một giao diện, có giao diện đó là một trong các loại của nó. Bản thân việc triển khai giao diện là một hình thức kế thừa. Một lớp Java có thể là một lớp con trực tiếp của chỉ một lớp cha. Nhưng nó có thể thực hiện bất kỳ số lượng giao diện nào

Với các định nghĩa này của các lớp con

public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
270 và
public String toString() {
  return "My name is " + name +  " and I am a Student.";
}
266, đoạn mã sau sẽ tạo ra kết quả giống như trong phần trước

Loại phương thức nào Không thể được kế thừa?

Các phương thức được khai báo là riêng tư trong lớp công khai hoặc được bảo vệ , không thể kế thừa. Constructor không thể kế thừa. Các phương thức cuối cùng không thể bị ghi đè. Các phương thức tĩnh không thể bị ghi đè, nhưng có thể được khai báo lại trong một lớp con.

Khi một lớp Không thể kế thừa trong Java?

Lớp cuối cùng không thể kế thừa. Nếu một lớp không phải là cuối cùng chứa phương thức cuối cùng thì nó có thể được kế thừa nhưng không thể ghi đè trong lớp con.