Khi bước vào thế giới lập trình hướng đối tượng (OOP là gì) với Java, việc tạo ra các đối tượng là thao tác nền tảng nhất. Nhưng làm thế nào để đảm bảo mỗi đối tượng khi được “sinh ra” đều có một trạng thái khởi đầu hợp lệ và đúng chuẩn? Câu trả lời nằm ở một khái niệm cơ bản nhưng cực kỳ quan trọng: hàm khởi tạo, hay còn gọi là Constructor. Đây chính là công cụ giúp lập trình viên kiểm soát quá trình tạo và gán giá trị ban đầu cho đối tượng một cách chính xác.
Nhiều người mới học Java thường cảm thấy bối rối, nhầm lẫn giữa hàm khởi tạo và các phương thức thông thường, dẫn đến việc sử dụng sai cách hoặc bỏ qua vai trò thiết yếu của nó. Bài viết này sẽ là kim chỉ nam của bạn, giải thích cặn kẽ từ định nghĩa, cách khai báo, các loại hàm khởi tạo cho đến nguyên tắc hoạt động sâu bên trong. Chúng ta sẽ cùng nhau đi từ những khái niệm cơ bản nhất, qua các ví dụ minh họa trực quan, để bạn có thể tự tin làm chủ công cụ mạnh mẽ này trong các dự án Java của mình.
Định nghĩa và khái niệm cơ bản về hàm khởi tạo trong Java
Để sử dụng thành thạo, trước tiên chúng ta cần hiểu rõ bản chất và vai trò của hàm khởi tạo. Đây là viên gạch đầu tiên xây dựng nên một đối tượng hoàn chỉnh.
Hàm khởi tạo là gì?
Hàm khởi tạo (constructor) trong Java là một khối mã đặc biệt được sử dụng để khởi tạo các đối tượng mới được tạo ra. Khi bạn dùng từ khóa new để tạo một instance của lớp, hàm khởi tạo sẽ được gọi tự động để thiết lập trạng thái ban đầu cho đối tượng đó, chẳng hạn như gán giá trị cho các biến instance.
Dù có cấu trúc trông giống một phương thức (method là gì), hàm khởi tạo có những điểm khác biệt cơ bản:
- Tên hàm: Tên của hàm khởi tạo phải trùng khớp tuyệt đối với tên của lớp.
- Kiểu trả về: Hàm khởi tạo không có bất kỳ kiểu trả về nào, kể cả
void.
Sự khác biệt này là có chủ đích. Mục đích duy nhất của hàm khởi tạo là “khởi tạo” một đối tượng, không phải thực hiện một hành động và trả về kết quả như các phương thức thông thường.

Vai trò của hàm khởi tạo trong Java
Vai trò chính của hàm khởi tạo không chỉ dừng lại ở việc tạo đối tượng. Nó đóng vai trò cốt lõi trong việc đảm bảo tính toàn vẹn và hợp lệ của dữ liệu ngay từ khi đối tượng ra đời.
Một là, nó tạo và khởi tạo đối tượng. Đây là nhiệm vụ cơ bản nhất. Nếu không có hàm khởi tạo, bạn không thể tạo một đối tượng từ lớp đó. Trình biên dịch Java sẽ tự động cung cấp một hàm khởi tạo mặc định nếu bạn không tự định nghĩa bất kỳ hàm khởi tạo nào, giúp quá trình tạo đối tượng luôn có thể diễn ra.
Hai là, nó đảm bảo đối tượng có trạng thái ban đầu hợp lệ. Đây là vai trò quan trọng nhất. Bạn có thể dùng hàm khởi tạo để gán các giá trị cụ thể cho các thuộc tính, đảm bảo rằng không có đối tượng nào được tạo ra trong trạng thái “trống” hoặc không xác định. Ví dụ, một đối tượng NhanVien khi tạo ra phải có maNhanVien và tenNhanVien, hàm khởi tạo sẽ đảm bảo các thông tin này được cung cấp ngay lập tức.
Cách khai báo và sử dụng hàm khởi tạo trong Java
Hiểu được khái niệm rồi, giờ là lúc chúng ta tìm hiểu cú pháp khai báo và cách gọi hàm khởi tạo trong thực tế. Việc này khá đơn giản và tuân theo một quy tắc rõ ràng.
Cấu trúc khai báo hàm khởi tạo
Cú pháp để khai báo một hàm khởi tạo rất đặc trưng và dễ nhận biết. Bạn chỉ cần nhớ hai quy tắc vàng:
- Tên của hàm khởi tạo phải giống hệt tên lớp.
- Hàm khởi tạo không được có kiểu trả về (không phải
void, mà là không có gì cả).
Hãy xem một ví dụ đơn giản với lớp XeHoi:
public class XeHoi {
String mauSac;
// Đây là một hàm khởi tạo đơn giản
public XeHoi() {
mauSac = "Đỏ"; // Gán giá trị mặc định cho thuộc tính mauSac
}
}
Trong ví dụ trên, public XeHoi() chính là một hàm khởi tạo. Nó có cùng tên với lớp XeHoi và không có kiểu trả về. Khi một đối tượng XeHoi được tạo bằng hàm khởi tạo này, thuộc tính mauSac của nó sẽ tự động được gán giá trị là “Đỏ”.

