You are on page 1of 44

CHƯƠNG III

Lớp và các thành phần của lớp


 Cấu trúc lớp và khai báo các thành phần của lớp,
 Định nghĩa hàm thành phần và cơ chế nạp chồng, viết đè
trong Java,
 Các thuộc tính kiểm soát truy nhập các thành phần của lớp,
 Toán tử tạo lập các đối tượng,
 Kế thừa giữa các lớp đối tượng,
 Các giao diện (interface) và sự mở rộng quan hệ kế thừa (đa
kế thừa) trong Java.

Requirement Capture and Analysis 1


3.1 Định nghĩa lớp
[<Phạm vi hoặc kiểm soát truy nhập>] class <Tên lớp>
[extends <Tên lớp cha>] [implements <Tên giao
diện>]
{
<Các thành phần của lớp>
}
Hàm thành phần của lớp:
[<Phạm vi hoặc thuộc tính kiểm soát truy nhập>]<Kiểu trả lại> <Tên hàm >(
[<Danh sách tham biến hình thức>]) [<Mệnh đề throws>{
<Nội dung của hàm>
}

Requirement Capture and Analysis 2


Nạp chồng các hàm thành phần
 Những hàm có nhiều nội dung thực hiện khác nhau, được gọi là hàm nạp
chồng hay hàm tải bội (overloading).
 Cơ chế nạp chồng cho phép một hàm có cùng một tên gọi nhưng danh sách
tham biến khác nhau, do vậy sẽ có các định danh khác nhau.
 Những hàm được nạp chồng với các định danh khác nhau có các phần cài
đặt thực hiện những công việc khác nhau và có kiểu trả lại khác nhau.
JDK API đã xây dựng rất nhiều hàm được nạp chồng. Ví dụ, lớp
java.lang.Math có hàm nạp chồng min() xác định giá trị cực tiểu của 2 số:
public static double min(double a, double b)
public static float min(float a, float b)
public static int min(int a, int b)
public static long min(long a, long b)

Requirement Capture and Analysis 3


Viết đè các hàm thành phần và vấn đề che bóng các biến
 Hàm viết đè phải có cùng định danh (tên gọi và danh sách tham biến) và cùng kiểu
trả lại giá trị.
 Định nghĩa mới của hàm viết đè trong lớp con chỉ có thể xác định tất cả hoặc tập
con các lớp ngoại lệ được kể ra trong mệnh đề cho qua ngoại lệ (throws).
 Định nghĩa của những hàm sẽ viết đè không được khai báo final ở lớp cha.
Ví dụ Viết đè và nạp chồng các hàm thành phần
import java.io.*;
class Den {
protected String loaiHoaDon = “Hoa don nho: ”; // (1)
protected double docHoaDon(int giaDien)
throws Exception { // (2)
double soGio = 10.0,
hoaDonNho = giaDien* soGio;
System.out.println(loaiHoaDon + hoaDonNho);
return hoaDonNho;
}
}

Requirement Capture and Analysis 4


<Tiếp theo>
class DenTuyp extends Den {
public String loaiHoaDon =“Hoa don lon:”;// Bị che bóng (3)
public double docHoaDon (int giaDien)
throws Exception { // Viết đè hàm (4)
double soGio = 100.0,
hoaDonLon = giaDien* soGio;
System.out.println(loaiHoaDon + hoaDonLon);
return hoaDonLon;
}
public double docHoaDon (){
System.out.println(“Khong co hoa don!”);
return 0.0;
}
}
 

Requirement Capture and Analysis 5


<Tiếp theo>
public class KhachHang {
public static void main(String args[])
throws Exception { // (6)
DenTuyp den1 = new DenTuyp(); // (7)
Den den2 = den1; // (8)
Den den3 = new Den(); // (9)
// Gọi các hàm đã viết đè
den1.docHoaDon(1000); // (10)
den2.docHoaDon(1000); // (11)
den3.docHoaDon(1000); // (12)
// Truy nhập tới các biến thành phần đã bị viết đè (bị che bóng)
System.out.println(den1.loaiHoaDon); // (13)
System.out.println(den2.loaiHoaDon); // (14)
System.out.println(den3.loaiHoaDon); // (15)
// Gọi các hàm nạp chồng
den1.docHoaDon();
}
}
Requirement Capture and Analysis 6
<Tiếp theo>
Viết đè và nạp chồng là khác nhau:
 Trước tiên cần phân biệt rõ hai cơ chế viết đè và nạp chồng là khác nhau
trong Java.
+ Viết đè yêu cầu cùng định danh hàm (cùng tên gọi, cùng danh sách tham
số) và cùng kiểu trả lại kết quả đã được định nghĩa tại lớp cha.
+ Nạp chồng yêu cầu khác nhau về định danh, nhưng giống nhau về tên gọi
của hàm, vì thế chúng sẽ khác nhau về số lượng, kiểu, hay thứ tự của các
tham biến.
+ Hàm có thể nạp chồng ở trong cùng lớp hoặc ở các lớp con cháu.
 Từ những lớp con khi muốn gọi tới các hàm ở lớp cha mà bị viết đè thì
phải gọi qua toán tử đại diện cho lớp cha, đó là super(). Đối với hàm nạp
chồng thì không cần như thế. Lời gọi hàm nạp chồng được xác định thông
qua danh sách các đối số hiện thời sánh với đối số hình thức để xác định
nội dung tương ứng.

Requirement Capture and Analysis 7


Phạm vi của các thành phần
•       Phạm vi lớp của các thành phần,
•       Phạm vi khối của các biến cục bộ (local),
•       Phạm vi gói (package)
1/ Phạm vi lớp
Phạm vi lớp xác định những thành phần được truy nhập bên
trong của một lớp (kể cả lớp được kế thừa). Quyền truy nhập của
chúng thường được xác định thông qua các bổ ngữ (modifier):
public, protected, private.

Requirement Capture and Analysis 8


<Tiếp theo>
2/ Phạm vi khối
Trong chương trình, các lệnh khai báo và các lệnh thực hiện
tuần tự có thể gộp lại thành từng khối (block) bằng cách sử
dụng {, }.
Luật phạm vi phát biểu tổng quát là một biến được khai báo ở
trong một khối có phạm vi xác định bên trong khối và không xác
định ở bên ngoài khối đó.
Lưu ý:
+ Trong các khối thì lệnh khai báo là tự do, thứ tự không quan
trọng, muốn khai báo ở chỗ nào cũng được miễn là phải khai
báo trước khi sử dụng.
+ Có thể có nhiều khối lồng nhau nhưng không được cắt nhau
và những biến khai báo ở khối ngoài đều có phạm vi xác định
ở trong mọi khối bao bên trong nó.

Requirement Capture and Analysis 9


<Tiếp theo>
public static void main(String args[]){// Khèi 1
// String args = “”; kh«ng thÓ khai b¸o l¹i c¸c tham biÕn (1)
char chuSo;

for (int k = 0; k < 10; ++k) { // Khèi 2

switch(chuSo) {// Khèi 3


case ‘1’: int i;// (2)
default: int i;// (3) Lçi v× khai b¸o l¹i i trong cïng khèi
}// switch

if (true){// Khèi 4
int i; // (4) OK
int chuSo;// (5) Lçi v× chuSo ®· ®­îc khai b¸o ë khèi ngoµi, khèi 1
} int k; // (6) Lçi v× k ®· ®­îc khai b¸o ë khèi ngoµi, khèi 2
}

int k; // (6) OK
}

Requirement Capture and Analysis 10


<Tiếp theo>
3/ Phạm vi gói
• Trong chương trình, một số lớp có liên quan với nhau (hệ
thống con) thường được tổ chức thành một gói (package).
• Một package là một tuyển tập các lớp, giao diện có quan hệ với
nhau nhằm quản lý không gian tên gọi (namespace) và bảo vệ
các truy cập.
• Lý do phải tổ chức thành các gói:
– Bạn và những người lập trình khác dễ phát hiện được các lớp trong
một gói có liên quan với nhau.
– Bạn và những người lập trình khác biết được cần tìm các lớp, giao diện
liên quan ở đâu, để thực hiện được các chức năng mong muốn.
– Khi tên gọi các lớp bị xung đột thì nên tổ chức chúng ở các gói khác
nhau. Tên của package sẽ tạo ra không gian tên mới.
– Bạn có thể cho phép các đối tượng trong một gói truy cập không hạn
chế tới lớp khác, trừ khi quyền truy cập đó bị giới hạn.

Requirement Capture and Analysis 11


<Tiếp theo>
Quản lý các tệp nguồn và tệp lớp
 Các tệp chương trình nguồn (.java) và các tệp lớp (.class) trong Java được
tổ chức thành hệ thống cây phân cấp.
 Đặt tệp chương trình nguồn (có thể gồm một số lớp) ở trong một gói (đặt
trong một thư mục), ví dụ tệp Rectangle.java
package graphics; graphics
public class Rectangle {
//
} Rectangle.java
class Circle{
//
}
Lưu ý:
 Theo qui ước sử dụng tên miền ngược lại trong Internet, mỗi component
trong tên của of gói tương ứng với một thư mục con.
 Khi dịch chương trình nguồn, compiler sẽ tạo ra các tệp lớp khác nhau cho
từng lớp và từng interface được định nghĩa trong tệp nguồn.

Requirement Capture and Analysis 12


<Tiếp theo>
package com.tamin.graphics;
com
public class Rectangle {
// tamin
}
Graphics
class Circle{
//
}
Rectangle.java Rectangle.class Circle.class

 Ta có thể tổ chức các tệp nguồn ở một thư mục riêng và các
tệp lớp ở một thư mục riêng.

Requirement Capture and Analysis 13


Các thuộc tính kiểm soát truy nhập các thành phần của lớp
Một trong những ưu điểm của phương pháp hướng đối tượng
là có thể tổ chức dữ liệu theo nguyên lý bao gói và che giấu
thông tin. Các thuộc tính và hàm thành phần của lớp có thể
khai báo thêm một số bổ ngữ để kiểm soát quyền truy nhập
đối với những thành phần đó. Khi thiết kế các thành phần của
lớp, chúng ta có thể sử dụng những bổ ngữ sau:
         public
         protected
        mặc định (không sử dụng thêm bổ ngữ khi định nghĩa lớp)
         private
         static
         final
         abstract
         synchronized
         native
         transient
         volatile
Requirement Capture and Analysis 14
(i)  Các thành phần static
 Thành phần static sẽ là chung cho tất cả các đối tượng trong một lớp, còn
những thành phần không static thì thường mỗi biến đều có bản sao các giá
trị riêng của từng đối tượng.
 Hàm tĩnh chỉ truy cập được những thành phần tĩnh.
Ví dụ
// NhaKho.java
class BongDen{
// Các biến thành phần
int soWatts; // Số watts của bóng đèn
boolean onOff; // true - bóng sáng, false - tắt
String viTri; // Nơi đặt bóng đèn
// Thành phần tĩnh
static int demBong; // Đếm số bóng của cả nhà kho (1)
// Định nghĩa toán tử tạo lập
BongDen(){
soWatts = 45; // Đặt mặc định bóng mới với công suất 45 watts
onOff = true; // Khi lắp bóng mới thì bật luôn
viTri = new String(“XYZ”);
++demBong; // Tăng demBong lên một khi lắp mới (2)
}

Requirement Capture and Analysis 15


<Tiếp theo>
// Hàm thành phần tĩnh static
public static ghiBong(){
System.out.println(“So bong den cua nha kho: ” + demBong);
//System.out.println(“Cong suat cua bong den: ” + soWatts);

}
}
public class NhaKho{
public static void main(String args[]){
BongDen.ghiBong(); // Gọi hàm tĩnh theo tên lớp
BongDen den1 = new BongDen(); // Tạo ra bóng mới
System.out.println(“Gia tri cua bo dem: ” + BongDen.demBong);
BongDen den2 = new BongDen(); // Tạo ra bóng mới
den2.ghiBong();// Gọi hàm tĩnh theo tên đối tượng
System.out.println(“Gia tri cua bo dem: ” + den2.demBong);
}
}

Requirement Capture and Analysis 16


(ii) Các thành phần final
• Biến thành phần final được xem như là hằng, giá trị của nó sẽ không thể
thay đổi được ngay sau khi đã được khởi tạo.
• Đối với các biến final kiểu nguyên thủy, khi chúng được khởi tạo giá trị thì
sẽ không thay đổi được,
• Đối với các biến final kiểu tham chiếu (reference), giá trị tham chiếu (là đối
tượng) sẽ không thay đổi được, nhưng các thành phần (trạng thái) của đối
tượng có thể thay đổi được.
Ngoài ra cần lưu ý:
• Các biến final không cần khởi tạo giá trị khi khai báo và còn được gọi là
biến “trắng”. Song phải gán trị trước khi sử dụng. Biến static không cần
khởi tạo giá trị trước khi sử dụng mà nó khởi tạo giá trị mặc định.
• Các hàm khai báo final ở trong lớp là đầy đủ (có đủ nội dung cần thực hiện)
và không thể viết đè ở các lớp con của lớp đó,
• Các biến final static được sử dụng chung trong chương trình như các hằng
(constant).
• Lớp cũng có thể khai báo final. Lớp final là không mở rộng được. Nói cách
khác, hành vi (các hàm thành phần) của lớp final là không thay đổi được ở
các lớp con của nó. Như vậy có thể xem lớp final là loại lớp “đầy đủ” còn lớp
abstract (sẽ đề cập ở phần sau) là không đầy đủ.
• Java API có nhiều lớp final đã được xây dựng, ví dụ java.lang.String là loại
lớp không thể tạo ra những lớp con cháu của nó được.
Requirement Capture and Analysis 17
<Tiếp theo>
Ví dụ: Minh họa khả năng truy nhập của các thành phần final của lớp
class BongDen{
// Biến thành phần final static (1)
final static double KWH_GIA = 500.00;// Giá bán điện 500đ/KWH
int soWatts;
// Hàm thành phần final
final public void datWatt(int watt ){ soWatts= watt; // (2)
}
public void datLaiGia(){
KWH_GIA = 400.00;// Sai vì không cho phép thay đổi lại biến final static (3)
}
}
class DenTuyp extends BongDen{
// Không thể nạp chồng lại các hàm của BongDen
public void datWatt(int w){ // Thử viết đè hàm final do vậy chương trình
soWatts = 2 * w; // dịch sẽ không thực hiện – Sai (4)
}
}
public class NhaKho{
public static void main(String args[]){
final BongDen den = new BongDen(); (5)
den.soWatts =100;// Được phép thay đổi trang thái đối tượng (6) den= new
BongDen();//Sai vì không cho phép thay đổi đối tượng (7)
}
}

Requirement Capture and Analysis 18


(iii)   Các thành phần abstract
 Hàm thành phần khai báo trừu tượng (abstract) có dạng:
abstract <Kiểu trả lại> <Tên hàm>([<Danh sách tham biến>])[<Mệnh đề
throws>];
àm abstract là hàm prototype, chỉ khai báo phần định danh
hàm mà không định nghĩa nội dung thực hiện, do vậy nó là
hàm không đầy đủ.
Hàm abstract chỉ sử dụng cho các lớp abstract và nó phải
được cài đặt nội dung thực hiện ở trong các lớp con cháu của
lớp chứa hàm đó.
Lưu ý:
+ Hàm final không thể khai báo abstract và ngược lại.
+ Các hàm trong interface đều là các hàm abstract.
 Lớp abstract phải chứa ít nhất một hàm abstract. Những hàm
này sau đó sẽ được cài đặt nội dung thực hiện trong các lớp
con cháu của lớp chứa chúng.

Requirement Capture and Analysis 19


<Tiếp theo>
Ví dụ: Minh họa khả năng truy nhập của lớp abstract
//NhaKho.java
abstract class BongDen{
// biến thành phần (1)
int soWatts;
boolean batTat;
String viTri;
// Hàm thành phần
public void batSang(){batTat = true; }
public void tat(){batTat = false; }
public boolean sangTat(){return batTat; }
// Hàm abstract
abstract public double xacDinhGia(); // Không có nội dung
}
 
class DenTuyp extends BongDen{
// Biến thành phần
int doDai;
int mau;
// Cài đặt hàm abstract xacDinhGia()
public double xacDinhGia(){
return 500.00;
}
}

Requirement Capture and Analysis 20


<Tiếp theo>
class NhaKho{
public static void main(String args[]){
DenTuyp den1 = new DenTuyp();
(5)
System.out.println(“Gia dien: ” + den1.xacDinhGia());
BongDen den2; // Được phép khai báo kiểu abstract (6)
BongDen den3= new BongDen();//Sai vì lớp abstract
không có thể hiện }
}
 

Requirement Capture and Analysis 21


(iv)  Các hàm thành phần đồng bộ synchronized

Java hỗ trợ chương trình thực hiện đa luồng (multi threads). Tại mỗi thời
điểm, chỉ một luồng (tiến trình) được khai báo đồng bộ synchronized được
thực hiện trên đối tượng chỉ định.
Ví dụ: Minh họa họat động của các hàm synchronized
class Stack{
private Object[] stackArray;
private int topfOfStack;
synchronized public void push(Object elem){ // (1)
stackArray[++topOfStack] = elem;
}
synchronized public Object pop(){ // (2)
Object obj = stackArray[topOfStack];
stackArray[topOfStack] = null;
return obj;
}
// Những hàm khác
public Object peek(){ return stackArray[topOfStack];
}
}
Hai hàm push() và pop() trong lớp Stack là đồng bộ synchronized. Do vậy khi
có nhiều luồng muốn đẩy các phần tử vào Stack (hàm push()) hay muốn lấy ra
các phần tử (hàm pop()) thì chỉ một luồng được phép thực hiện, còn những
luồng khác sẽ phải chờ.
Requirement Capture and Analysis 22
Kiểm soát truy nhập các thành phần

Từ khoá Lớp Hàm thành phần Biến thành phần Biến cục bộ

Abstract   - -
static -   -
public    -
protected    -
private -   -
final    
synchronized -  - -
native -  - -
transient - -  -
volatile - -  -

Requirement Capture and Analysis 23


Quan hệ kế thừa giữa các lớp
 Java chỉ hỗ trợ kế thừa đơn (tuyến tính), nghĩa là một lớp chỉ kế
thừa được từ một lớp cha.
 Mọi lớp của Java đều là lớp con cháu mặc định của Object.

 
Java.lang.Object

BongDen NhaKho

DenTuyp

Requirement Capture and Analysis 24


<Tiếp theo>
Cú pháp qui định quan hệ kế thừa trong Java là sự mở rộng
của lớp cha, có dạng:
<Tên lớp con> extends <Tên lớp cha> {
// Các thuộc tính dữ liệu bổ sung
// Các hàm thành phần bổ sung hay viết đè
}
 
Lưu ý:
 Mọi đối tượng của lớp con cũng sẽ là đối tượng thuộc lớp cha.
Do vậy việc gán một đối tượng của lớp con sang cho biến
tham chiếu đối tượng của lớp cha là sự mở rộng kiểu và do đó
không cần ép kiểu.
 Ngược lại gán một đối tượng của lớp cha cho biến tham chiếu
đối tượng thuộc lớp con sẽ phải thực hiện ép kiểu. Lưu ý khi
đó sẽ có thể bị tổn thất thông tin.
Requirement Capture and Analysis 25
<Tiếp theo>
Ví dụ
class SuperClass { /* ... */ }
class SubClass { /* ... */ }
class UserClass {
public static main(String args[]){
SuperClass super1 = new SuperClass();// Tạo ra đối tượng lớp cha
SubClass sub1 = super1; // Mở rộng kiểu

SuperClass super2 = (SubClass) sub1; // Thu hẹp kiểu nên phải ép kiểu
}

Requirement Capture and Analysis 26


Toán tử tạo lập this(), super()
 Các toán tử tạo lập không thể viết đè ở các lớp dẫn xuất (lớp con). Chúng có thể
được nạp chồng nhưng phải trong cùng lớp.
 Trong Java có 2 toán tử tạo lập đặc biệt có tên là this() và super() được sử dụng
để móc xích giữa các lớp có quan hệ kế thừa với nhau.
  Toán tử tạo lập this() được sử dụng để tạo ra đối tượng của lớp hiện thời.
Ví dụ: Nạp chồng toán tử tạo lập
class BongDen{
private int soWatts;
private boolean batTat;
private String viTri;
BongDen(){
soWatts = 40;
batTat = true;
viTri = new String(“XX”);
System.out.println(“Toán tử số 1”);
}
 

Requirement Capture and Analysis 27


Toán tử tạo lập this(), super()
// Định nghĩa toán tử tạo lập không mặc định, số 2 (3)
BongDen(int w, boolean s){
soWatts = w;
batTat = s;
viTri = new String(“XX”);
System.out.println(“Toán tử số 2”);
}
// Định nghĩa toán tử tạo lập không mặc định, số 3 nạp chồng (4)
BongDen(int soWatts, boolean batTat, String viTri){
this.soWatts = soWatts;
this.batTat = batTat;
this.viTri = new String(viTri);
System.out.println(“Toán tử số 3”);
}
// ...
}
 
Requirement Capture and Analysis 28
Toán tử tạo lập this(), super()
Ví dụ: Sử dụng toán tử this()
class BongDen{
private int soWatts;
private boolean batTat;
private String viTri;
BongDen(){
this(40, true);
System.out.println(“Toán tử số 1”);
}
  BongDen(int w, boolean s){
this(w, s, “XX”);
System.out.println(“Toán tử số 2”);
}
BongDen(int soWatts, boolean batTat, String viTri){
this.soWatts = soWatts;
this.batTat = batTat;
this.viTri = new String(viTri);
System.out.println(“Toán tử số 3”);
} }
Requirement Capture and Analysis 29
Toán tử tạo lập this(), super()
 Toán tử super() được sử dụng trong các toán tử tạo lập của lớp con (subclass) để
gọi tới các toán tử tạo lập của lớp cha (superclass) trực tiếp.
 Ví dụ: Sử dụng toán tử super()
 class BongDen{
private int soWatts;
private boolean batTat;
private String viTri;
BongDen(){
this(40, true);
System.out.println(“Toán tử số 1”);}
BongDen(int w, boolean s){
this(w, s, “XX”);
System.out.println(“Toán tử số 2”);}
BongDen(int soWatts, boolean batTat, String viTri){
this.soWatts = soWatts;
this.batTat = batTat;
this.viTri = new String(viTri);
System.out.println(“Toán tử số 3”);
} }
Requirement Capture and Analysis 30
Toán tử tạo lập this(), super()
class DenTuyp extends BongDen {
private int doDai;
private int mau;
DenTuyp(int leng, int colo){ // (5) this(leng, colo, 100, true, “Chua biet”);
}
DenTuyp(int leng, int colo, int soWatt,
boolean bt, String noi){ // (6) super(soWatt, bt, noi);
this.doDai = leng;
this.mau = colo;
}
}
public class NhaKho{
public static void main(String args[]){
System.out.println(“Tao ra bong đen tuyp”);
DenTuyp d = new DenTuyp(20, 5);
}
}

Requirement Capture and Analysis 31


Quan hệ giữa các lớp
Hai lớp có thể có các quan hệ sau:
 Quan hệ liên kết (Association),
 Quan hệ kết tập (Aggregation),
 Quan hệ kế thừa (Inheritance),
 Quan hệ phụ thuộc (Dependency).

Requirement Capture and Analysis 32


<Tiếp theo>
Ví dụ: Xét hệ thống gồm các lớp như sau:
next

List Node

Queue

duoi

Stack Object

Requirement Capture and Analysis 33


<Tiếp theo>
Chuyển đổi biểu đồ lớp trên sang Java chúng ta nhận được chương trình:
class Node { // (1)
private Object duLieu; // Dulieu
private Node next; // next node
public Node(Object obj, Node link){
duLieu = obj;
next = link;
}
// Accessor
public void setDL(Object obj){duLieu = obj;}
public Object getDL(){ return duLieu;}
public void setNext(Node node){next = node;}
public Node getNext() { return next;}
}
Requirement Capture and Analysis 34
<Tiếp theo>
class List{ // (2)
protected Node dau = null;
protected Node duoi = null;
public void insertInFront(Object obj){
if (isEmpty()) dau = duoi = new Node(obj,null);
else dau = new Node(obj,dau);
}
public void insertAtBack(Object obj){
if (isEmpty()) dau = duoi = new Node(obj,null);
else { duoi.setNext(new Node(obj,dau));
duoi = duoi.getNext();
}
}

Requirement Capture and Analysis 35


<Tiếp theo>
public Object deleteFront(){
if(isEmpty()) return null;
Node removed = dau;
if(dau == duoi) dau = duoi = null;
else dau = dau.getNext();
return removed.getDL();
}
public boolean isEmpty(){ return dau == null;}
}
class Queue{
private List qlist; // (3)
public Queue(){
qlist = new List();
}

Requirement Capture and Analysis 36


<Tiếp theo>
public void enqueue(Object item){
qlist.insertAtBack(item);
}
public Object dequeue(){
if (empty()) return null;
else return qlist.deleteFront();
}
public Object peek(){
Object obj = dequeue();
if (obj != null) qlist.insertInFront(obj);
return obj;
}
public boolean empty(){ return qlist.isEmpty();}
}

Requirement Capture and Analysis 37


<Tiếp theo>
class Stack extends List{ // (4)
public void push(Object obj){insertInFront(obj);}
public Object pop(){
if(empty()) return null;
else return deleteFront();
}
public Object peek(){
return (isEmpty() ? null : dau.getDL());
}
public boolean empty() {return isEmpty();
}
}

Requirement Capture and Analysis 38


Giao diện và sự mở rộng quan hệ kế thừa trong Java
Java không hỗ trợ trực tiếp kế thừa bội, song để tránh những trở ngại trên
Java cung cấp khái niệm interface và cho phép kế thừa bội đối với các
interface.
Định nghĩa interface
interface <Tên interface > {
// Các thuộc tính (hằng - ngầm định)
// Các hàm mẫu prototype
}
 Giao diện interface là loại kiểu lớp đặc biệt, trong đó tất cả các hàm thành
phần đều là trừu tượng, do vậy không thể khởi tạo được giá trị, nghĩa là
không thể tạo ra đối tượng của interface.
 Những hàm trong interface sẽ được cài đặt ở những lớp xây dựng mới theo
dạng:
class <Tên lớp> implements <Tên interface> {
// Bổ sung các thành phần;
// Cài đặt các hàm mẫu đã cho trong <Tên interface>;
}
Requirement Capture and Analysis 39
<Tiếp theo>
Sự mở rộng (kế thừa) của các interface
 Giống như cấu trúc lớp, interface cũng có thể được mở rộng từ nhiều
interface khác bằng mệnh đề extends.
 Khác với sự mở rộng tuyến tính của các lớp, các interface được phép mở
rộng không tuyến tính, nghĩa là có thể mở rộng nhiều hơn một interface.
class MyClass implements Interface1, Interface2 {
// Cài đặt một số hàm mẫu của Interface1, Interface2;
}
interface Interface1{
// Khai báo các hằng và hàm mẫu;
}
interface Interface2{
// Khai báo các hằng và hàm mẫu;
}

Requirement Capture and Analysis 40


<Tiếp theo>
Ví dụ: Cách xây dựng và sử dụng các interface
interface IStack{ // (1)
void push(Object item);
Object pop();
}
class StackImpl implements IStack {// (2)
protected Object[] stackArray; // Bổ sung mảng đối tượng
protected int tos; // Bổ sung biến tos
public StackImpl(int capacity) {
stackArray = new Object[capacity];
tos = -1;
}
public void push(Object item) { // Cài đặt hàm push()
(3)
stackArray[++tos] = item;
}

Requirement Capture and Analysis 41


<Tiếp theo>
public Object pop() { // Cài đặt hàm pop() (4)
Object obj = stackArray[tos];
stackArray[tos] = null;
tos--;
return obj;
}
public Object peek() {// Bổ sung hàm peek() (5)
return stackArray[tos];
}
}
// Xây dựng thêm interface IsafeStack kế thừa Istack để kiểm tra các trạng thái
của
// Stack đảm bảo an toàn khi thao tác.
interface ISafeStack extends IStack { // (6)
boolean isEmpty();
boolean isFull();
}
Requirement Capture and Analysis 42
<Tiếp theo>
class SafeStackImpl extends StackImpl implements ISafeStack {
public SafeStackImpl(int capacity) {super(capacity);}
public boolean isEmpty() {return tos < 0;}
public boolean isFull() {return tos == stackArray.length-1;}
}
public class StackUser {
public static void main(String args[]){
SafeStackImpl safe1 = new SafeStackImpl(80); // (8)
StackImpl s1 = safe1; // (9)
ISafeStack iSafe1 = safe1; // (10)
IStack iS1 = safe1;
Object obj = safe1;
safe1.push(“Khoa CNTT”);s1.push(“ĐHQG”);
System.out.println(iSafe1.pop()); // (11)
System.out.println(iS1.pop()); // (12)
System.out.println(obj.getClass()); // (13)
}
}
Requirement Capture and Analysis 43
<Tiếp theo>
Lưu ý:
 Các hàm mẫu của interface được mặc định là abstract và
không được khai báo static;
 Một lớp có thể chọn một số hàm mẫu để cài đặt, nghĩa là có
thể chỉ cài đặt một phần của giao diện interface;
 Một giao diện có thể kế thừa từ nhiều hơn một giao diện;
 Một giao diện có thể kế thừa từ nhiều hơn một giao diện và
lớp;

Requirement Capture and Analysis 44

You might also like