Giao Diện (Interface)
Giao diện (Interface) là một khái niệm quan trọng trong lập trình hướng đối tượng Java. Nó đóng vai trò như một "bản mẫu" hoặc "bản hợp đồng" hành vi của một lớp. Giao diện giúp tăng tính trừu tượng (abstraction) và tính kế thừa (inheritance) trong code.
Ⅰ. Những điểm cần phải lưu ý khi sử dụng interface
↳ Được khai báo bằng từ khóa interface.
↳ Không thể khởi tạo đối tượng vì interface không thể chứa hàm tạo (constructor)
↳ Tất cả các phương thức trong interface đều là public và abstract: Từ Java 8, có thể có phương thức mặc định(defautl) và tĩnh(static).
↳ Biến trong giao diện phải là public, static, và final: biến trong giao diện không thể thay đổi giá trị và phải được khởi tạo khi khai báo. Các hằng số này được chia sẻ bởi tất cả các lớp triển khai giao diện.
↳ Một interface không thể kế thừa(extends) từ lớp, nó được triển khai (implement) bởi một lớp.
↳ Một interface có thể kế thừa từ nhiều interface khác.

Ⅱ. Cú pháp của interface trong Java
Cú Pháp
interface TênGiaoDiện {
// Các phương thức trừu tượng
returnType methodName(parameterList);
// Các hằng số (public static final)
dataType CONSTANT_NAME = value;
}
Giải thích từng phần:
↳ interface: Từ khóa dùng để khai báo một giao diện.
↳ TênGiaoDiện: Đặt tên cho giao diện theo quy tắc đặt tên biến trong Java.
Các phương thức trừu tượng:
↳ methodName: Tên của phương thức.
↳ parameterList: Danh sách các tham số truyền vào phương thức.
↳ Trừu tượng: Các phương thức trong giao diện không có thân hàm, chỉ có khai báo.
Các hằng số:
↳ dataType: Kiểu dữ liệu của hằng số.
↳ CONSTANT_NAME: Tên hằng số, thường viết hoa.
↳ value: Giá trị của hằng số.
Ⅲ. Phương thức của interface trong Java
Quy tắc
Tất cả phương thức trong giao diện đều là public: Phương thức trong giao diện phải được khai báo là public. Nếu không khai báo rõ ràng là public, thì chúng sẽ tự động được coi là public (Không Có Phương Thức private và protected).
Các phương thức trong giao diện
Phương thức mặc định (Default Methods): Từ Java 8 trở đi, giao diện có thể bao gồm các phương thức mặc định. Phương thức mặc định có phần thân và không bắt buộc phải được ghi đè trong các lớp triển khai. Để khai báo một phương thức mặc định, bạn sử dụng từ khóa default.
Ví dụ
interface Animal {
// Phương thức mặc định
default void eat() {
System.out.println("Con vật này ăn thức ăn.");
}
}
Phương thức tĩnh (Static Methods): Giao diện có thể chứa phương thức tĩnh, và các phương thức này có thể được gọi mà không cần tạo đối tượng của giao diện. Phương thức tĩnh không thể bị ghi đè bởi các lớp triển khai.
Ví dụ
interface Animal {
// Phương thức trừu tượng tĩnh
static void sleep() {
System.out.println("Động vật ngủ.");
}
Phương thức trừu tượng (Abstract Methods): Các phương thức trừu tượng là các phương thức không có phần thân và cần phải được triển khai bởi các lớp con. Chúng được khai báo mà không có từ khóa default và không có phần thân.
Bắt buộc phải triển khai: Bất kỳ lớp nào muốn triển khai (implement) giao diện đó phải định nghĩa lại (override) tất cả các phương thức trừu tượng của giao diện. Nếu không, lớp đó sẽ không được biên dịch.
Ví dụ
interface Animal {
// Phương thức trừu tượng
void makeSound();
}
So sánh giữa phương thức bình thường, phương thức trong lớp trừu tượng và phương thức trong interface:
Tính năng | Phương thức bình thường | Phương thức trong lớp trừu tượng | Phương thức trong interface |
---|---|---|---|
Có phần thân | Có | Không | Không |
Trừu tượng | Không | Có | Có |
Truy cập | Trực tiếp từ đối tượng | Chỉ thông qua đối tượng của lớp con | Chỉ thông qua đối tượng của lớp triển khai |
Mặc định | Không có | Có thể là public, protected hoặc private | Luôn là public |
Kế thừa | Không áp dụng | Lớp con phải override | Lớp triển khai phải override |
Mục đích | Thực hiện hành động cụ thể | Định nghĩa hành vi chung cho lớp con | Định nghĩa hợp đồng mà lớp triển khai phải tuân theo |
So sánh giữa biến bình thường, biến trong lớp trừu tượng và biến trong interface:
Tính năng | Biến trong lớp bình thường | Biến trong lớp trừu tượng | Biến trong interface |
---|---|---|---|
Loại biến | Thành viên, lớp | Thành viên, lớp | Chỉ hằng số |
Mức truy cập | public, private, protected | public, private, protected | public, static, final |
Khởi tạo | Có thể khởi tạo hoặc không | Có thể khởi tạo hoặc không | Phải khởi tạo ngay khi khai báo |
Kế thừa | Lớp con kế thừa | Lớp con kế thừa | Lớp triển khai có quyền truy cập |
So sánh về công cụ sửa đổi truy cập (access modifiers) giữa các lớp (class), lớp trừu tượng (abstract class), và giao diện (interface)
Tính năng | Class | Abstract class | Interface |
---|---|---|---|
public | Có | Có | Có (mặc định) |
protected | Không | Có | Không |
default | Có | Có | Có |
private | Không | Không | Không |
extends | Có | Có | Không |
abstract | Không | Có | Có (cho phương thức) |
implements | Không | Không | Có |
Trong quá trình biên dịch code Java, trình biên dịch tự động thêm các từ khóa mặc định cho các thành phần của giao diện.
Trình biên dịch tự động thêm public, static, và final cho các trường dữ liệu của giao diện.
Trình biên dịch tự động thêm public và abstract cho các phương thức của giao diện

Dưới đây là một ví dụ minh họa về các loại phương thức trong giao diện và cách chúng có thể được sử dụng:
Ví dụ: Example.java
interface Animal {
void makeSound(); // Phương thức trừu tượng
default void eat() {
System.out.println("Động vật đang ăn thưc ăn.");
}
static void sleep() {
System.out.println("Động vật đang ngủ.");
}
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Chú chó đang sủa.");
}
// Không cần phải ghi đè phương thức eat() nếu không cần thay đổi
}
public class Example {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); //gọi phuong thức abstract từ giao diện
dog.eat(); // Gọi phương thức default từ giao diện
Animal.sleep(); // (Gọi phương thức tĩnh từ giao diện)
}
}
Kết quả của chương trình là:
Động vật đang ăn thưc ăn.
Động vật đang ngủ.
Tóm lại, phương thức trong giao diện trong Java có thể là phương thức trừu tượng, phương thức mặc định, hoặc phương thức tĩnh, và mỗi loại phương thức có các quy tắc và mục đích sử dụng riêng.
Ⅳ. Các kiểu kế thừa theo interface
Kế thừa trong Java qua các giao diện (interfaces) có thể được phân loại thành nhiều kiểu, bao gồm cả đa kế thừa và kế thừa hỗn hợp. Dưới đây là giải thích chi tiết về từng kiểu:
1. Đa kế thừa (Multiple Inheritance) qua interface
Đa kế thừa là một tính năng cho phép một lớp kế thừa từ nhiều lớp cha. Trong Java, lớp không thể kế thừa trực tiếp từ nhiều lớp (Java không hỗ trợ đa kế thừa lớp), nhưng có thể thực hiện đa kế thừa qua giao diện.
Cách thực hiện đa kế thừa qua Interface
Một lớp có thể triển khai nhiều giao diện. Khi đó, lớp đó phải cung cấp triển khai cho tất cả các phương thức trừu tượng từ các interface. Một lớp có thể thực hiện nhiều giao diện bằng cách liệt kê các giao diện, cách nhau bởi dấu phẩy.