Cách gọi và sử dụng hàm khởi tạo
Bạn không thể gọi một hàm khởi tạo trực tiếp như gọi một phương thức thông thường. Thay vào đó, hàm khởi tạo được gọi một cách ngầm định khi bạn sử dụng từ khóa new để tạo một instance (thể hiện) mới của lớp.
Cú pháp để tạo đối tượng và gọi hàm khởi tạo như sau:
// TênLớp tenDoiTuong = new HamKhoiTao();
Hãy xem cách chúng ta sử dụng hàm khởi tạo của lớp XeHoi đã định nghĩa ở trên:
public class Main {
public static void main(String[] args) {
// Tạo một đối tượng XeHoi bằng cách gọi hàm khởi tạo
XeHoi xeCuaToi = new XeHoi();
// In ra màu sắc của xe để kiểm tra
System.out.println("Màu sắc của xe là: " + xeCuaToi.mauSac);
// Kết quả: Màu sắc của xe là: Đỏ
}
}
Như bạn thấy, dòng new XeHoi() chính là lúc hàm khởi tạo XeHoi() được thực thi, và thuộc tính mauSac được thiết lập. Tùy vào việc bạn định nghĩa hàm khởi tạo có tham số hay không, cách gọi với new cũng sẽ thay đổi tương ứng.
Phân loại các loại hàm khởi tạo trong Java
Trong Java, có hai loại hàm khởi tạo chính mà bạn sẽ thường xuyên sử dụng. Việc hiểu rõ và lựa chọn đúng loại sẽ giúp mã nguồn của bạn trở nên linh hoạt và rõ ràng hơn.

