Bài 2 (Java): Khai báo biến và cách sử dụng biến

2.1. Khái niệm về biến :

    • Java là ngôn ngữ định kiểu tĩnh mạnh mẽ (Strongly Typed). Mọi biến đều phải được khai báo kiểu dữ liệu rõ ràng trước khi sử dụng.

    • Biến là một tên gọi (identifier) dùng để tham chiếu đến một vùng nhớ, nơi lưu trữ dữ liệu trong chương trình. Nói cách khác, biến là “hộp chứa” dữ liệu để ta có thể sử dụng, thay đổi và xử lý trong quá trình chạy chương trình.
  • Tác dụng của biến: Lưu trữ dữ liệu chính xác trong bộ nhớ và xử lý logic các điều kiện, vòng lặp giải thuật.
  • Ý nghĩa của biến: Cung cấp đầy đủ các khối xây dựng cơ bản để lập trình viên giải quyết mọi bài toán thuật toán và nghiệp vụ.

2.2. Các loại biến trong Java

Trong Java, biến là đơn vị lưu trữ cơ bản của chương trình, được xác định bởi định danh (tên biến), kiểu dữ liệu, phạm vi truy cập và vòng đời tồn tại. Dựa vào vị trí khai báo và cách thức cấp phát bộ nhớ, Java phân chia biến thành 3 loại chính:

  • Biến cục bộ (Local Variables)

    • Vị trí khai báo: Được khai báo bên trong thân một phương thức, hàm khởi tạo (constructor) hoặc trong một khối lệnh { ... } (ví dụ: khối if, vòng lặp for).

    • Vòng đời & Phạm vi: Biến cục bộ được khởi tạo khi luồng thực thi đi vào khối lệnh và bị hủy ngay khi khối lệnh đó kết thúc. Biến không thể được truy cập từ bên ngoài khối lệnh đã khai báo nó.

    • Vùng nhớ & Khởi tạo: Lưu trên vùng nhớ Stack. Java không tự động gán giá trị mặc định cho biến cục bộ; lập trình viên bắt buộc phải khởi tạo giá trị cụ thể trước khi sử dụng.

Ví dụ minh hoạ Biến cục bộ  trong Java

public class ViDuBienCucBo {
    public static void main(String[] args) {
        // Biến cục bộ trong hàm main: BẮT BUỘC phải gán giá trị trước khi dùng
        int donGia = 15000;
        int soLuong = 4;
        
        // Biến cục bộ tính thành tiền
        int thanhTien = donGia * soLuong;
        System.out.println("Thành tiền: " + thanhTien);

        // Biến cục bộ bên trong khối lệnh if
        if (thanhTien > 50000) {
            String thongBao = "Được miễn phí ship!"; // Chỉ sống trong khối if này
            System.out.println(thongBao);
        }
        
        // System.out.println(thongBao); // LỖI: Ra ngoài khối if, thongBao đã bị hủy!
    }
}

Ghi nhớ là: Biến khai báo ở đâu chỉ sống trong cặp ngoặc { } ở đó, kết thúc khối lệnh là biến sẽ tự động bị xóa khỏi bộ nhớ.

  • Biến toàn cục của đối tượng (Instance Variables)

    Vị trí khai báo: Được khai báo bên trong khối lớp (class) nhưng nằm ngoài tất cả các phương thức, không có từ khóa static đi kèm.

    • Vòng đời & Phạm vi: Biến ra đời khi đối tượng được tạo lập bằng toán tử new và bị thu hồi khi đối tượng bị trình gom rác (Garbage Collector) tiêu hủy. Mỗi đối tượng (instance) sở hữu một bản sao dữ liệu hoàn toàn độc lập của các biến này.

    • Vùng nhớ & Khởi tạo: Lưu trên vùng nhớ Heap (gắn liền với đối tượng). Nếu không gán giá trị khởi đầu, Java sẽ tự động nạp giá trị mặc định: 0 (với số nguyên), 0.0 (với số thực), false (với boolean) và null (với kiểu tham chiếu).