Trong ảnh mô tả trên:
↳ Interface1 và Interface2 là hai giao diện, mỗi giao diện định nghĩa một tập hợp các phương thức trừu tượng mà các lớp thực hiện phải cài đặt.
↳ MyClass là một lớp, nó implements cả hai giao diện Interface1 và Interface2, nghĩa là lớp MyClass phải cung cấp định nghĩa cho tất cả các phương thức trong cả hai interface.
↳ Từ khóa implements Trong lập trình hướng đối tượng (OOP), từ khóa implements trong Java được sử dụng bởi một lớp để chỉ ra rằng lớp đó sẽ triển khai (implement) một hoặc nhiều giao diện (interfaces). Khi một lớp triển khai (implement) một giao diện, nó bắt buộc phải cung cấp phần triển khai (implement) cho tất cả các phương thức trừu tượng được định nghĩa trong giao diện đó.
Ví dụ: Example.java
// public interface - Có thể được truy cập từ bất kỳ gói nào
public interface A {
void methodA();
}
// Mặc định interface - Chỉ có thể được truy cập từ cùng một gói
interface B {
void methodB();
}
// Lớp trong cùng một gói
class C implements A, B {
@Override
public void methodA() {
System.out.println("Phương thức của lớp A");
}
@Override
public void methodB() {
System.out.println("Phương thức của lớp B");
}
}
public class Example {
public static void main(String[] args) {
C classc = new C();
classc.methodA(); // Gọi phương thức của interface A
classc.methodB(); // Gọi phương thức của interface B
}
}
Kết quả của chương trình là:
Phương thức của lớp B
Trong ví dụ trên, lớp C là một lớp triển khai (implements) interface A và B. Lớp C phải cung cấp các triển khai cụ thể cho tất cả các phương thức trừu tượng methodA và methodB trong hai interface này.
2. Kế thừa hỗn hợp (Hybrid Inheritance) qua interface
Kế thừa hỗn hợp là sự kết hợp giữa kế thừa từ lớp và implements interface. Một lớp có thể kế thừa từ một lớp cha và đồng thời implements nhiều interface. Điều này có nghĩa là kế thừa hỗn hợp có thể liên quan đến việc kế thừa(extends) một lớp cha duy nhất và triển khai (implements) nhiều giao diện, cũng như việc mở rộng các lớp ở nhiều cấp hoặc từ các lớp trừu tượng.