Constructor mặc định (Default Constructor)
Constructor mặc định là loại hàm khởi tạo không có bất kỳ tham số nào. Nó có vai trò đặc biệt quan trọng vì nếu bạn không tự mình định nghĩa bất kỳ một constructor nào trong lớp, trình biên dịch Java (Java Compiler) sẽ tự động tạo ra một constructor mặc định cho bạn.
Constructor mặc định do trình biên dịch tạo ra sẽ có dạng:
public TenLop() {
// Không có mã lệnh bên trong
// Nó sẽ gọi constructor của lớp cha bằng super()
}
Nó không thực hiện hành động gì đặc biệt ngoài việc gọi constructor của lớp cha (trong trường hợp không kế thừa từ lớp nào cụ thể, nó sẽ gọi constructor của lớp Object). Các biến instance sẽ được khởi tạo với giá trị mặc định (0 cho kiểu số, false cho boolean, và null cho kiểu đối tượng).
Lưu ý quan trọng: Nếu bạn đã định nghĩa bất kỳ một constructor nào (ví dụ: constructor có tham số), trình biên dịch sẽ không tự động tạo constructor mặc định nữa.
Constructor tham số (Parameterized Constructor)
Trái ngược với constructor mặc định, constructor tham số là loại hàm khởi tạo có chứa một hoặc nhiều tham số. Đây là cách mạnh mẽ và phổ biến nhất để khởi tạo đối tượng với các giá trị cụ thể ngay từ đầu.
Việc sử dụng constructor tham số giúp đảm bảo đối tượng được tạo ra luôn ở trạng thái hợp lệ và đầy đủ thông tin, thay vì phải gán giá trị cho từng thuộc tính sau khi tạo.
Hãy xem ví dụ với lớp MayTinh:
public class MayTinh {
String cpu;
int ram;
// Constructor tham số
public MayTinh(String cpu, int ram) {
this.cpu = cpu;
this.ram = ram;
}
void hienThiThongTin() {
System.out.println("CPU: " + this.cpu + ", RAM: " + this.ram + "GB");
}
}
Để sử dụng constructor này, bạn cần truyền các đối số tương ứng khi dùng từ khóa new:
public class Main {
public static void main(String[] args) {
// Tạo đối tượng bằng constructor tham số
MayTinh gamingPC = new MayTinh("Intel Core i9", 32);
gamingPC.hienThiThongTin(); // Kết quả: CPU: Intel Core i9, RAM: 32GB
}
}
Cách làm này giúp mã nguồn trở nên ngắn gọn và minh bạch hơn rất nhiều.
Nguyên tắc hoạt động của hàm khởi tạo trong Java
Để thực sự làm chủ hàm khởi tạo, chúng ta cần nhìn sâu hơn vào cách nó hoạt động “phía sau hậu trường”. Nắm được nguyên tắc này sẽ giúp bạn tránh được những lỗi tiềm ẩn và thiết kế lớp hiệu quả hơn.
Quá trình thực thi hàm khởi tạo
Hàm khởi tạo được gọi tự động tại thời điểm một đối tượng được tạo ra bằng từ khóa new. Quá trình này diễn ra theo một trình tự rất cụ thể, đặc biệt là trong các cấu trúc kế thừa.
Khi bạn tạo một đối tượng của lớp con, hàm khởi tạo của lớp cha sẽ được gọi trước, sau đó mới đến hàm khởi tạo của lớp con. Đây được gọi là chuỗi gọi hàm khởi tạo (constructor chaining). Việc này đảm bảo rằng phần “cốt lõi” của đối tượng (kế thừa từ lớp cha) được xây dựng hoàn chỉnh trước khi các phần “mở rộng” (của lớp con) được thêm vào.
Nếu bạn không chỉ định rõ hàm khởi tạo nào của lớp cha cần gọi bằng từ khóa super(), Java sẽ tự động thêm một lệnh gọi super() không tham số vào dòng đầu tiên của hàm khởi tạo ở lớp con. Điều này đảm bảo chuỗi khởi tạo luôn được duy trì.

Những lưu ý quan trọng khi thiết kế hàm khởi tạo
Khi làm việc với hàm khởi tạo, có một vài quy tắc và kỹ thuật bạn cần ghi nhớ để mã nguồn luôn sạch sẽ và hiệu quả.
1. Không được gọi trực tiếp: Như đã đề cập, bạn không thể gọi một hàm khởi tạo bằng cách viết tenDoiTuong.HamKhoiTao(). Nó chỉ được gọi thông qua từ khóa new.
2. Sử dụng this() để gọi hàm khởi tạo khác trong cùng lớp: Đôi khi, bạn có nhiều hàm khởi tạo trong cùng một lớp và muốn tái sử dụng mã lệnh. Từ khóa this() cho phép một hàm khởi tạo gọi đến một hàm khởi tạo khác trong cùng lớp đó. Lệnh gọi this() phải là câu lệnh đầu tiên trong hàm khởi tạo.
Ví dụ:
public class SanPham {
String ten;
double gia;
// Constructor mặc định gọi đến constructor tham số
public SanPham() {
this("Sản phẩm mặc định", 0.0); // Gọi constructor bên dưới
}
// Constructor tham số
public SanPham(String ten, double gia) {
this.ten = ten;
this.gia = gia;
}
}
Kỹ thuật này giúp giảm thiểu việc lặp lại mã (code duplication) và giữ cho logic khởi tạo được tập trung tại một nơi.
Ví dụ minh họa cụ thể về việc sử dụng hàm khởi tạo trong Java
Lý thuyết sẽ trở nên dễ hiểu hơn rất nhiều khi được áp dụng vào một ví dụ thực tế. Hãy cùng xây dựng lớp SinhVien để thấy rõ cách hoạt động của cả constructor mặc định và constructor tham số.

