Lập Trình Hướng Đối Tượng (OOP) Trong Java: Từ Khái Niệm Đến Ứng Dụng Chi Tiết

Lập trình hướng đối tượng (OOP) là một phương pháp lập trình mạnh mẽ, đóng vai trò then chốt trong việc xây dựng các ứng dụng Java hiện đại. Bài viết này sẽ cung cấp một cái nhìn toàn diện về OOP trong Java, từ những khái niệm cơ bản như đối tượng, lớp, package đến các khái niệm nâng cao hơn như constructor và phạm vi truy cập.

Khái niệm về lập trình hướng đối tượng trong Java

Lập trình hướng đối tượng (Object-Oriented Programming – OOP) là một phương pháp thiết kế chương trình bằng cách sử dụng các lớp và các đối tượng.

Java là một ngôn ngữ lập trình hướng đối tượng, do đó nó hỗ trợ các đặc tính của lập trình hướng đối tượng:

  • Đa hình (Polymorphism)
  • Thừa kế (Inheritance)
  • Đóng gói (Encapsulation)
  • Trừu tượng (Abstraction)

Mô hình lập trình hướng đối tượng OOP trong JavaMô hình lập trình hướng đối tượng OOP trong Java

Đối tượng (Object) trong Java

Đối tượng là một thực thể có trạng thái và hành vi. Nó có thể mang tính vật lý hoặc logic.

Trong thế giới thực, chúng ta có thể tìm thấy nhiều đồ vật xung quanh: cái bàn, con chó, con người,… Tất cả các đối tượng này đều có thuộc tính và hành vi.

Ví dụ, một con chó có các thuộc tính như tên, giống, màu sắc và các hành vi như sủa, chạy, ăn,… Trong phần mềm, thuộc tính của đối tượng được lưu trữ trong trường (field) và hành vi được lưu trữ trong phương thức (method).

Lớp (Class) trong Java

Lớp có thể được xem như một khuôn mẫu (template) của đối tượng (Object). Nó bao gồm dữ liệu của đối tượng (fields hay properties) và các phương thức (methods) tác động lên thành phần dữ liệu đó.

Khai báo lớp

Cú pháp:

class <Tên lớp> {
    // Khai báo thuộc tính (field) hay biến
    <Kiểu dữ liệu> <Tên biến 1>;
    <Kiểu dữ liệu> <Tên biến 2>;

    // Hàm khởi tạo không có tham số
    <Tên lớp>() {
        // Khởi tạo dữ liệu
    }

    // Hàm khởi tạo có tham số
    <Tên lớp>(<Kiểu dữ liệu> <Tham số 1>[, <Kiểu dữ liệu> <Tham số 2>]) {
        // Khởi tạo dữ liệu
    }

    // Phương thức xử lý (method)
    <Kiểu trả về> method_1() {
        // Logic xử lý
    }

    <Kiểu trả về> method_2(<Tham số>) {
        // Logic xử lý
    }
}

Trong đó:

  • class: từ khóa để khai báo lớp trong Java.
  • <Tên lớp>: tên của lớp (viết hoa mỗi chữ cái đầu).
  • field_1, field_2: các thuộc tính, biến hoặc thành phần dữ liệu của lớp (chữ cái đầu tiên viết thường, các chữ cái đầu của từ tiếp theo viết hoa).
  • Hàm khởi tạo (constructor): hàm dùng để khởi tạo đối tượng lớp.
  • method_1, method_2: các phương thức thể hiện các thao tác xử lý, tác động lên các thành phần dữ liệu của lớp.

Ví dụ:

public class Student {
    // Khai báo thuộc tính
    private int id;
    private String name;

    // Hàm khởi tạo có 1 tham số là id
    public Student(int id) {
        // Sử dụng từ khóa this để truy cập thuộc tính trong lớp
        this.id = id;
    }

    // Cung cấp phương thức lấy giá trị id
    public int getId() {
        return id;
    }

    // Cung cấp phương thức lấy giá trị name
    public String getName() {
        return name;
    }

    // Cung cấp phương thức gán giá trị name
    public void setName(String name) {
        this.name = name;
    }
}

Lưu ý:

  • Từ khóa this dùng để chỉ đến chính lớp đang được tham chiếu đến. this.name sẽ tham chiếu đến thuộc tính name của class Student.
  • Các phương thức (method) dùng để gán giá trị của một thuộc tính (field) nên bắt đầu là set.
  • Các phương thức (method) dùng để lấy giá trị của một thuộc tính (field) nên bắt đầu là get.

Sử dụng lớp

Cú pháp:

<Tên lớp> <Tên đối tượng> = new <Tên lớp>();
<Tên đối tượng>.<Thuộc tính> = <Giá trị>;
<Tên đối tượng>.<Phương thức>();

Sử dụng từ khóa new để khởi tạo một đối tượng của một lớp.

Ví dụ:

Student student = new Student(1);
student.setName("Sen Tay Ho");
System.out.println("Id=" + student.getId());
System.out.println("Name=" + student.getName());