Ví dụ: Example.java
class A {
void methodA() {
}
}
interface B {
void methodB();
}
interface C {
void methodC();
}
// Lớp D kê thừa từ lớp A và triển khai hai giao diện B, C
class D extends A implements B,C {
// Ghì đè phương thức của lớp A
@Override
public void methodA() {
System.out.println("Đây là phương thức của lớp A");
}
// Ghì đè phương thức của interface B
@Override
public void methodB() {
System.out.println("Đây là phương thức của lớp B");
}
// Ghì đè phương thức của interface C
@Override
public void methodC() {
System.out.println("Đây là phương thức của lớp C");
}
}
public class Example {
public static void main(String[] args) {
// Tạo đối tượng của lớp D
D classd = new D();
classd.methodA(); // Gọi phương thức của lớp A
classd.methodB(); // Gọi phương thưc của interface B
classd.methodC(); // Gọi phương thưc của interface C
}
}
Kết quả của chương trình là:
Đây là phương thức của lớp B
Đây là phương thức của lớp C
Trong ví dụ trên, lớp D mở rộng lớp A (kế thừa đơn lẻ) và thực hiện cả hai giao diện B và C (đa kế thừa qua giao diện). Điều này thể hiện kế thừa hỗn hợp, nơi một lớp có thể thừa hưởng các phương thức và thuộc tính từ một lớp cha và đồng thời thực hiện nhiều giao diện.
Mối quan hệ giữa class và interface trong Java
Kế thừa (extends) được sử dụng để tạo ra các lớp con có thể tận dụng lại các thuộc tính và phương thức từ lớp cha.
Implements được sử dụng để thiết lập một hợp đồng giữa lớp và giao diện. Lớp hứa sẽ cung cấp các phương thức cụ thể cho các phương thức trừu tượng được định nghĩa trong giao diện.
Giao diện cũng có thể kế thừa từ các giao diện khác để mở rộng chức năng.