Ví dụ hàm khởi tạo mặc định và tham số trong lớp SinhVien
Chúng ta sẽ tạo một lớp SinhVien với các thuộc tính cơ bản như mã sinh viên, họ tên và điểm trung bình. Lớp này sẽ có hai hàm khởi tạo: một mặc định và một có tham số.
Đây là mã nguồn của lớp SinhVien:
public class SinhVien {
private String maSV;
private String hoTen;
private double diemTB;
// 1. Constructor mặc định (không tham số)
public SinhVien() {
this.maSV = "000";
this.hoTen = "Chưa nhập tên";
this.diemTB = 0.0;
System.out.println("Đối tượng SinhVien đã được tạo bằng constructor mặc định.");
}
// 2. Constructor tham số
public SinhVien(String maSV, String hoTen, double diemTB) {
this.maSV = maSV;
this.hoTen = hoTen;
this.diemTB = diemTB;
System.out.println("Đối tượng SinhVien đã được tạo bằng constructor tham số.");
}
// Phương thức để hiển thị thông tin
public void hienThiThongTin() {
System.out.println("----------");
System.out.println("Mã SV: " + maSV);
System.out.println("Họ tên: " + hoTen);
System.out.println("Điểm TB: " + diemTB);
System.out.println("----------");
}
}
Bây giờ, hãy tạo một lớp Main để sử dụng lớp SinhVien này:
public class Main {
public static void main(String[] args) {
// Sử dụng constructor mặc định
System.out.println("Tạo sinh viên 1:");
SinhVien sv1 = new SinhVien();
sv1.hienThiThongTin();
// Sử dụng constructor tham số
System.out.println("\nTạo sinh viên 2:");
SinhVien sv2 = new SinhVien("SV001", "Bùi Mạnh Đức", 8.5);
sv2.hienThiThongTin();
}
}
Phân tích kết quả và ý nghĩa của ví dụ
Khi bạn chạy lớp Main, kết quả trên màn hình console sẽ như sau:
Tạo sinh viên 1:
Đối tượng SinhVien đã được tạo bằng constructor mặc định.
———-
Mã SV: 000
Họ tên: Chưa nhập tên
Điểm TB: 0.0
———-
Tạo sinh viên 2:
Đối tượng SinhVien đã được tạo bằng constructor tham số.
———-
Mã SV: SV001
Họ tên: Bùi Mạnh Đức
Điểm TB: 8.5
———-
Phân tích:
- Đối tượng
sv1: Khi dòng lệnh new SinhVien() được thực thi, Java đã gọi đến constructor mặc định (không có tham số). Bên trong constructor này, các thuộc tính maSV, hoTen, diemTB được gán các giá trị khởi đầu là “000”, “Chưa nhập tên”, và 0.0. Điều này hữu ích khi bạn muốn tạo một đối tượng “nháp” và sẽ cập nhật thông tin sau.
- Đối tượng
sv2: Khi dòng lệnh new SinhVien("SV001", "Bùi Mạnh Đức", 8.5) được thực thi, Java tìm và gọi đến constructor tham số. Các giá trị bạn truyền vào (“SV001”, “Bùi Mạnh Đức”, 8.5) được dùng để khởi tạo các thuộc tính tương ứng ngay lập tức. Cách này đảm bảo đối tượng sv2 có đầy đủ và chính xác thông tin ngay từ lúc tạo ra.
Ví dụ này cho thấy sự linh hoạt của việc sử dụng nhiều hàm khởi tạo, cho phép bạn tạo đối tượng theo nhiều cách khác nhau tùy vào ngữ cảnh và nhu cầu.
Ứng dụng và vai trò của hàm khởi tạo trong lập trình hướng đối tượng
Hàm khởi tạo không chỉ là một quy tắc cú pháp, nó là một công cụ thiết kế mạnh mẽ, đóng vai trò nền tảng cho các nguyên lý cốt lõi của lập trình hướng đối tượng (OOP là gì).

