1. Cấu trúc bộ nhớ
Mỗi máy tính đều có bộ nhớ truy cập ngẫu nhiên. Nó là gì, có những đặc tính nào và, quan trọng nhất, chúng ta được lợi gì từ đó?
Mỗi chương trình (bao gồm cả các chương trình viết bằng Java) trước khi chạy đều được nạp vào bộ nhớ truy cập ngẫu nhiên. Trong bộ nhớ này có mã chương trình (được bộ xử lý thực thi) và dữ liệu của chương trình (được chính chương trình đưa vào bộ nhớ).
Vậy bộ nhớ truy cập ngẫu nhiên là gì và nó giống với điều gì?
Hãy hình dung Excel 😎. Một trang trong Excel gồm các ô, và mỗi ô có số hiệu duy nhất của nó (A1, A2, ... B1, B2). Biết số hiệu của ô, ta luôn có thể ghi vào đó một giá trị nào đó hoặc lấy giá trị đang lưu ở đó. Bộ nhớ máy tính được tổ chức tương tự.

Chương trình và dữ liệu của nó trong khi chạy được lưu trong bộ nhớ. Toàn bộ bộ nhớ máy tính được biểu diễn dưới dạng các ô nhỏ – byte. Mỗi ô có số hiệu duy nhất – 0, 1, 2, 3, ... (đánh số bắt đầu từ 0). Biết số hiệu ô, chúng ta có thể lưu dữ liệu vào ô đó hoặc lấy ra từ đó. Một số ô chứa mã chương trình – tập lệnh cho bộ xử lý, số khác – dữ liệu của chương trình. Số hiệu ô cũng được gọi là địa chỉ của ô.
Bộ xử lý có thể thực thi các lệnh từ chương trình đã nạp vào bộ nhớ. Hầu như tất cả các lệnh của bộ xử lý đều theo kiểu: lấy dữ liệu từ một số ô → làm gì đó với chúng → đặt kết quả vào các ô khác.
Kết hợp hàng trăm lệnh đơn giản, ta có được các thao tác phức tạp và hữu ích.
Khi trong mã chương trình khai báo một biến, một phần bộ nhớ chưa dùng sẽ được cấp cho nó. Thường là vài byte. Khi khai báo biến, bắt buộc phải chỉ định kiểu thông tin mà chương trình sẽ lưu trong đó: số, văn bản hoặc dữ liệu khác. Bởi nếu không biết kiểu thông tin thì không rõ cần cấp khối bộ nhớ kích thước bao nhiêu cho biến.
Thuở sơ khai của ngành máy tính, các chương trình làm việc đơn thuần với số hiệu ô nhớ, nhưng sau đó để thuận tiện cho lập trình viên, người ta bắt đầu đặt tên cho các ô. Tên biến duy nhất trước hết là để thuận tiện cho lập trình viên: chương trình khi chạy vẫn có thể xử lý chỉ với các số hiệu.
2. Biến trong bộ nhớ
Trong Java có 4 kiểu dữ liệu chính để lưu số nguyên. Đó là byte, short, int và long.
| Kiểu | Kích thước, byte | Nguồn gốc tên gọi |
|---|---|---|
|
|
Byte, vì chiếm một byte bộ nhớ |
|
|
Viết tắt của Short Integer |
|
|
Viết tắt của Integer |
|
|
Viết tắt của Long Integer |
Ngoài ra, trong Java có 2 kiểu số thực — float và double:
| Kiểu | Kích thước, byte | Nguồn gốc tên gọi |
|---|---|---|
|
|
Viết tắt của Floating Point Number |
|
|
Viết tắt của Double Float |
Mỗi lần chương trình đến lệnh tạo biến, một vùng bộ nhớ nhỏ được cấp cho biến (kích thước phụ thuộc vào kiểu biến).
Địa chỉ của biến được coi là địa chỉ của ô đầu tiên trong khối bộ nhớ được cấp cho nó.
Các chương trình Java không được phép truy cập bộ nhớ trực tiếp. Mọi thao tác với bộ nhớ chỉ diễn ra thông qua máy ảo Java.
3. Kiểu String trong bộ nhớ
Kiểu String có thể lưu lượng dữ liệu lớn, vì thế đây không chỉ là kiểu dữ liệu, mà là một đối tượng đầy đủ.
Chính dữ liệu kiểu String (văn bản) được đặt trong một đối tượng chuyên dụng, bộ nhớ được cấp riêng cho nó, và địa chỉ của đối tượng này được đặt trong biến, vốn cũng được cấp bộ nhớ.