So sánh chi tiết hơn về lớp (Class) và giao diện (interface) để bạn có cái nhìn tổng quan rõ ràng hơn nhé.
Đặc điểm | Class (Lớp) | Interface (Giao diện) |
---|---|---|
Khái niệm | Bản thiết kế cho các đối tượng, định nghĩa các thuộc tính và hành vi. | Một hợp đồng, một bản giao kèo, định nghĩa các phương thức mà các lớp phải thực hiện. |
Thành phần | Có thể chứa cả thuộc tính (fields) và phương thức (methods), bao gồm cả phương thức trừu tượng. | Chỉ chứa các phương thức trừu tượng và các hằng số (constants). |
Kế thừa | Một lớp chỉ có thể kế thừa từ một lớp cha duy nhất. | Một lớp có thể implements nhiều interface. |
Khả năng khởi tạo | Có thể tạo đối tượng trực tiếp từ lớp. | Không thể tạo đối tượng trực tiếp từ interface, chỉ có thể được implement bởi các lớp. |
Phương thức | Có thể có cả phương thức cụ thể (có thân) và phương thức trừu tượng. | Chỉ có phương thức trừu tượng (không có thân). |
Mục đích | Định nghĩa các đối tượng cụ thể, mô tả trạng thái và hành vi. | Định nghĩa một hợp đồng, các lớp thực hiện interface phải tuân thủ hợp đồng đó. |
Tính đa hình | Hỗ trợ tính đa hình thông qua kế thừa và override. | Hỗ trợ tính đa hình thông qua việc nhiều lớp khác nhau implements cùng một interface. |
Ⅴ. Interface lồng nhau (Nested Interface)
Trong Java, một interface có thể được khai báo bên trong một interface khác hoặc bên trong một lớp. Đây được gọi là nested interface (giao diện lồng nhau). Giao diện lồng nhau được sử dụng để nhóm các giao diện có liên quan lại với nhau hoặc để làm cho giao diện chỉ có thể truy cập được trong bối cảnh của lớp hoặc giao diện bao ngoài.
Cú pháp của Interface lồng nhau trong Java
Cú pháp
interface OuterInterface {
// Các phương thức của OuterInterface
interface InnerInterface {
// Các phương thức của InnerInterface
}
}
Dưới đây là giải thích chi tiết về cú pháp khai báo này:
↳ OuterInterface: Đây là interface bên ngoài, nơi chúng ta khai báo interface lồng nhau.
↳ InnerInterface: Đây là interface lồng nhau, được khai báo bên trong OuterInterface.
Truy cập của Interface lồng nhau trong Java
Để truy cập một nested interface, ta sử dụng cú pháp: OuterInterface.InnerInterface.
Ví dụ: Example.java
interface OuterInterface {
void outerMethod();
interface NestedInterface {
void nestedMethod();
}
}
class Test implements OuterInterface,OuterInterface.NestedInterface {
@Override
public void outerMethod() {
System.out.println("Phương thức của interface bên ngoài");
}
@Override
public void nestedMethod() {
System.out.println("Phương thức của interface bên trong");
}
}
public class Example {
public static void main(String[] args) {
Test test = new Test();
test.nestedMethod();// Gọi phương thức nestedMethod
test.outerMethod();// Gọi phương thức outerMethod
}
}
Kết quả của chương trình là:
Phương thức của interface bên ngoài
Điều này chứng tỏ rằng phương thức nestedMethod đã được triển khai thành công trong lớp Test và được gọi trong phương thức main của lớp Example.
Dưới đây là một ví dụ khi một interface kế thừa từ một interface khác
Kế thừa Interface: Một interface có thể kế thừa từ một hoặc nhiều interface khác, nghĩa là nó sẽ thừa hưởng tất cả các phương thức trừu tượng của các interface cha.
Ví dụ: Example.java
// Giao diện cha
interface Animal {
void eat();
void sleep();
}
// Giao diện con kế thừa từ giao diện cha
interface Dog extends Animal {
void bark();
}
// Lớp triển khai giao diện con
class Labrador implements Dog {
@Override
public void eat() {
System.out.println("Labrador Đang ăn");
}
@Override
public void sleep() {
System.out.println("Labrador đang ngủ");
}
@Override
public void bark() {
System.out.println("Labrador đang sủa");
}
}
public class Example {
public static void main(String[] args) {
Labrador labrador = new Labrador();
labrador.eat();
labrador.sleep();
labrador.bark();
}
}
Kết quả của chương trình là:
Labrador đang ngủ
Labrador đang sủa
Điều này cho thấy rằng lớp Labrador đã triển khai tất cả các phương thức từ các interface Dog và Animal một cách thành công.
So sánh chi tiết hơn về abstract class và interface để bạn có cái nhìn tổng quan rõ ràng hơn nhé.
Đặc điểm | Abstract Class | Interface |
---|---|---|
Khái niệm | Một bản thiết kế chưa hoàn thiện cho các lớp, cung cấp một cấu trúc chung và các hành vi cơ bản. | Một hợp đồng, định nghĩa một tập hợp các phương thức mà các lớp phải thực hiện. |
Thành phần | Có thể chứa cả phương thức trừu tượng và phương thức cụ thể. Có thể có thuộc tính, cả static và non-static. | Chỉ chứa các phương thức trừu tượng (trước Java 8), từ Java 8 có thêm default method và static method. Chỉ có hằng số (final static). |
Kế thừa | Một lớp chỉ kế thừa một lớp cha duy nhất, có thể là abstract class hoặc class cụ thể. | Một lớp có thể implements nhiều interface. |
Khả năng khởi tạo | Không thể khởi tạo trực tiếp đối tượng của abstract class. | Không thể khởi tạo trực tiếp đối tượng của interface. |
Mục đích | Cung cấp một cấu trúc chung, định nghĩa hành vi chung, và có thể cung cấp một phần triển khai. | Định nghĩa một hợp đồng, đạt được tính đa hình cao, tách biệt giao diện với việc triển khai. |
Ví dụ | Động vật (Animal) là một abstract class, các loài động vật cụ thể (chó, mèo, ...) kế thừa từ Animal và cung cấp các hành vi cụ thể. | Flyable (có thể bay) là một interface, các loài chim, máy bay implements interface này. |
Khi sử dụng | Sử dụng khi muốn tạo một lớp cơ sở cung cấp một số hành vi chung và một số hành vi cụ thể. | Sử dụng khi muốn định nghĩa một hợp đồng mà nhiều lớp khác nhau có thể thực hiện, đạt được tính đa hình cao. |
Ⅵ. Marker (hay Tagging) Interface trong Java là gì?
Marker Interface (hay còn gọi là Tagging Interface) là một loại interface đặc biệt trong Java. Đây là những interface không có bất kỳ phương thức nào bên trong, chỉ đơn giản được sử dụng để "đánh dấu" một lớp với một tính chất cụ thể nào đó. Những interface này không cung cấp bất kỳ hành vi nào nhưng lại mang ý nghĩa đặc biệt đối với các lớp, đối tượng hoặc hệ thống.
Mục đích của Marker Interface: Marker Interface được sử dụng để cung cấp metadata cho các lớp. Nó cho phép lập trình viên xác định rằng một lớp nào đó có một tính chất hoặc hành vi đặc biệt mà không cần thêm bất kỳ phương thức nào.
Sử dụng giao diện nhãn (Marker Interface)
↳ Giao diện nhãn tích hợp sẵn: Java cung cấp một số giao diện nhãn tích hợp sẵn, chẳng hạn như Cloneable, Serializable, Remote.
↳ Tạo giao diện nhãn tùy chỉnh: Bạn có thể tạo các giao diện nhãn của riêng mình để đánh dấu các lớp có hành vi đặc biệt trong ứng dụng của bạn.
Giao diện nhãn (Marker Interface) tích hợp sẵn trong Java
Trong Java, giao diện nhãn tích hợp sẵn là những giao diện đã có sẵn trong JDK (Java Development Kit) và bạn có thể sử dụng ngay lập tức. Một số giao diện nhãn tích hợp sẵn phổ biến bao gồm:
1. Giao diện Cloneable (Cloneable Interface)
Cho biết lớp implements nó có khả năng tạo ra bản sao (clone) của đối tượng.
JVM cung cấp phương thức clone() cho các lớp implements Cloneable, cho phép bạn tạo ra một đối tượng mới là bản sao chính xác của đối tượng ban đầu nhưng có thể được sửa đổi độc lập.
Sử dụng:
↳ Để một lớp có thể tạo bản sao (clone), lớp đó cần implements giao diện Cloneable.
↳ Lớp cũng cần định nghĩa lại phương thức clone() được thừa hưởng từ lớp Object.
↳ Nếu bạn cố gắng clone một đối tượng của lớp không implements Cloneable, ngoại lệ CloneNotSupportedException sẽ được ném.
Ví dụ: main.java
import java.util.Scanner;
// Lớp Product triển khai Cloneable để hỗ trợ việc sao chép đối tượng
class SanPham implements Cloneable {
int pid; // ID của sản phẩm
String pname; // Tên của sản phẩm
double pcost; // Giá của sản phẩm
// Constructor của lớp Product để khởi tạo các thuộc tính
public SanPham(int pid, String pname, double pcost) {
this.pid = pid; // Gán giá trị cho thuộc tính pid
this.pname = pname; // Gán giá trị cho thuộc tính pname
this.pcost = pcost; // Gán giá trị cho thuộc tính pcost
}
// Phương thức để hiển thị thông tin chi tiết của sản phẩm
public void showDetail() {
System.out.println("ID sản phẩm: " + pid); // Hiển thị ID sản phẩm
System.out.println("Tên sản phẩm: " + pname); // Hiển thị tên sản phẩm
System.out.println("Giá sản phẩm: " + pcost); // Hiển thị giá sản phẩm
}
@Override
public Object clone() throws CloneNotSupportedException {
// Gọi phương thức clone() của lớp Object để sao chép các thuộc tính cơ bản
return super.clone();
}
public static void main(String args[]) throws CloneNotSupportedException {
// Đọc giá trị của sản phẩm từ người dùng
Scanner sc = new Scanner(System.in); // Tạo đối tượng Scanner để đọc dữ liệu từ bàn phím
System.out.print("Nhập ID sản phẩm: ");
int pid = sc.nextInt(); // Đọc ID sản phẩm từ người dùng
System.out.print("Nhập tên sản phẩm: ");
String pname = sc.next(); // Đọc tên sản phẩm từ người dùng
System.out.print("Nhập giá sản phẩm: ");
double pcost = sc.nextDouble(); // Đọc giá sản phẩm từ người dùng
System.out.println("-------Thông tin sản phẩm--------");
// Tạo đối tượng Product mới với các giá trị nhập từ người dùng
SanPham sanpham1 = new SanPham(pid, pname, pcost);
// Sao chép đối tượng p1 bằng phương thức clone()
SanPham sanpham2 = (SanPham) sanpham1.clone(); // Ép kiểu để chuyển đổi đối tượng clone() thành Product
// Gọi phương thức showDetail để hiển thị thông tin của sản phẩm sao chép
sanpham2.showDetail();
}
}
Kết quả của chương trình là:
Nhập tên sản phẩm: car
Nhập giá sản phẩm: 100
-------Thông tin sản phẩm--------
ID sản phẩm: 1
Tên sản phẩm: car
Giá sản phẩm: 100.0
Lưu ý: Đảm bảo rằng lớp của bạn thực sự implements Cloneable để tránh CloneNotSupportedException.
2. Giao diện Serializable (Serializable Interface)
Giao diện Serializable là một giao diện nhãn có sẵn trong gói java.io của Java. Nó cho phép bạn lưu trữ trạng thái của một đối tượng vào luồng dữ liệu (stream) và khôi phục lại đối tượng đó sau này.
Sử dụng:
↳ Để một lớp có thể lưu trữ và khôi phục trạng thái, lớp đó cần implements giao diện Serializable.
↳ Lưu ý rằng Serializable là một giao diện nhãn, không yêu cầu định nghĩa bất kỳ phương thức nào.
↳ Thư viện Java cung cấp các lớp ObjectOutputStream để thực hiện serialization (ghi đối tượng vào luồng) và ObjectInputStream để thực hiện deserialization (đọc đối tượng từ luồng)..