Tạo đối tượng với trạng thái ban đầu đúng chuẩn
Đây là ứng dụng trực tiếp và quan trọng nhất. Trong OOP, một đối tượng không chỉ là một tập hợp dữ liệu mà còn đại diện cho một thực thể có trạng thái và hành vi. Hàm khởi tạo đảm bảo rằng không có đối tượng nào được sinh ra trong một trạng thái vô nghĩa hoặc không hợp lệ.
Hãy tưởng tượng một lớp KetNoiDatabase. Sẽ rất nguy hiểm nếu một đối tượng của lớp này được tạo ra mà không có chuỗi kết nối (connection string) hoặc thông tin xác thực. Bằng cách sử dụng một constructor tham số yêu cầu các thông tin này, bạn có thể ép buộc rằng mọi đối tượng KetNoiDatabase đều phải được cấu hình đúng ngay từ đầu, giảm thiểu rủi ro lỗi trong quá trình chạy.
Việc này giúp thực thi nguyên lý đóng gói (Encapsulation) một cách mạnh mẽ, vì lớp sẽ tự kiểm soát trạng thái nội tại của nó và không cho phép tạo ra các đối tượng không hoàn chỉnh.
Hỗ trợ tính kế thừa và tính đa hình trong OOP
Trong kế thừa, hàm khởi tạo đóng vai trò then chốt trong việc xây dựng chuỗi đối tượng từ lớp cha đến lớp con. Khi một đối tượng của lớp con được tạo, constructor của lớp cha luôn được gọi trước tiên. Điều này đảm bảo rằng các thuộc tính và trạng thái kế thừa từ lớp cha được thiết lập đúng cách trước khi lớp con thêm vào các đặc tính riêng của nó.
Từ khóa super() trong constructor của lớp con chính là cầu nối để gọi đến constructor của lớp cha. Bạn có thể sử dụng super() để lựa chọn và truyền tham số cho constructor phù hợp của lớp cha, giúp tùy chỉnh quá trình khởi tạo trong một hệ thống phân cấp phức tạp.
Ví dụ, một lớp QuanLy kế thừa từ NhanVien. Constructor của QuanLy sẽ dùng super() để gọi constructor của NhanVien nhằm khởi tạo các thông tin cơ bản như tên, mã nhân viên, sau đó mới tự mình khởi tạo thêm các thuộc tính riêng như danhSachNhanVienQuanLy. Nếu không có cơ chế này, tính toàn vẹn của đối tượng trong kế thừa sẽ bị phá vỡ.
Các vấn đề thường gặp và cách khắc phục
Dù khái niệm khá đơn giản, lập trình viên, đặc biệt là người mới, vẫn thường gặp phải một số lỗi phổ biến liên quan đến hàm khởi tạo. Hiểu rõ các lỗi này sẽ giúp bạn gỡ rối nhanh hơn.

Lỗi không có constructor mặc định khi có constructor tham số
Đây có lẽ là lỗi phổ biến nhất. Nhiều người cho rằng Java luôn tự tạo constructor mặc định. Điều này chỉ đúng khi bạn không định nghĩa bất kỳ constructor nào.
Nguyên nhân: Ngay khi bạn viết một constructor có tham số cho lớp của mình, trình biên dịch Java sẽ cho rằng bạn muốn kiểm soát hoàn toàn việc khởi tạo và sẽ không tự động thêm constructor mặc định nữa.
public class User {
String username;
// Chỉ có constructor tham số
public User(String username) {
this.username = username;
}
}
// Lỗi sẽ xảy ra ở đây!
User newUser = new User(); // Compile Error: constructor User in class User cannot be applied to given types.
Cách khắc phục: Nếu bạn vẫn muốn có khả năng tạo đối tượng mà không cần truyền tham số, bạn phải tự mình khai báo một constructor mặc định một cách tường minh.
public class User {
String username;
// Khai báo thủ công constructor mặc định
public User() {
this.username = "Guest";
}
// Constructor tham số
public User(String username) {
this.username = username;
}
}
// Giờ thì mã này hoàn toàn hợp lệ
User guestUser = new User();
User namedUser = new User("manhduc");
Gọi nhầm hoặc thiếu constructor trong kế thừa
Khi làm việc với kế thừa, việc gọi constructor của lớp cha là bắt buộc. Java sẽ tự động chèn một lệnh gọi super() không tham số nếu bạn không làm.
Nguyên nhân: Vấn đề phát sinh khi lớp cha không có constructor mặc định (không tham số). Ví dụ, lớp cha chỉ có constructor tham số.
class Animal {
String name;
// Lớp cha chỉ có constructor tham số
Animal(String name) {
this.name = name;
}
}
class Dog extends Animal {
// Lỗi! Trình biên dịch sẽ cố chèn super() nhưng không tìm thấy Animal()
Dog() {
System.out.println("Creating a dog...");
}
}
Cách khắc phục: Bạn phải gọi một cách tường minh constructor của lớp cha bằng cách sử dụng super() và truyền vào các tham số cần thiết. Lệnh gọi super() phải là câu lệnh đầu tiên trong constructor của lớp con.
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Dog extends Animal {
Dog(String name) {
// Gọi tường minh constructor của lớp cha
super(name);
System.out.println("Creating a dog named " + name);
}
}
// Hợp lệ
Dog myDog = new Dog("Buddy");
Best Practices khi sử dụng hàm khởi tạo trong Java
Viết mã không chỉ là làm cho nó chạy được, mà còn là làm cho nó dễ đọc, dễ bảo trì và dễ mở rộng. Áp dụng các “best practices” sau đây sẽ giúp bạn sử dụng hàm khởi tạo một cách chuyên nghiệp.

