Trong lập trình Java, việc nắm vững các khái niệm cơ bản là nền tảng quan trọng. Bài viết này sẽ đi sâu vào các yếu tố cốt lõi như cú pháp chú thích, các kiểu dữ liệu, cơ chế ép kiểu, và cách khai báo cũng như quản lý biến.
1. Chú thích trong Java
Chú thích (comments) là những đoạn văn bản trong mã nguồn mà trình biên dịch Java bỏ qua. Chúng được sử dụng để giải thích mã, giúp lập trình viên khác (hoặc chính bạn trong tương lai) dễ hiểu hơn. Java hỗ trợ ba loại chú thích chính:
- Chú thích một dòng: Bắt đầu bằng
//và kéo dài đến hết dòng. - Chú thích nhiều dòng: Bắt đầu bằng
/*và kết thúc bằng*/. - Chú thích Javadoc: Bắt đầu bằng
/**và kết thúc bằng*/. Loại này được dùng để tạo tài liệu API tự động.
package com.example.basicjava;
public class DemoChuThich {
public static void main(String[] args) {
// Đây là một chú thích một dòng, giải thích về phương thức main
System.out.println("Xin chào Java!"); // Có thể đặt chú thích cuối dòng mã
/*
* Đây là một chú thích nhiều dòng.
* Nó có thể bao gồm nhiều dòng văn bản
* để giải thích các khối mã phức tạp hơn.
*/
/**
* Phương thức này thực hiện một công việc đơn giản là in ra chuỗi chào mừng.
* @param args Các đối số dòng lệnh (nếu có).
*/
int soLuong = 10; // Khai báo và khởi tạo biến số lượng
}
}
2. Các Kiểu Dữ liệu Cơ bản
Java là một ngôn ngữ có kiểu dữ liệu mạnh mẽ, yêu cầu bạn phải khai báo kiểu dữ liệu cho mọi biến. Có hai loại kiểu dữ liệu chính: kiểu nguyên thủy (primitive types) và kiểu tham chiếu (reference types).
2.1. Kiểu Dữ liệu Nguyên thủy (Primitive Types)
Java cung cấp tám kiểu dữ liệu nguyên thủy, được chia thành bốn nhóm:
- Số nguyên:
byte(1 byte),short(2 bytes),int(4 bytes),long(8 bytes). Hậu tố 'L' được dùng cho giá trịlongđể tránh nhầm lẫn vớiint. - Số thực (Dấu phẩy động):
float(4 bytes),double(8 bytes). Hậu tố 'F' hoặc 'f' bắt buộc cho giá trịfloat.doublelà kiểu mặc định cho số thực. - Ký tự:
char(2 bytes), lưu trữ một ký tự Unicode. - Boolean:
boolean(1 bit), lưu trữ giá trịtruehoặcfalse.
package com.example.basicjava;
public class DemoKieuDuLieu {
public static void main(String[] args) {
// Kiểu số nguyên
int maSanPham = 12345; // Kiểu int là phổ biến nhất
byte tuoi = 30; // Giới hạn nhỏ hơn, tiết kiệm bộ nhớ
short soLuongTonKho = 500; // Phù hợp cho số lượng vừa phải
long tongDoanhThu = 9876543210L; // Bắt buộc có 'L' cho giá trị long
// Kiểu số thực (dấu phẩy động)
float giaBan = 25000.50F; // Bắt buộc có 'F' cho giá trị float
double tyLePhanTram = 0.7589123; // Kiểu double mặc định và chính xác hơn
// Kiểu ký tự
char chuCaiDauTien = 'V'; // Lưu trữ một ký tự đơn
char kyTuUnicode = 'ệ'; // Hỗ trợ ký tự Unicode
// Kiểu boolean
boolean sanPhamConHang = true; // Lưu trữ giá trị đúng/sai
boolean ketThucGiaoDich = false;
System.out.println("Mã sản phẩm: " + maSanPham);
System.out.println("Tuổi: " + tuoi);
System.out.println("Giá bán: " + giaBan);
System.out.println("Sản phẩm còn hàng: " + sanPhamConHang);
}
}
2.2. Kiểu Dữ liệu Mở rộng và Lưu ý
Có một số khía cạnh quan trọng khác liên quan đến kiểu dữ liệu:
- Biểu diễn số nguyên: Java cho phép biểu diễn số nguyên dưới dạng hệ thập phân (mặc định), bát phân (tiền tố
0), thập lục phân (tiền tố0x), và nhị phân (tiền tố0b). - Vấn đề với số thực: Kiểu
floatvàdoublecó thể gặp vấn đề về độ chính xác do cách chúng biểu diễn số. Vì vậy, KHÔNG NÊN sử dụng chúng để so sánh trực tiếp hoặc trong các tính toán tài chính. Thay vào đó, hãy dùng lớpBigDecimal. - Ký tự là số: Mọi ký tự trong Java về bản chất được lưu trữ dưới dạng một số nguyên (giá trị Unicode).
- So sánh chuỗi: Kiểu
Stringkhông phải là kiểu nguyên thủy mà là một lớp. Khi so sánh chuỗi, sử dụng phương thức.equals()thay vì toán tử==, đặc biệt khi so sánh các đối tượngStringđược tạo bằngnew String(). Toán tử==sẽ so sánh địa chỉ bộ nhớ, trong khi.equals()so sánh nội dung.
package com.example.basicjava;
import java.math.BigDecimal; // Import BigDecimal cho tính toán chính xác
public class DemoKieuDuLieuMoRong {
public static void main(String[] args) {
// Biểu diễn số nguyên
int soThapPhan = 20; // Hệ thập phân
int soBatPhan = 024; // Hệ bát phân (tương đương 20 thập phân)
int soThapLucPhan = 0x14; // Hệ thập lục phân (tương đương 20 thập phân)
int soNhiPhan = 0b10100; // Hệ nhị phân (tương đương 20 thập phân)
System.out.println("Thập phân: " + soThapPhan);
System.out.println("Bát phân: " + soBatPhan);
System.out.println("Thập lục phân: " + soThapLucPhan);
System.out.println("Nhị phân: " + soNhiPhan);
System.out.println("-----------------------------------");
// Vấn đề với số thực (float và double)
float giaTriF = 0.1f;
double giaTriD = 1.0 / 10.0;
System.out.println("0.1f == (1.0/10.0)? " + (giaTriF == giaTriD)); // Thường trả về false
// Sử dụng BigDecimal cho tính toán chính xác
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal tong = bd1.add(bd2);
System.out.println("0.1 + 0.2 (BigDecimal) = " + tong); // Kết quả chính xác: 0.3
System.out.println("-----------------------------------");
// Ký tự và giá trị Unicode
char chuCaiA = 'a';
char kyTuViet = 'đ';
System.out.println("Ký tự 'a': " + chuCaiA + ", giá trị Unicode: " + (int)chuCaiA);
System.out.println("Ký tự 'đ': " + kyTuViet + ", giá trị Unicode: " + (int)kyTuViet);
System.out.println("-----------------------------------");
// So sánh chuỗi
String str1 = new String("chao mung");
String str2 = new String("chao mung");
String str3 = "lap trinh";
String str4 = "lap trinh";
System.out.println("str1 == str2 (địa chỉ bộ nhớ)? " + (str1 == str2)); // false
System.out.println("str1.equals(str2) (nội dung)? " + str1.equals(str2)); // true
System.out.println("str3 == str4 (hằng chuỗi)? " + (str3 == str4)); // true (do String Pool)
}
}
3. Ép kiểu Dữ liệu (Type Casting)
Ép kiểu là quá trình chuyển đổi một kiểu dữ liệu sang kiểu dữ liệu khác. Java có hai loại ép kiểu:
- Ép kiểu tự động (Widening Conversion / Implicit Casting): Xảy ra khi chuyển đổi từ một kiểu dữ liệu có phạm vi nhỏ hơn sang một kiểu dữ liệu có phạm vi lớn hơn. Điều này an toàn và không làm mất dữ liệu.
- Ép kiểu tường minh (Narrowing Conversion / Explicit Casting): Xảy ra khi chuyển đổi từ một kiểu dữ liệu có phạm vi lớn hơn sang một kiểu dữ liệu có phạm vi nhỏ hơn. Điều này có thể gây mất dữ liệu hoặc tràn số (overflow) và yêu cầu cú pháp
(kiểu_dữ_liệu_đích) biểu_thức.
Các lưu ý quan trọng khi ép kiểu:
- Không thể ép kiểu giữa kiểu
booleanvà các kiểu số. - Không thể ép kiểu giữa các đối tượng không liên quan.
- Khi ép kiểu từ số thực sang số nguyên, phần thập phân sẽ bị cắt bỏ (truncation).
- Cẩn thận với tràn số khi ép kiểu từ kiểu lớn sang kiểu nhỏ.
package com.example.basicjava;
public class DemoEpKieu {
public static void main(String[] args) {
// Ép kiểu tự động (từ nhỏ sang lớn)
int soNguyenNho = 150;
double soThucLon = soNguyenNho; // int tự động chuyển thành double
System.out.println("int sang double: " + soThucLon); // Kết quả: 150.0
// Ép kiểu tường minh (từ lớn sang nhỏ) và vấn đề tràn số
int giaTriInt = 200; // Giá trị byte max là 127
byte giaTriByte = (byte) giaTriInt; // Ép kiểu từ int sang byte
System.out.println("int 200 ép kiểu sang byte: " + giaTriByte); // Kết quả: -56 (tràn số)
double soThuc = 99.87;
int soNguyenTuThuc = (int) soThuc; // Ép kiểu từ double sang int (cắt bỏ phần thập phân)
System.out.println("double 99.87 ép kiểu sang int: " + soNguyenTuThuc); // Kết quả: 99
// Ép kiểu trong biểu thức: tránh tràn số khi tính toán
int soLuongHang = 1_000_000_000; // Một tỷ
int soNam = 5;
// int tongSoLuong = soLuongHang * soNam; // Có thể gây tràn số vì kết quả vượt quá giới hạn int
long tongSoLuongChinhXac = (long) soLuongHang * soNam; // Ép kiểu một toán hạng sang long trước khi nhân
System.out.println("Tổng số lượng chính xác: " + tongSoLuongChinhXac); // Kết quả: 5000000000
}
}
4. Biến và Hằng số trong Java
Biến là nơi lưu trữ dữ liệu trong bộ nhớ máy tính. Mỗi biến có một kiểu dữ liệu và một tên. Hằng số là biến có giá trị không thay đổi sau khi được khởi tạo.
4.1. Quy tắc Đặt tên (Naming Conventions)
Việc đặt tên theo quy ước giúp mã nguồn dễ đọc và dễ bảo trì hơn:
- Biến và Phương thức: Bắt đầu bằng chữ thường, theo quy tắc camelCase (
tenSanPham,tinhTong). - Lớp và Interface: Bắt đầu bằng chữ hoa, theo quy tắc PascalCase (
TenLop,GiaoDien). - Hằng số: Tất cả chữ cái in hoa, các từ phân tách bằng dấu gạch dưới (
MAX_VALUE,PI_NUMBER). - Gói (Package): Tất cả chữ thường (
com.example.utilities).
4.2. Phạm vi Biến (Variable Scope)
Phạm vi xác định nơi một biến có thể được truy cập và tồn tại:
- Biến cục bộ (Local Variables):
- Khai báo bên trong một phương thức, khối mã, hoặc hàm tạo.
- Chỉ có thể được truy cập trong khối mã mà chúng được định nghĩa.
- Phải được khởi tạo trước khi sử dụng.
- Biến thành viên (Instance Variables / Non-static Fields):
- Khai báo bên trong lớp nhưng bên ngoài bất kỳ phương thức nào.
- Thuộc về một đối tượng cụ thể của lớp.
- Được cấp phát bộ nhớ khi một đối tượng được tạo.
- Có giá trị mặc định nếu không được khởi tạo (0 cho số,
falsecho boolean,nullcho kiểu tham chiếu).
- Biến lớp (Class Variables / Static Fields):
- Khai báo với từ khóa
staticbên trong lớp. - Thuộc về lớp chứ không phải bất kỳ đối tượng nào.
- Tất cả các đối tượng của lớp đều chia sẻ cùng một bản sao của biến này.
- Được cấp phát bộ nhớ khi lớp được nạp. Cũng có giá trị mặc định.
- Khai báo với từ khóa
4.3. Hằng số (Constants)
Sử dụng từ khóa final để khai báo một hằng số. Một khi đã được gán giá trị, giá trị đó không thể thay đổi. Thường được kết hợp với static để tạo ra hằng số cấp lớp, có thể truy cập mà không cần tạo đối tượng.
package com.example.basicjava;
public class DemoBienVaHangSo {
// Biến thành viên (Instance variables) - thuộc về đối tượng
String tenNhanVien;
int maSo;
boolean dangHoatDong; // Mặc định là false
// Biến lớp (Class variables / Static variables) - thuộc về lớp
static String tenCongTy = "Cong Ty ABC";
static double tyGiaVAT = 0.10; // Mặc định là 0.0
// Hằng số (Constants) - sử dụng 'final static'
public final static double PI = 3.14159; // Giá trị không thay đổi
public static final int SO_NGAY_TRONG_TUAN = 7;
public static void main(String[] args) {
// Biến cục bộ (Local variables) - phải được khởi tạo
int luongThang = 15000000; // Khởi tạo biến cục bộ
String chucVu = "Kỹ sư phần mềm";
System.out.println("Lương tháng: " + luongThang);
System.out.println("Chức vụ: " + chucVu);
// Truy cập biến thành viên thông qua đối tượng
DemoBienVaHangSo nhanVien1 = new DemoBienVaHangSo();
nhanVien1.tenNhanVien = "Nguyen Van A";
nhanVien1.maSo = 1001;
nhanVien1.dangHoatDong = true;
System.out.println("Tên nhân viên 1: " + nhanVien1.tenNhanVien);
System.out.println("Tình trạng hoạt động: " + nhanVien1.dangHoatDong); // In ra true
DemoBienVaHangSo nhanVien2 = new DemoBienVaHangSo();
System.out.println("Tên nhân viên 2 (chưa khởi tạo): " + nhanVien2.tenNhanVien); // Mặc định là null
System.out.println("Mã số nhân viên 2 (chưa khởi tạo): " + nhanVien2.maSo); // Mặc định là 0
System.out.println("Tình trạng hoạt động nhân viên 2 (chưa khởi tạo): " + nhanVien2.dangHoatDong); // Mặc định là false
System.out.println("-----------------------------------");
// Truy cập biến lớp trực tiếp qua tên lớp
System.out.println("Tên công ty: " + DemoBienVaHangSo.tenCongTy);
System.out.println("Tỷ giá VAT: " + DemoBienVaHangSo.tyGiaVAT);
// Truy cập hằng số trực tiếp qua tên lớp
System.out.println("Hằng số PI: " + DemoBienVaHangSo.PI);
System.out.println("Số ngày trong tuần: " + SO_NGAY_TRONG_TUAN); // Có thể bỏ DemoBienVaHangSo. vì trong cùng lớp
}
public void phuongThucKhac() {
// luongThang không thể truy cập ở đây vì nó là biến cục bộ của main
// System.out.println(luongThang); // Lỗi biên dịch
System.out.println("Truy cập biến lớp trong phương thức khác: " + tenCongTy);
// Biến thành viên 'tenNhanVien' cần một đối tượng để truy cập.
// Ví dụ: DemoBienVaHangSo obj = new DemoBienVaHangSo(); System.out.println(obj.tenNhanVien);
}
}