Sự khác nhau giữa lớp và đối tượng trong Java

Đặc điểm Đối tượng Lớp
Khái niệm Thể hiện của một lớp Khuôn mẫu hoặc thiết kế để tạo ra các đối tượng
Tính chất Thực thể trong thế giới thực (ví dụ: con mèo) Nhóm các đối tượng tương tự (ví dụ: lớp động vật)
Bản chất Thực thể vật lý Thực thể logic
Cách tạo Sử dụng từ khóa new Sử dụng từ khóa class
Số lượng Có thể tạo nhiều lần Chỉ được khai báo một lần duy nhất
Cấp phát bộ nhớ Được cấp phát bộ nhớ khi tạo Không được cấp phát bộ nhớ khi tạo
Tạo thành phần Nhiều cách (new, newInstance, clone,…) Chỉ có một cách (class)

Package trong Java

Định nghĩa

Một package (gói) trong Java là một nhóm các kiểu tương tự của các lớp, giao diện và các package con.

Package trong Java có thể được phân loại theo hai hình thức, package được dựng sẵn và package do người dùng định nghĩa.

Có rất nhiều package được dựng sẵn như java.lang, java.net, java.io, java.util, java.sql,…

Package do người dùng tự định nghĩa

Cú pháp:

package [<Tên package cha>.[<Tên package con>]];

Ví dụ về Java package:

package gpcoder; // Package cha
package gpcoder.example; // Package example là con của package gpcoder

Lợi thế của việc sử dụng package trong Java:

  • Package được sử dụng để phân loại lớp và interface giúp dễ dàng bảo trì.
  • Package cung cấp bảo vệ truy cập.
  • Package khắc phục được việc đặt trùng tên.

Truy cập package từ package khác

Có 3 cách để truy cập package từ package bên ngoài:

  • *Khai báo `import <Tên package>.**: Tránh sử dụng cách này vì không xác định sẽ sử dụng class nào, có thể gặp vấn đề trùng tên lớp nếu cả 2 package importpackage.giống nhau. Ví dụ: sử dụng classDatecó thể gặp lỗi biên dịch do không thể xác định chính xác sử dụng classDatecủa package nào nếu import cảjava.util.java.sql.*`.
package com.example;
import java.util.*;
import java.sql.*;
import com.example.service.*;
public class PackageDemo {
    public static void main(String[] args) {
        Date d = new Date();
    }
}
  • Khai báo import <Tên package>.<Tên class>: Nên sử dụng cách này để giữ code đơn giản, rõ ràng, tái sử dụng lại nhiều chỗ, hạn chế xung đột về tên.
  • Sử dụng tên đầy đủ: Tránh sử dụng cách này, do code trở nên dài dòng nếu package gồm nhiều cấp cha, con.

Ví dụ: một project có package như sau:

Cấu trúc package trong JavaCấu trúc package trong Java

Class CommonService cung cấp 2 phương thức method1method2.

Chúng ta có thể sử dụng method1 từ một class khác ngoài package com.example.service như sau:

  • Khai báo import com.example.service.*:
package com.example;
import com.example.service.*;
public class PackageDemo {
    public static void main(String[] args) {
        CommonService service = new CommonService();
        service.method1();
    }
}
  • Khai báo import com.example.service.CommonService:
package com.example;
import com.example.service.CommonService;
public class PackageDemo {
    public static void main(String[] args) {
        CommonService service = new CommonService();
        service.method1();
    }
}
  • Sử dụng tên đầy đủ:
package com.example;
public class PackageDemo {
    public static void main(String[] args) {
        com.example.service.CommonService service = new com.example.service.CommonService();
        service.method1();
    }
}

Lưu ý:

  • Nếu bạn import một package thì package con của package đó không được import.
  • Thứ tự của chương trình phải là khai báo package -> import -> class.
  • Khi sử dụng một class của package này ở package khác nên sử dụng ở dạng import <Tên package>.<Tên class>.
  • Mỗi package tương ứng với một thư mục.

Constructor trong Java

Định nghĩa

Constructor trong Java là một dạng đặc biệt của phương thức được sử dụng để khởi tạo các đối tượng.

Java Constructor được gọi tại thời điểm tạo đối tượng. Nó khởi tạo các giá trị để cung cấp dữ liệu cho các đối tượng, đó là lý do tại sao nó được gọi là constructor.

Khai báo của Constructor giống với khai báo của method (phương thức). Nó phải có cùng tên với class (lớp)không có giá trị trả về.

Có 2 kiểu của constructor:

  • Constructor mặc định (không có tham số truyền vào – default constructors).
  • Constructor tham số (parameterized constructors).

Constructor mặc định trong Java

Một constructor mà không có tham số được gọi là constructor mặc định. Constructor mặc định được sử dụng để cung cấp các giá trị mặc định cho các thuộc tính như 0, null, false …, tùy thuộc vào kiểu dữ liệu được sử dụng.