- Luôn định nghĩa constructor phù hợp: Thay vì phụ thuộc vào constructor mặc định của trình biên dịch, hãy chủ động định nghĩa các constructor phản ánh đúng nhu cầu khởi tạo đối tượng của bạn. Điều này làm cho ý đồ của lớp trở nên rõ ràng hơn.
- Ưu tiên constructor tham số: Sử dụng constructor tham số để tạo ra các đối tượng đã ở trạng thái hợp lệ ngay từ đầu. Việc này an toàn hơn là tạo đối tượng “rỗng” rồi mới dùng các phương thức setter để gán giá trị, vì có thể quên một bước nào đó.
- Giữ constructor đơn giản: Tránh đặt các logic phức tạp, tốn nhiều thời gian (như gọi API, kết nối cơ sở dữ liệu) bên trong constructor. Chức năng chính của nó là khởi tạo trạng thái (gán giá trị cho các biến). Nếu cần các tác vụ nặng, hãy tạo một phương thức riêng (ví dụ:
init(), connect()) để gọi sau khi đối tượng đã được tạo.
- Sử dụng
this() để tái sử dụng constructor: Nếu bạn có nhiều constructor, hãy dùng this() để gọi chồng chéo lẫn nhau. Điều này giúp tránh lặp lại mã và tập trung logic khởi tạo chính vào một constructor tham số đầy đủ nhất. Đây là một cách tuyệt vời để tuân thủ nguyên tắc DRY (Don’t Repeat Yourself).
- Khai báo constructor mặc định nếu có kế hoạch mở rộng: Nếu bạn đang viết một lớp mà bạn dự đoán nó có thể sẽ được kế thừa bởi các lớp khác, hãy cân nhắc cung cấp một constructor mặc định (
public hoặc protected). Điều này giúp các lớp con dễ dàng hơn trong việc khởi tạo và tránh các lỗi liên quan đến super() như đã đề cập.
Kết luận
Hàm khởi tạo (Constructor) là một trong những khái niệm nền tảng nhưng có sức ảnh hưởng sâu rộng nhất trong lập trình hướng đối tượng với Java. Nó không chỉ đơn thuần là cú pháp để tạo đối tượng, mà còn là người “bảo vệ” đảm bảo mỗi đối tượng khi sinh ra đều mang một trạng thái khởi đầu hợp lệ, toàn vẹn và đúng chuẩn. Từ việc phân biệt constructor mặc định và constructor tham số, cho đến việc hiểu rõ cách chúng hoạt động trong chuỗi kế thừa với super() hay tái sử dụng mã với this(), bạn đã có trong tay những công cụ cần thiết để thiết kế các lớp một cách vững chắc và chuyên nghiệp.
Nắm vững hàm khởi tạo chính là nắm vững nghệ thuật “thổi hồn” vào các lớp, biến chúng từ những bản thiết kế trừu tượng thành các đối tượng sống động và đáng tin cậy. Bùi Mạnh Đức khuyến khích bạn hãy bắt tay vào thực hành ngay hôm nay. Hãy thử tạo ra các lớp của riêng mình, thử nghiệm với các loại constructor khác nhau và quan sát cách chúng hoạt động. Từ nền tảng vững chắc này, bạn sẽ tự tin hơn khi khám phá các chủ đề nâng cao hơn như kế thừa, đa hình và quản lý bộ nhớ trong Java.