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.

Những điểm cần lưu ý khi sử dụng interface
Ảnh mô tả những điểm cần lưu ý khi sử dụng interface.

Ⅱ. Cú pháp của interface trong Java

Cú Pháp

interface TênGiaoDin {
    // 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ăngPhương thức bình thườngPhương thức trong lớp trừu tượngPhương thức trong interface
Có phần thânKhôngKhông
Trừu tượngKhông
Truy cậpTrực tiếp từ đối tượngChỉ thông qua đối tượng của lớp conChỉ thông qua đối tượng của lớp triển khai
Mặc địnhKhông cóCó thể là public, protected hoặc privateLuôn là public
Kế thừaKhông áp dụngLớp con phải overrideLớp triển khai phải override
Mục đíchThự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ăngBiến trong lớp bình thườngBiến trong lớp trừu tượngBiến trong interface
Loại biếnThành viên, lớpThành viên, lớpChỉ hằng số
Mức truy cậppublic, private, protectedpublic, private, protectedpublic, static, final
Khởi tạoCó thể khởi tạo hoặc khôngCó thể khởi tạo hoặc khôngPhải khởi tạo ngay khi khai báo
Kế thừaLớp con kế thừaLớp con kế thừaLớ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ăngClassAbstract classInterface
publicCó (mặc định)
protectedKhôngKhông
default
privateKhôngKhôngKhông
extendsKhông
abstractKhôngCó (cho phương thức)
implementsKhôngKhông

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

Trình biên dịch tự động thêm từ khóa mặc định
Ảnh mô tả trình biên dịch tự động thêm từ khóa mặc định.

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

Chú chó đang sủa.
Độ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.

Đa kế thừa trong interface
Ảnh mô tả đa kế thừa trong interface.

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

Kế thừa hỗn hợp trong interface
Ảnh mô tả kế thừa hỗn hợp trong interface.

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 A
Đâ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.

Quan hệ giữa class và interface trong Java
Ảnh mô tả quan hệ giữa class và interface trong Java.

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ểmClass (Lớp)Interface (Giao diện)
Khái niệmBả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ầnCó 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ừaMộ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ạoCó 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ứcCó 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ìnhHỗ 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 trong
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 ăn
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ểmAbstract ClassInterface
Khái niệmMộ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ầnCó 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ừaMộ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ạoKhô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 đíchCung 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ụngSử 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 ID sản phẩm: 1
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)..

Khả năng chuyển đổi trạng thái của đối tượng thành luồng dữ liệu (serialization) và ngược lại (deserialization)
Ảnh mô tả khả năng chuyển đổi trạng thái của đối tượng thành luồng dữ liệu (serialization) và ngược lại (deserialization).

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

Dữ liệu đã được ghi vào tập tin.

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

SinhVien implements Printable.
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.

Câu Nói Truyền Cảm Hứng

“Không ai sinh ra đã giỏi. Mọi thành công đều bắt đầu từ một bước nhỏ.” – Lao Tzu

Không Gian Tích Cực

“Chúc bạn một ngày mới đầy năng lượng và sự sáng tạo, luôn tiến về phía trước.”