Ví dụ: SerializationExample.java
// Import các lớp từ java.io để sử dụng IO Streams
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; // Import thư viện để sử dụng Serializable
public class Employee implements Serializable { // Lớp Employee implements Serializable
int empid; // Thuộc tính ID của nhân viên
String empname; // Thuộc tính tên của nhân viên
// Constructor của lớp Employee để khởi tạo các thuộc tính
public Employee(int empid, String empname) {
this.empid = empid;
this.empname = empname;
}
}
class SerializationExample {
public static void main(String args[]) {
try {
// Tạo đối tượng Employee
Employee emp = new Employee(1187345, "Nguyễn Thi A");
// Tạo một FileOutputStream để ghi dữ liệu vào file
FileOutputStream fout = new FileOutputStream("Employeedata.txt");
// Tạo một ObjectOutputStream để chuyển đổi đối tượng thành dòng byte và ghi vào file
ObjectOutputStream out = new ObjectOutputStream(fout);
// Ghi đối tượng vào file
out.writeObject(emp);
// Đảm bảo tất cả dữ liệu được ghi vào file
out.flush();
// Đóng ObjectOutputStream để giải phóng tài nguyên
out.close();
// Thông báo rằng dữ liệu đã được ghi vào file
System.out.println("Dữ liệu đã được ghi vào tập tin.");
} catch (Exception e) {
// Xử lý lỗi nếu có xảy ra
e.printStackTrace();
}
}
}
Kết quả của chương trình là:
Trong ví dụ này, chúng ta tạo một đối tượng Employee, sau đó ghi đối tượng này vào một file bằng cách sử dụng serialization. Điều này cho phép bạn lưu trữ trạng thái của đối tượng vào file và sau đó có thể đọc lại nó từ file sau này.
3. Giao diện từ xa (Remote Interface)
Giao diện từ xa (Remote Interface) là một giao diện nhãn có sẵn trong gói java.rmi của Java. Nó được sử dụng trong Remote Method Invocation (RMI) - Gọi Phương Thức Từ Xa.
Mục đích:
↳ Đánh dấu một lớp có khả năng trở thành đối tượng từ xa (remote object), có thể truy cập được từ một máy tính khác trên cùng mạng.
↳ Các phương thức được khai báo trong giao diện từ xa có thể được gọi từ một JVM (Java Virtual Machine) khác.
Sử dụng:
↳ Để tạo một đối tượng từ xa, lớp cần implements giao diện Remote.
↳ Remote là một giao diện nhãn, không yêu cầu định nghĩa bất kỳ phương thức nào.
Triển khai giao diện từ xa: Có hai cách chính để triển khai giao diện từ xa.
1. Mở rộng lớp UnicastRemoteObject:
↳ Lớp cần mở rộng lớp UnicastRemoteObject của Java RMI.
↳ Lớp con sẽ thừa hưởng các phương thức cần thiết để quản lý đối tượng từ xa.
2. Sử dụng phương thức exportObject():
↳ Lớp tạo một đối tượng của UnicastRemoteObject và truyền đối tượng của lớp chính vào hàm khởi tạo.
↳ Phương thức exportObject() của UnicastRemoteObject được gọi để xuất đối tượng cục bộ thành đối tượng từ xa.
Ví dụ về giao diện từ xa (Remote Interface):
Ví dụ: AddAll.java
// Nhập các lớp cần thiết từ gói java.rmi để sử dụng RMI
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface AddAll extends Remote { // Interface AddAll mở rộng Remote
public int add(int r, int s) throws RemoteException; // Phương thức add có thể được gọi từ xa, ném RemoteException nếu có lỗi
}
Ví dụ: AddAllRomete.java
// Nhập các lớp cần thiết từ gói java.rmi
import java.rmi.RemoteException;
// Nhập các lớp cần thiết từ gói java.rmi.server để sử dụng UnicastRemoteObject
import java.rmi.server.UnicastRemoteObject;
public class AddAllRemote extends UnicastRemoteObject implements AddAll { // Lớp AddAllRemote kế thừa từ UnicastRemoteObject và thực hiện AddAll
AddAllRemote() throws RemoteException { // Constructor của lớp, ném RemoteException nếu có lỗi
super(); // Gọi constructor của lớp cha UnicastRemoteObject
}
public int add(int r, int s) { // Triển khai phương thức add từ giao diện AddAll
return r + s; // Trả về tổng của hai số
}
}
Ví dụ: Server.java
// Nhập các lớp cần thiết từ gói java.rmi
import java.rmi.Naming;
public class Server {
public static void main(String args[]) {
try {
AddAll stub = new AddAllRemote(); // Tạo đối tượng của lớp AddAllRemote
Naming.rebind("rmi://localhost:5000/sak", stub); // Đăng ký đối tượng với RMI Registry với tên "rmi://localhost:5000/sak"
} catch (Exception e) {
System.out.println(e); // In lỗi nếu có ngoại lệ
}
}
}
Ví dụ: Client.java
// Nhập các lớp cần thiết từ gói java.rmi
import java.rmi.Naming;
public class Client {
public static void main(String args[]) {
try {
AddAll stub = (AddAll) Naming.lookup("rmi://localhost:5000/sak"); // Tìm đối tượng đăng ký với tên "rmi://localhost:5000/sak"
System.out.println(stub.add(29, 18)); // Gọi phương thức add trên đối tượng từ xa và in kết quả
} catch (Exception e) {
e.printStackTrace(); // In lỗi nếu có ngoại lệ
}
}
}
Tổng kết:
↳ Server: Đăng ký đối tượng từ xa với RMI Registry.
↳ Client: Tìm đối tượng từ xa, gọi phương thức từ xa và in kết quả.
Lưu ý rằng cả Server và Client phải chạy trên cùng một máy (hoặc máy có thể kết nối với nhau) và cổng 5000 phải mở cho giao tiếp RMI. Nếu không, bạn sẽ gặp lỗi kết nối hoặc tìm kiếm đối tượng từ xa.
Tạo giao diện nhãn (Marker Interface) tùy chỉnh
Ngoài các giao diện nhãn tích hợp sẵn, Java cũng cho phép bạn tạo giao diện nhãn của riêng mình. Điều này mang lại sự linh hoạt để đánh dấu các lớp có hành vi hoặc đặc điểm cụ thể trong ứng dụng của bạn.
Ví dụ: Example.java
// Giao diện nhãn tùy chỉnh
interface Printable {
// Không có phương thức nào trong giao diện nhãn
}
// Lớp SinhVien triển khai giao diện nhãn Printable
class SinhVien implements Printable {
private String ten;
private int tuoi;
// Constructor
public SinhVien(String ten, int tuoi) {
this.ten = ten;
this.tuoi = tuoi;
}
// Phương thức hiển thị thông tin sinh viên
public void hienThiThongTin() {
System.out.println("Tên: " + ten);
System.out.println("Tuổi: " + tuoi);
}
// Getters và Setters
public String getTen() {
return ten;
}
public void setTen(String ten) {
this.ten = ten;
}
public int getTuoi() {
return tuoi;
}
public void setTuoi(int tuoi) {
this.tuoi = tuoi;
}
}
public class Example {
public static void main(String[] args) {
// Tạo một đối tượng SinhVien
SinhVien sinhVien = new SinhVien("Nguyen Van A", 20);
// Kiểm tra xem đối tượng có phải là loại đánh dấu Printable không
if (sinhVien instanceof Printable) {
System.out.println("SinhVien implements Printable.");
sinhVien.hienThiThongTin(); // Hiển thị thông tin sinh viên
} else {
System.out.println("SinhVien không implement Printable.");
}
}
}
Kết quả của chương trình là:
Tên: Nguyen Van A
Tuổi: 20
Trong ví dụ này, chương trình này sẽ kiểm tra xem đối tượng SinhVien có triển khai giao diện Printable hay không. Nếu đúng, nó sẽ in ra thông tin của sinh viên, xác nhận rằng đối tượng thuộc loại đánh dấu Printable.