Nếu một lớp không khai báo contructor thì trình biên dịch sẽ tự động tạo một constructor mặc định trong lớp đó.

Cú pháp:

<Tên lớp>() {
    // Khởi tạo dữ liệu
}

Ví dụ:

package com.example;
public class JavaDefaultConstructor {
    public JavaDefaultConstructor() {
        System.out.println("This is default constructor.");
    }
    public static void main(String[] args) {
        JavaDefaultConstructor obj = new JavaDefaultConstructor();
    }
}

Kết quả khi chạy chương trình trên:

This is default constructor.

Constructor tham số trong Java

Một constructor có tham số truyền vào được gọi là constructor tham số. Constructor tham số được sử dụng để cung cấp các giá trị khác nhau cho các đối tượng khác nhau.

<Tên lớp>(<Kiểu dữ liệu> <Tham số 1>[, <Kiểu dữ liệu> <Tham số 2>]) {
    // Khởi tạo dữ liệu
}

Ví dụ:

package com.example;
public class JavaParameterizedConstructor {
    private String website;
    public JavaParameterizedConstructor(String website) {
        this.website = website;
        System.out.println("This is parameterized constructor");
        System.out.println("from website = " + website);
    }
    public static void main(String[] args) {
        JavaParameterizedConstructor obj = new JavaParameterizedConstructor("gpcoder.com");
    }
}

Kết quả:

This is parameterized constructor
from website = gpcoder.com

Overloading Constructor trong Java

Constructor Overloading là một kỹ thuật trong Java. Bạn có thể tạo nhiều constructor trong cùng một lớp với danh sách tham số truyền vào khác nhau, điều này được gọi là phép đa năng hóa (Overloading). Trình biên dịch phân biệt các constructor này thông qua số lượng và kiểu của các tham số truyền vào.

Ví dụ:

package com.example;
public class JavaOverloadingConstructors {
    private String website;
    private String subject;

    public JavaOverloadingConstructors() {
        System.out.println("This is default constructor.");
    }

    public JavaOverloadingConstructors(String website) {
        this.website = website;
        System.out.println("This is parameterized constructor");
        System.out.println("from website = " + website);
    }

    public JavaOverloadingConstructors(String website, String subject) {
        this.website = website;
        this.subject = subject;
        System.out.println("This is parameterized constructor");
        System.out.println("from website = " + website);
        System.out.println("with subject = " + subject);
    }

    public static void main(String[] args) {
        JavaOverloadingConstructors obj = new JavaOverloadingConstructors();
        System.out.println("-");
        obj = new JavaOverloadingConstructors("gpcoder.com");
        System.out.println("-");
        obj = new JavaOverloadingConstructors("gpcoder.com", "OOP");
    }
}

Kết quả:

This is default constructor.
-
This is parameterized constructor
from website = gpcoder.com
-
This is parameterized constructor
from website = gpcoder.com
with subject = OOP

Lưu ý:

  • Từ khóa this trong java là một biến tham chiếu được sử dụng để tham chiếu tới đối tượng của lớp hiện tại.
  • Từ khóa super trong java là một biến tham chiếu được sử dụng để tham chiếu trực tiếp đến đối tượng của lớp cha gần nhất.

Sự khác nhau giữa constructor và phương thức trong Java

Đặc điểm Constructor Phương thức
Chức năng Khởi tạo trạng thái của một đối tượng Thể hiện hành động của một đối tượng
Kiểu trả về Không có kiểu trả về Có kiểu trả về
Tạo mặc định Trình biên dịch Java tạo nếu không có constructor nào Không được tạo bởi trình biên dịch Java
Tên Phải giống tên lớp Có thể giống hoặc khác tên lớp

Phạm vi truy cập (Access Modifier) trong Java

Có hai loại modifier trong java: access modifiersnon-access modifiers.

Các access modifiers trong java xác định độ truy cập (Phạm vi) vào dữ liệu của của các trường (field), phương thức (method), cấu tử (constructor) hoặc lớp (class).

Có 4 kiểu của java access modifiers:

  • private
  • (default)
  • protected
  • public

Và có một vài non-access modifiers chẳng hạn static, abstract, synchronized, native, volatile, transient,…

Bảng mô tả tổng quan về cách sử dụng các access modifier:

Access Modifier Truy cập bên trong class? Truy cập bên trong package? Truy cập bên ngoài package bởi class con? Truy cập bên ngoài class và không thuộc class con?
private Không Không Không
default Không Không
protected Không
public

Kết luận

Bài viết đã cung cấp một cái nhìn tổng quan về lập trình hướng đối tượng trong Java. Hiểu rõ các khái niệm và nguyên tắc OOP là rất quan trọng để xây dựng các ứng dụng Java mạnh mẽ, dễ bảo trì và mở rộng. Hy vọng rằng bài viết này sẽ giúp bạn nắm vững kiến thức cơ bản và áp dụng OOP hiệu quả trong các dự án thực tế của mình.

Tài liệu tham khảo