Ví dụ về biến toàn cục của đối tượng trong Java

class SinhVien {
    // Biến thể hiện: Khai báo trong class, ngoài hàm, không có từ khóa "static"
    // Mỗi sinh viên khi tạo ra sẽ có bản sao riêng của các biến này
    String maSV;
    double diemTB;

    public void inThongTin() {
        System.out.println("Mã: " + maSV + " | Điểm: " + diemTB);
    }
}

public class Main {
    public static void main(String[] args) {
        // Tạo đối tượng sv1 -> sv1 có vùng nhớ riêng cho maSV và diemTB
        SinhVien sv1 = new SinhVien();
        sv1.maSV = "SV01";
        sv1.diemTB = 8.5;

        // Tạo đối tượng sv2 -> sv2 có vùng nhớ riêng biệt hoàn toàn
        SinhVien sv2 = new SinhVien();
        sv2.maSV = "SV02";
        sv2.diemTB = 6.0;

        sv1.inThongTin(); // In: Mã: SV01 | Điểm: 8.5
        sv2.inThongTin(); // In: Mã: SV02 | Điểm: 6.0
    }
}

Ghi nhớ: Biến sinh ra cùng với đối tượng khi dùng new và nằm ở bộ nhớ Heap; mỗi đối tượng sở hữu dữ liệu độc lập, đối tượng này sửa giá trị thì đối tượng kia không bị ảnh hưởng.

  • Biến tĩnh / Biến của lớp (Class / Static Variables)

    Vị trí khai báo: Được khai báo bên trong khối lớp nhưng có từ khóa static đứng phía trước.

    • Vòng đời & Phạm vi: Tồn tại xuyên suốt từ khi lớp được nạp vào bộ nhớ JVM cho đến khi chương trình kết thúc. Biến tĩnh đóng vai trò như một biến dùng chung; tất cả các đối tượng được tạo ra từ lớp đó đều chia sẻ chung một ô nhớ duy nhất thay vì tạo bản sao riêng.

    • Cách truy xuất: Có thể truy xuất trực tiếp thông qua tên lớp mà không cần phải khởi tạo đối tượng: TenLớp.tenBien.

Ví dụ về biến tĩnh / biến của lớp trong Java

class SinhVien {
    // Biến thể hiện: Mỗi sinh viên có tên riêng
    String hoTen;

    // Biến tĩnh: Có từ khóa "static", dùng chung cho TẤT CẢ các đối tượng
    static String tenTruong = "Trung Cấp Nghề Củ Chi";
}

public class Main {
    public static void main(String[] args) {
        // 1. Gọi trực tiếp qua tên Lớp mà KHÔNG cần tạo đối tượng
        System.out.println("Trường: " + SinhVien.tenTruong);

        // 2. Các đối tượng cùng chia sẻ chung 1 ô nhớ duy nhất
        SinhVien sv1 = new SinhVien();
        sv1.hoTen = "Nguyễn Văn An";

        SinhVien sv2 = new SinhVien();
        sv2.hoTen = "Trần Thị Bích";

        // 3. Khi một nơi thay đổi, tất cả đối tượng đều thấy giá trị mới[cite: 2]
        SinhVien.tenTruong = "Trường Đổi Tên Mới";

        System.out.println(sv1.hoTen + " học tại: " + SinhVien.tenTruong);
        System.out.println(sv2.hoTen + " học tại: " + SinhVien.tenTruong);
    }
}

Ghi nhớ: Biến tĩnh có từ khóa static, chỉ tồn tại duy nhất một bản sao trong bộ nhớ và nó được truy xuất thẳng qua cú pháp TenLop.tenBien mà không cần new

2.3. Cách khai báo biến trong Java

2.3.1. Cú pháp cơ bản khai báo biến

Trong Java, mọi biến bắt buộc phải được khai báo kiểu dữ liệu rõ ràng trước khi đưa vào sử dụng (ngôn ngữ định kiểu tĩnh). Cú pháp tổng quát có dạng như sau