- Biến a kiểu int chiếm 4 byte và lưu giá trị 1.
- Biến b kiểu int chiếm 4 byte và lưu giá trị 10,555. Dấu phẩy ở đây không phải là phần thập phân, mà là phân tách hàng nghìn. Phần thập phân được phân tách bằng dấu chấm.
- Biến d kiểu double chiếm 8 byte và lưu giá trị 13.001.
- Biến str kiểu String lưu giá trị D12 — địa chỉ ô đầu tiên của đối tượng chứa văn bản.
Đối tượng kiểu String (chứa văn bản) được lưu bằng một khối bộ nhớ riêng. Địa chỉ của ô đầu tiên của nó được lưu trong biến str.
4. Điều gì xảy ra khi chúng ta gán
Một điều quan trọng nữa — phép gán hoạt động như thế nào. Ví dụ:
int a = 10;
int b = a;
b = 20;
System.out.println(a); // 10
Trong ví dụ này, trong bộ nhớ tạo ra hai ô: một cho a, một cho b. Khi chúng ta viết b = a;, giá trị (10) được sao chép, chứ không phải chính “cái hộp”. Việc thay đổi b không ảnh hưởng gì đến a.
Bây giờ tương tự, nhưng với chuỗi:
String s1 = "Hello";
String s2 = s1;
s2 = s2 + " World";
System.out.println(s1); // "Hello"
System.out.println(s2); // "Hello World"
Ở đây cả hai biến s1 và s2 tham chiếu tới cùng một chuỗi "Hello" cho đến thời điểm thay đổi. Nhưng khi chúng ta thực hiện s2 = s2 + " World";, một chuỗi mới "Hello World" được tạo cho s2, còn s1 vẫn trỏ đến chuỗi cũ "Hello".
5. Vì sao trong lập trình người ta đánh số từ 0
Mọi người thường thắc mắc vì sao trong lập trình hầu như ở khắp nơi lại đếm từ 0. Lý do là có rất nhiều tình huống đếm từ 0 tiện hơn (dù cũng có những trường hợp đếm từ 1 sẽ tiện hơn).
Hãy thử một phép so sánh. Bạn đứng trong một hàng dài ở cửa hàng. Và thu ngân nói rằng tất cả khách hàng, bắt đầu từ người thứ 20, hãy sang quầy số hai. Họ sẽ có số mấy? 20, 21, 22, 23, ... Hợp lý. Còn tính tương đối so với người thứ 20? 0, 1, 2, 3, ...
Khi ta mô tả một nhóm như “số của phần tử đầu tiên” + “số lượng”, thì bên trong nhóm các số thứ tự tương đối sẽ là +0, +1, +2, ...
Trong lập trình cũng vậy. Nếu biến của bạn được cấp 100 byte bộ nhớ và bạn có X – địa chỉ của byte đầu tiên, thì địa chỉ của tất cả các byte sẽ là gì? X+0, X+1, X+2, ..., X+99. Thế là ta đã có nhóm byte với chỉ số 0, 1, 2, 99.
Khi chúng ta nghĩ về địa chỉ tương đối bên trong một khối dữ liệu nào đó, ta luôn nhận được đánh số bắt đầu từ 0. Đây là lý do đầu tiên và phổ biến nhất của việc đếm từ 0.
GO TO FULL VERSION