Gói (Packages)
"Trong Java, gói (package) là một cách tổ chức các lớp và giao diện liên quan thành một nhóm logic. Điều này giúp quản lý mã nguồn dễ dàng hơn, tránh xung đột tên, và hỗ trợ tái sử dụng mã một cách hiệu quả. Dưới đây là những điều cơ bản bạn cần biết về gói trong Java."
Ⅰ. Gói (Packages) là gì?
Một gói (packages) trong java là một nhóm các "kiểu dữ liệu" có liên quan với nhau, cung cấp tính bảo mật truy cập và quản lý không gian tên. Lưu ý rằng "kiểu dữ liệu" ở đây đề cập đến các lớp (class), giao diện (interface), kiểu liệt kê (enumeration) và kiểu chú thích (annotation type). Kiểu liệt kê và kiểu chú thích là những dạng đặc biệt của lớp và giao diện, vì vậy "kiểu dữ liệu" trong bài học này thường được gọi đơn giản là lớp và giao diện.
Trong Java, gói (package) có thể được phân loại theo hai hình thức chính:
↳ Gói được dựng sẵn (Built-in Packages)
↳ Gói do người dùng định nghĩa (User-defined Packages)

Ⅱ. Gói được dựng sẵn (Built-in Packages)
Các gói được dựng sẵn là các gói được cung cấp sẵn bởi Java API (Application Programming Interface). Chúng bao gồm rất nhiều lớp và giao diện mà bạn có thể sử dụng để phát triển ứng dụng của mình mà không cần phải tự viết lại từ đầu. Không có con số chính xác cho số lượng Gói Được Dựng Sẵn (Built-in Packages) trong Java vì nó liên tục được cập nhật và phát triển. Tuy nhiên, dựa trên tài liệu tham khảo, khảo sát cộng đồng và kinh nghiệm thực tế, ta có thể liệt kê một số gói phổ biến sau:
1. Gói cơ bản (java.lang): Chứa các lớp cốt lõi như Object, String, Math, System, v.v., cung cấp các chức năng cơ bản như thao tác chuỗi, số học, nhập/xuất dữ liệu, v.v.
2. Gói thu thập (java.util): Cung cấp các cấu trúc dữ liệu và thuật toán hữu ích như danh sách, tập hợp, bản đồ, v.v., giúp quản lý và xử lý dữ liệu hiệu quả.
3. Gói I/O (java.io): Cung cấp các lớp để đọc và ghi dữ liệu từ các nguồn khác nhau như tệp tin, bàn phím, mạng, v.v.
4. Gói thời gian (java.time): Gói java.time chứa các lớp để làm việc với ngày và giờ, được giới thiệu từ Java 8.
5. Gói SQL (java.sql): Cung cấp các lớp để kết nối và thao tác với cơ sở dữ liệu SQL.
6. Gói giao diện (javax.swing): Gói javax.swing chứa các lớp để xây dựng giao diện người dùng (GUI)
7. Gói mạng (java.net): Cung cấp các lớp để lập trình mạng như kết nối socket, giao thức HTTP, FTP, v.v.
8. Gói xử lý ngoại lệ (java.lang.Exception): Cung cấp các lớp để xử lý các lỗi và ngoại lệ trong chương trình.
9. Gói đồng thời (java.util.concurrent): Cung cấp các lớp và giao diện để lập trình đa luồng và đồng thời trong Java.
10. Gói bảo mật (java.security): Cung cấp các lớp cho các tính năng bảo mật như mã hóa, xác thực, quản lý khóa, v.v.
11. Gói chú thích (java.lang.annotation): Cung cấp các lớp để tạo và sử dụng chú thích trong Java.
Ngoài ra, một số gói khác cũng có thể được sử dụng phổ biến tùy theo lĩnh vực ứng dụng cụ thể, ví dụ:
↳ Gói xử lý ảnh (javax.imageio)
↳ Gói xử lý XML (javax.xml)
↳ Gói dịch vụ web (javax.ws)
↳ Gói GUI (javafx, Swing)
Lưu ý: Mức độ phổ biến của các gói có thể thay đổi theo thời gian và giữa các dự án khác nhau. Việc sử dụng các gói phụ thuộc vào nhu cầu cụ thể của từng ứng dụng. Nên tham khảo tài liệu và hướng dẫn sử dụng cho từng gói để sử dụng hiệu quả.
Bên cạnh danh sách trên, bạn có thể tham khảo thêm trang web chính thức của Oracle để tìm hiểu về các Gói Được Dựng Sẵn (Built-in Packages) trong Java:Tại đây
Ⅲ. Gói do người dùng định nghĩa (User-defined Packages)
Gói do người dùng định nghĩa là các gói mà bạn tạo ra để tổ chức các lớp và giao diện của riêng bạn. Việc sử dụng gói tự định nghĩa giúp bạn quản lý mã nguồn một cách có cấu trúc và tránh xung đột tên.
Lý do sử dụng Gói(Packages)?
↳ Nhóm các thành phần liên quan: Gói giúp bạn nhóm các lớp và giao diện có liên quan với nhau. Điều này giúp mã nguồn của bạn dễ quản lý và dễ hiểu hơn.
↳ Quản lý không gian tên (Namespace Management): Gói giúp tránh xung đột tên lớp bằng cách tổ chức chúng trong các không gian tên riêng biệt. Ví dụ, bạn có thể có hai lớp có tên giống nhau trong các gói khác nhau.
↳ Kiểm soát truy cập (Access Control): Gói cung cấp cơ chế bảo mật để kiểm soát quyền truy cập vào các thành phần của nó. Bạn có thể sử dụng các từ khóa truy cập như public, protected, và private để giới hạn quyền truy cập vào các thành phần của gói.
↳ Tái sử dụng mã nguồn: Gói cho phép bạn tái sử dụng mã nguồn dễ dàng bằng cách tổ chức các thư viện và chức năng chung trong các gói riêng biệt. Điều này giúp giảm thiểu việc viết lại mã và tăng hiệu suất làm việc.
Tạo một Gói (Package)
Để tạo một Gói, bạn cần thực hiện các bước sau:
↳ Chọn tên Gói: Quy tắc đặt tên Gói sẽ được đề cập trong phần tiếp theo.
↳ Thêm câu lệnh package: Đặt một câu lệnh package với tên Gói đã chọn ở đầu mỗi tệp nguồn chứa các kiểu dữ liệu (lớp, giao diện, kiểu liệt kê và kiểu chú thích) mà bạn muốn đưa vào Gói đó.
↳ Câu lệnh package (ví dụ: package com.example.myapp;) phải là dòng đầu tiên trong tệp nguồn.
↳ Lưu Trữ Tệp: Lưu trữ tệp Java trong một thư mục tương ứng với tên gói.
Ví dụ: MyClass.java
// Lưu tệp này với tên MyClass.java trong thư mục com/example/myapp
package com.example.myapp;
public class MyClass {
public void display() {
System.out.println("Đây là phương thức của lớp MyClass trong gói com.example.myapp");
}
}
Cách đặt tên Gói (Package)
Tại sao cần đặt tên gói trong Java?
Hãy tưởng tượng hàng triệu lập trình viên Java trên thế giới cùng viết các lớp và giao diện. Rất có khả năng nhiều người sẽ sử dụng cùng một cái tên cho các kiểu dữ liệu khác nhau. Ví dụ, có hai lớp Service được định nghĩa trong hai gói khác nhau:
↳ Gói com.example
↳ Gói java.awt
Mặc dù tên lớp giống nhau, trình biên dịch vẫn cho phép vì chúng nằm trong các gói khác nhau. Tên đầy đủ của mỗi lớp bao gồm cả tên gói:
↳ com.example.Service
↳ java.awt.Service
Tuy nhiên, vấn đề xảy ra khi hai lập trình viên độc lập sử dụng cùng một tên cho các gói của họ. Lúc này, trình biên dịch không thể phân biệt được các lớp có cùng tên và sẽ dẫn đến lỗi.
Để giải quyết vấn đề này, Java sử dụng quy ước đặt tên (Naming Conventions). Quy ước này giúp đảm bảo rằng các tên gói và tên lớp không bị trùng lặp, tránh xung đột và tạo ra một hệ thống tổ chức mã rõ ràng, dễ quản lý.
Quy ước đặt tên (Naming Conventions)
↳ Tên gói nên được viết toàn bộ bằng chữ thường: Điều này giúp tránh xung đột với tên của các lớp hoặc giao diện, vì tên lớp và giao diện thường bắt đầu bằng chữ hoa.
↳ Các công ty sử dụng tên miền ngược lại để đặt tên gói: Ví dụ, công ty có tên miền example.com sẽ sử dụng com.example làm tiền tố cho các gói của họ.
↳ Tránh xung đột tên trong cùng một công ty: Bằng cách thêm tên khu vực hoặc tên dự án sau tên công ty để tránh xung đột trong cùng một công ty. (ví dụ: com.example.region.mypackage).
↳ Gói chuẩn của Java: Các gói Được Dựng Sẵn trong java luôn bắt đầu với tiền tố java. hoặc javax. cung cấp các chức năng cốt lõi cho ngôn ngữ Java.
↳ Tên miền không hợp lệ làm tên gói: Nếu tên miền chứa ký tự đặc biệt hoặc không hợp lệ theo quy tắc đặt tên của Java, bạn có thể thêm dấu gạch dưới để hợp pháp hóa tên gói.
Ví dụ hợp pháp hóa tên gói:
↳ Tên miền hyphenated-name.example.org sẽ trở thành gói org.example.hyphenated_name.
↳ Tên miền example.int sẽ trở thành gói int_.example.
↳ Tên miền 123name.example.com sẽ trở thành gói com.example._123name.
Truy cập các thành phần của Gói (Package)
Các thành phần của gói bao gồm các lớp (classes), giao diện (interfaces), kiểu liệt kê (enumerations) và kiểu chú thích (annotation types). Các thành phần này được gọi là thành viên của gói (package members).
Có ba cách để truy cập một thành phần công khai của một gói từ bên ngoài gói đó:
↳ Sử dụng tên đầy đủ của thành phần đó.
↳ Import thành phần đó.
↳ Import toàn bộ gói chứa thành phần đó.
Mỗi cách phù hợp cho các tình huống khác nhau, được giải thích chi tiết trong các phần tiếp theo.
Sử dụng tên đầy đủ của thành phần đó.
Nếu bạn đang cố gắng sử dụng một thành phần từ một Gói khác và Gói đó chưa được import, bạn phải sử dụng tên đầy đủ của thành phần đó, bao gồm cả tên Gói. Ví dụ, nếu bạn có một lớp Rectangle trong gói graphics, tên đầy đủ của lớp đó sẽ là graphics.Rectangle.
Ví dụ: Main.java
// Import gói java.awt để sử dụng lớp Graphics
import java.awt.*;
public class Main {
public static void main(String[] args) {
// Tạo đối tượng Rectangle từ gói graphics
graphics.Rectangle myRectangle = new graphics.Rectangle(10, 20);
}
}
Sử dụng tên đầy đủ phù hợp cho các trường hợp sử dụng không thường xuyên. Tuy nhiên, khi một tên được sử dụng nhiều lần, việc gõ tên nhiều lần sẽ trở nên tẻ nhạt và code khó đọc. Như một cách thay thế, bạn có thể import thành phần đó hoặc toàn bộ Gói của nó và sau đó sử dụng tên đơn giản.
Import thành phần đó.
Nếu bạn muốn sử dụng một thành phần nhiều lần mà không muốn gõ tên đầy đủ, bạn có thể import thành phần đó. Câu lệnh import được đặt ở đầu file, sau câu lệnh package (nếu có). Dưới đây là cách bạn import lớp Rectangle từ Gói graphics được tạo trong phần trước:
Ví dụ: Main.java
import graphics.Rectangle;
public class Example {
public static void main(String[] args) {
Rectangle myRectangle = new Rectangle();
}
}
Cách tiếp cận này hoạt động tốt nếu bạn chỉ sử dụng một vài thành phần từ Gói graphics. Nhưng nếu bạn sử dụng nhiều kiểu dữ liệu từ một Gói, bạn nên import toàn bộ Gói.
Import toàn bộ gói chứa thành phần đó.
Để import tất cả các kiểu dữ liệu có trong một Gói cụ thể, hãy sử dụng câu lệnh import với ký tự wildcard (*).
Ví dụ: Main.java
import graphics.*;
public class Test {
public static void main(String[] args) {
Circle myCircle = new Circle();
Rectangle myRectangle = new Rectangle();
}
}
Sử dụng ký tự wildcard (*) sẽ import tất cả các lớp và giao diện trong gói graphics. Bây giờ bạn có thể tham chiếu đến bất kỳ lớp hoặc giao diện nào trong Gói graphics bằng tên đơn giản của nó.
Lưu ý về ký tự Wildcard (*)
Ký tự wildcard (*) chỉ có thể được sử dụng để import tất cả các lớp và giao diện trong một gói cụ thể. Bạn không thể sử dụng ký tự wildcard để chỉ định một tập hợp con của các lớp trong gói đó. Ví dụ, bạn không thể sử dụng ký tự wildcard để import chỉ các lớp bắt đầu bằng chữ 'A'.
Ví dụ Sai
// Không hợp lệ - Sẽ tạo ra lỗi biên dịch
import graphics.A*;
Câu lệnh import graphics.A*; sẽ tạo ra lỗi biên dịch vì Java không hỗ trợ việc import một tập hợp con của các lớp theo tên.
Câu lệnh import static
Trong một số trường hợp, bạn cần truy cập thường xuyên đến các hằng số tĩnh (static final field) và các phương thức tĩnh (static method) từ một hoặc hai lớp. Việc thêm tiền tố tên của các lớp này nhiều lần có thể khiến code trở nên lộn xộn. Câu lệnh static import cung cấp cho bạn một cách để import các hằng số và phương thức tĩnh mà bạn muốn sử dụng để không cần thêm tiền tố tên lớp của chúng.
Lớp java.lang.Math định nghĩa hằng số PI (số Pi) và nhiều phương thức tĩnh, bao gồm các phương thức tính sin, cos, tan, căn bậc hai, giá trị lớn nhất, giá trị nhỏ nhất, lũy thừa và nhiều hơn nữa. Thông thường, để sử dụng các đối tượng này từ một lớp khác, bạn thêm tiền tố tên lớp, như sau:
Cú Pháp
double r = Math.cos(Math.PI * theta);
Bạn có thể sử dụng câu lệnh static import để import các thành phần tĩnh của java.lang.Math để không cần thêm tiền tố tên lớp Math. Các thành phần tĩnh của Math có thể được import riêng lẻ:
Cú Pháp
import static java.lang.Math.PI;
import static java.lang.Math.cos;
Hoặc bạn có thể import tất cả các thành phần tĩnh của lớp Math:
Cú Pháp
import static java.lang.Math.*;
Sau khi được import, các thành phần tĩnh có thể được sử dụng mà không cần thêm tiền tố. Ví dụ, đoạn code mẫu trước đó sẽ trở thành:
Cú Pháp
double r = cos(PI * theta);
Ví dụ cụ thể khác
Bạn cũng có thể tạo các lớp riêng của mình chứa các hằng số và phương thức tĩnh mà bạn sử dụng thường xuyên. Giả sử bạn có lớp MyConstants trong gói mypackage như sau:
Ví dụ: MyConstants.java
package mypackage;
public class MyConstants {
public static final double MY_CONSTANT = 3.14;
public static double myMethod(double x) {
return x * x;
}
}
Bạn có thể import các thành phần tĩnh từ lớp MyConstants:
Cú Pháp
import static mypackage.MyConstants.MY_CONSTANT;
import static mypackage.MyConstants.myMethod;
Hoặc import tất cả các thành phần tĩnh:
Cú Pháp
import static mypackage.MyConstants.*;
Bây giờ bạn có thể sử dụng chúng mà không cần thêm tiền tố tên lớp:
Cú Pháp
double value = myMethod(MY_CONSTANT);
Lưu ý: Sử dụng static import một cách hạn chế. Lạm dụng static import có thể dẫn đến code khó đọc và khó bảo trì, vì người đọc code sẽ không biết lớp nào định nghĩa một đối tượng
Gói con (Subpackage) trong Java
Một gói nằm trong một gói khác được gọi là gói con. Gói con được tạo ra để phân loại các gói theo một cách chi tiết hơn.
Ví dụ: Sun Microsystems đã định nghĩa một gói có tên là java chứa nhiều lớp như System, String, Reader, Writer, Socket, v.v. Các lớp này đại diện cho các nhóm chức năng cụ thể. Ví dụ, các lớp Reader và Writer được sử dụng cho các hoạt động nhập/xuất (Input/Output), các lớp Socket và ServerSocket được sử dụng cho các hoạt động mạng (networking), v.v.
Để quản lý và tổ chức các lớp này một cách hợp lý, Sun Microsystems đã phân loại gói java thành các gói con như lang, net, io, v.v. Các lớp liên quan đến nhập/xuất (Input/Output) được đặt trong gói io, các lớp Socket và ServerSocket được đặt trong gói net, v.v. Ví dụ về cấu trúc gói và gói con:
Gói (Package) trong Java
↳ Gói con java.lang chứa các lớp cơ bản như String, System.
↳ Gói con java.io chứa các lớp liên quan đến nhập/xuất như Reader, Writer.
↳ Gói con java.net chứa các lớp liên quan đến mạng như Socket, ServerSocket.
Bằng cách sử dụng gói con, chúng ta có thể tổ chức mã nguồn một cách logic và dễ quản lý hơn, giúp người lập trình dễ dàng tìm kiếm và sử dụng các lớp cần thiết cho dự án của mình.