type identifier [ = value][, identifier [= value]...];
  • type: Kiểu dữ liệu của biến (có thể là kiểu nguyên thủy như int, double, boolean hoặc kiểu đối tượng/tham chiếu như String, SanPham).

  • identifier: Tên của biến (phải tuân thủ quy tắc đặt tên định danh trong Java).

  • = value: Thành phần khởi tạo giá trị ban đầu (tùy chọn, có thể gán ngay khi khai báo hoặc gán sau).

  • Dấu phẩy ,: Cho phép khai báo đồng thời nhiều biến có cùng kiểu dữ liệu trên cùng một dòng lệnh.

Ví dụ minh họa:

int a, b, c;             // Khai báo 3 biến nguyên a, b, c chưa gán giá trị
int d = 3, e, f = 5;     // Khai báo 3 biến nguyên, gán trước giá trị cho d và f
double pi = 3.14159;     // Khai báo số thực và gán giá trị
char kyTu = 'X';         // Khai báo ký tự
String hoTen = "Nguyen Van A"; // Khai báo biến tham chiếu kiểu chuỗi

2.3.2. Quy tắc đặt tên biến (Identifiers & Naming Conventions)

  • Quy tắc bắt buộc (Compiler rules):Tên biến phải bắt đầu bằng một chữ cái (a–z, A–Z), dấu gạch dưới _ hoặc dấu đô la $.
    • Không được bắt đầu bằng chữ số (ví dụ: 2count là sai cú pháp).

    • Java phân biệt rạch ròi giữa chữ hoa và chữ thường (diemTB khác DiemTB).

    • Tuyệt đối không được trùng với các từ khóa dành riêng của ngôn ngữ Java (như class, public, int, for, static,…).

  • Quy ước chuẩn Java (CamelCase Convention):

    • Tên biến nên viết theo quy tắc lạc đà (lowerCamelCase): Chữ cái đầu tiên viết thường, các từ phía sau viết hoa chữ cái đầu (ví dụ: soLuongTon, tienLuongThang, ngayVaoLam).

    • Tên biến phải mang tính gợi mở, thể hiện đúng bản chất dữ liệu đang lưu giữ (tránh đặt tên vô nghĩa như x, y, temp cho các dữ liệu quan trọng).

2.3.3. Khởi tạo động cho biến (Dynamic Initialization)

Java cho phép khởi tạo giá trị cho một biến một cách linh hoạt tại thời điểm chạy (runtime) thông qua bất kỳ biểu thức, hàm toán học hay phép toán nào hợp lệ:

double a = 3.0, b = 4.0;
// Biến c được khởi tạo động thông qua lời gọi hàm căn bậc hai
double c = Math.sqrt(a * a + b * b); 

2.3.4. Khai báo hằng số với từ khóa final

Khi một giá trị cần cố định và ngăn chặn mọi thao tác chỉnh sửa trong suốt thời gian chạy chương trình, lập trình viên sử dụng từ khóa final đứng trước kiểu dữ liệu:

  • Cú pháp:

    final type CONSTANT_NAME = value;
    
  • Quy ước: Tên hằng số thường được viết hoa toàn bộ và nối các từ bằng dấu gạch dưới _.

  • Ví dụ:

    final double SO_PI = 3.141592653589793;
    final int SO_NGAY_LAM_VIEC_CHUAN = 24;
    // SO_PI = 3.14; // Lỗi biên dịch: Không thể gán lại giá trị cho hằng số fina

2.4. Các kiểu dữ liệu trong Java

2.4.1. Khái quát về kiểu dữ liệu trong Java

Java là một ngôn ngữ định kiểu tĩnh mạnh mẽ (Strongly Typed). Mọi biến, hằng số và biểu thức đều phải được xác định rõ kiểu dữ liệu trước khi biên dịch. Trình biên dịch Java kiểm soát chặt chẽ tính tương thích về kiểu, không tự ý chuyển đổi ngầm định các kiểu xung đột nhằm đảm bảo an toàn bộ nhớ và ngăn ngừa lỗi lúc thực thi (runtime).

Trong Java, hệ thống kiểu dữ liệu được chia làm 2 nhóm chính:

  1. Kiểu dữ liệu nguyên thủy (Primitive Data Types): Lưu trữ giá trị thực tế của dữ liệu trực tiếp trong bộ nhớ.

  2. Kiểu dữ liệu tham chiếu (Reference Data Types): Lưu trữ địa chỉ tham chiếu trỏ đến đối tượng được cấp phát trên bộ nhớ Heap (như String, Mảng, Class, Interface).

2.4.2. Nhóm kiểu dữ liệu nguyên thủy (Primitive Types)

Java định nghĩa 8 kiểu dữ liệu nguyên thủy độc lập nền tảng, có kích thước cố định trên mọi hệ điều hành (đảm bảo tính tương thích “Write Once, Run Anywhere”). 8 kiểu này được gom vào 4 nhóm:

a. Nhóm số nguyên (Integers)

Tất cả kiểu số nguyên trong Java đều là số có dấu (signed) biểu diễn bằng dạng bù hai (two’s complement):

  • byte (8-bit): Miền giá trị từ -128 đến 127. Thích hợp để xử lý luồng dữ liệu thô từ mạng hoặc tập tin.

  • short (16-bit): Miền giá trị từ -32.768 đến 32.767.

  • int (32-bit): Miền giá trị từ -2.147.483.648 đến 2.147.483.647. Đây là kiểu số nguyên mặc định và phổ biến nhất trong tính toán, điều khiển vòng lặp và chỉ số mảng.

  • long (64-bit): Miền giá trị từ -9.223.372.036.854.775.808 đến 9.223.372.036.854.775.807. Dùng khi lưu các con số rất lớn; khi gán hằng số bắt buộc thêm hậu tố L hoặc l (ví dụ: 10000000000L).

b. Nhóm số thực (Floating-Point Numbers)

Biểu diễn các số có phần thập phân theo chuẩn IEEE-754:

  • float (32-bit): Số thực độ chính xác đơn. Khi gán hằng số bắt buộc thêm hậu tố F hoặc f (ví dụ: 5.75f).

  • double (64-bit): Số thực độ chính xác kép, là kiểu số thực mặc định trong Java. Thích hợp cho các phép tính khoa học, hàm toán học (Math.sqrt, Math.sin) hoặc tính toán tài chính cơ bản.

c. Kiểu ký tự (Character)
  • char (16-bit): Dùng để lưu trữ một ký tự Unicode độc lập.

  • Miền giá trị từ 0 đến 65.535 (không có giá trị âm).

  • Ký tự được bao bọc trong cặp dấu nháy đơn ' ' (ví dụ: 'A', '9', '\n').

d. Kiểu logic (Boolean)
  • boolean (1-bit logic): Chỉ nhận một trong hai giá trị hằng số: true hoặc false.

  • Trong Java, true không tương đương số 1, false không tương đương số 0 (không thể ép kiểu qua lại giữa boolean và kiểu số).

2.4.3. Kiểu dữ liệu tham chiếu phổ biến: Lớp chuỗi (String)

Mặc dù không phải là kiểu nguyên thủy, nhưng kiểu chuỗi ký tự được sử dụng thường xuyên như một kiểu dữ liệu cơ bản:

  • Chuỗi trong Java là một đối tượng thuộc lớp java.lang.String (viết hoa chữ S).

  • Giá trị chuỗi được đặt trong cặp nháy kép " ".

  • Chuỗi trong Java có tính chất bất biến (immutable): Giá trị của đối tượng chuỗi một khi đã tạo ra thì không thể bị thay đổi nội dung trên cùng ô nhớ đó.

Ví dụ minh họa về khai báo và sử dụng các kiểu dữ liệu trong Java

package bai1.kieudulieu;

public class DemoKieuDuLieu {
    public static void main(String[] args) {
        // 1. Nhóm số nguyên
        byte tuoi = 18;
        int soLuong = 120;
        long doanhThu = 15000000000L; // Hậu tố L cho kiểu long

        // 2. Nhóm số thực
        float diemThi = 7.5f;         // Hậu tố f cho kiểu float
        double tienLuong = 12500000.75;

        // 3. Nhóm ký tự và logic
        char kyTuXepLoai = 'A';       // Nháy đơn
        boolean quaMon = true;        // true hoặc false

        // 4. Kiểu chuỗi tham chiếu
        String hoTen = "Nguyen Van An"; // Nháy kép

        // In kiểm chứng ra màn hình
        System.out.println("----- THÔNG TIN HỌC VIÊN -----");
        System.out.println("Họ tên: " + hoTen);
        System.out.println("Tuổi: " + tuoi + " | Xếp loại: " + kyTuXepLoai);
        System.out.printf("Điểm: %.1f | Lương thực tập: %,.0f VNĐ\n", diemThi, tienLuong);
        System.out.println("Trạng thái qua môn: " + quaMon);
    }
}

2.5. Bài tập

Bài tập 1: Quản lý hồ sơ nhân sự (Thực hành khai báo các kiểu dữ liệu nguyên thủy và chuỗi)

  • Mục tiêu: Rèn luyện việc lựa chọn đúng các kiểu dữ liệu cơ bản (String, byte/int, double, char, boolean) và in dữ liệu ra console.

  • Yêu cầu:

    • Khai báo và gán giá trị khởi tạo cho các biến đại diện cho một nhân viên:

      • hoTen: Kiểu chuỗi ký tự (String).

      • tuoi: Kiểu số nguyên (byte hoặc int).

      • chieuCao: Chiều cao tính theo mét (kiểu double hoặc float, ví dụ: 1.72).

      • xepLoaiNgoaiNgu: Xếp loại chứng chỉ ngoại ngữ gồm 1 ký tự A, B hoặc C (kiểu char).

      • daKetHon: Tình trạng hôn nhân (kiểu boolean, nhận giá trị true hoặc false).

    • Sử dụng lệnh System.out.println()System.out.printf() để xuất toàn bộ hồ sơ của nhân viên ra màn hình.

Bài tập 2: Tính chu vi, diện tích và tính toán hình học (Sử dụng hằng số final và toán tử số học)

  • Mục tiêu: Thực hành sử dụng từ khóa final để tạo hằng số không thể thay đổi, thực hiện các phép toán số học (+, -, *, /) và hàm toán học cơ bản.

  • Yêu cầu:

    • Khai báo hằng số số Pi: final double SO_PI = 3.14159;.

    • Khai báo biến bán kính hình tròn double banKinh = 5.5;.

    • Tính chu vi và diện tích hình tròn theo công thức:

      • Chu vi = 2 *PI*banKinh

      • Diện tích =PI*banKinh^2 (sử dụng phép nhân hoặc Math.pow())

    • In kết quả ra màn hình console, làm tròn 2 chữ số thập phân bằng cách sử dụng định dạng %.2f trong lệnh System.out.printf().

Bài tập 3: Tính toán hóa đơn bán hàng và ép kiểu dữ liệu (Toán tử số học & Casting)

  • Mục tiêu: Thực hành tính toán biểu thức tiền tệ, phân biệt chia nguyên/chia thực và kỹ thuật ép kiểu dữ liệu (Casting) giữa các kiểu số.

  • Yêu cầu:

    • Khai báo các biến:

      • tenSanPham (chuỗi): ví dụ "Vở kẻ ngang 200 trang".

      • donGia (kiểu double): ví dụ 18500.0.

      • soLuong (kiểu int): ví dụ 12.

      • Hằng số thuế VAT: final double THUE_VAT = 0.08; (8%).

    • Thực hiện các phép tính:

      • TienHang = donGia*soLuong

      • TienThue = TienHang*THUE_VAT

      • TongThanhToan = TienHang + TienThue

    • Thực hiện ép kiểu giá trị TongThanhToan (từ kiểu double) về một biến số nguyên long tongTienLamTron (để in số tiền chẵn không lấy phần thập phân).