Chương 2: Biến và kiểu

Trở về Mục lục cuốn sách

2.1  Nói thêm về lệnh in

Bạn có thể tùy ý đặt bao nhiêu câu lệnh vào trong  main cũng được; chẳng hạn, để in nhiều dòng:

class Hello { 
  // Generates some simple output. 
  public static void main(String[] args) { 
    System.out.println("Hello, world."); // in một dòng 
    System.out.println("How are you?"); // in dòng nữa 
  } 
}

Như ví dụ này đã cho thấy, bạn có thể đặt lời chú thích ở cuối dòng lệnh, hoặc đặt nó ở riêng một dòng.

Những cụm từ đặt giữa hai dấu nháy kép được gọi là chuỗi, vì chúng được hợp thành từ một dãy (chuỗi) các kí tự. Chuỗi có thể gồm bất kì tổ hợp nào từ các chữ cái, chữ số, dấu câu, và các kí tự đặc biệt khác.

println là tên gọi tắt của “print line,” vì sau mỗi dòng nó thêm vào một kí tự đặc biệt, gọi là newline, để đẩy con trỏ xuống dòng tiếp theo trên màn hình. Lần tới, khi println được gọi, các chữ mới sẽ xuất hiện ở dòng kế tiếp.

Để hiển thị kết quả từ nhiều lệnh in trên cùng một dòng, hãy dùng print:

class Hello { 
  // Phát sinh một số kết quả đơn giản. 
  public static void main(String[] args) { 
    System.out.print("Goodbye, "); 
    System.out.println("cruel world!"); 
  } 
}

Kết quả xuất hiện trên cùng một dòng là Goodbye, cruel world!. Có một dấu cách giữa từ “Goodbye” và dấu nháy kép tiếp theo. Dấu cách này xuất hiện ở kết quả, vì vậy nó ảnh hưởng đến hành vi của chương trình.

Những dấu cách xuất hiện ngoài cặp dấu nháy kép thì nói chung không ảnh hưởng gì đến hành vi của chương trình. Chẳng hạn, tôi đã có thể viết:

class Hello { 
public static void main(String[] args) { 
System.out.print("Goodbye, "); 
System.out.println("cruel world!"); 
} 
}

Chương trình này sẽ biên dịch và chạy được thông suốt như chương trình ban đầu. Dấu ngắt ở cuối dòng (dấu newline) cũng không ảnh hưởng tới hành vi của chương trình, vì vậy tôi cũng có thể đã viết thành:

class Hello { public static void main(String[] args) { 
System.out.print("Goodbye, "); System.out.println 
("cruel world!");}}

Chương trình này cũng hoạt động được, nhưng nó trở nên ngày càng khó đọc. Các dấu ngắt dòng và dấu cách rất có ích trong việc bố trí hình thức của chương trình, làm chương trình dễ đọc và dễ định vị lỗi hơn.

 2.2  Biến

Một trong những tính năng mạnh nhất của một ngôn ngữ lập trình là khả năng thao tác với các biến. Biến là một tên gọi tham chiếu đến một giá trị. Giá trị là những thứ có thể in ra, lưu giữ, và (như ta sẽ thấy sau này) thao tác tính toán được. Các chuỗi mà ta đã in đến giờ ("Hello, World.", "Goodbye, ", v.v.) đều là những giá trị.

Để lưu một giá trị, bạn phải tạo ra một biến. Vì những giá trị ta muốn lưu trữ ở đây là các chuỗi, nên ta khai báo biến mới là một chuỗi

    String bob;

Đây là câu lệnh khai báo, vì nó khai báo rằng biến mang tên bob có kiểu String. Mỗi biến có một kiểu với tác dụng quyết định loại giá trị nào mà biến đó có thể lưu trữ được. Chẳng hạn, kiểu int có thể lưu trữ các số nguyên, còn kiểu String lưu trữ chuỗi.

Một số kiểu có tên gọi bắt đầu bằng chữ in và một số kiểu thì bắt đầu bằng chữ thường. Ta sẽ học ý nghĩa của sự phân biệt này về sau, còn bây giờ chỉ cần lưu ý để viết đúng. Không có kiểu nào gọi là Int hay string, và trình biên dịch sẽ bác bỏ nếu bạn cố gắng dựng nên một cái tên như vậy.

Để tạo nên một biến nguyên, cú pháp là int bob;, trong đó bob là tên gọi tùy ý mà bạn đặt cho biến. Nói chung, bạn sẽ muốn đặt tên biến để chỉ rõ mục tiêu dùng biến đó. Chẳng hạn, nếu thấy các lệnh khai báo biến sau đây:

    String firstName; 
    String lastName; 
    int hour, minute;

thì bạn có thể đoán được rằng những giá trị nào sẽ được lưu vào chúng. Ví dụ này cũng giới thiệu cú pháp để khai báo nhiều biến với cùng kiểu: hour và second đều là số nguyên (kiểu int).

2.3  Lệnh gán

Bây giờ khi đã tạo nên các biến, ta muốn lưu giữ những giá trị. Ta làm điều này với lệnh gán.

    bob = "Hello."; // cho bob giá trị "Hello." 
    hour = 11; // gán giá trị 11 vào hour 
    minute = 59; // đặt minute là 59

Ví dụ này có ba lệnh gán, và các lời chú thích đi kèm cho thấy ba cách khác nhau mà chúng ta đôi khi nói về câu lệnh gán. Cách dùng từ có thể gây nhầm lẫn, song ý tưởng rất đơn giản:

  • Khi khai báo một biến, bạn tạo nên một chỗ lưu dữ liệu được đặt tên.
  • Khi gán cho một biến, bạn cho nó một giá trị.

Một cách thông dụng để biểu diễn biến trên giấy là vẽ một hộp với tên biến ghi bên ngoài và giá trị biến ở beent rong. Hình dưới đây cho thấy hiệu ứng của ba câu lệnh gán này:

Một quy tắc chung là biến phải có cùng kiểu với giá trị mà bạn gán cho nó. Bạn không thể lưu trữ một String vào trong minute hay một số nguyên vào bob.

Mặt khác, quy tắc này cũng có thể gây nhầm lẫn, vì có rất nhiều cách để bạn chuyển giá trị từ kiểu này sang kiểu khác, và đôi khi Java cũng tự động chuyển đổi. Riêng bây giờ thì bạn nên nhớ quy tắc chung, và sau này ta sẽ nói về những ngoại lệ.

Một điều nữa dễ gây nhầm đó là có những chuỗi trông giống như số nguyên nhưng thực ra lại không phải. Chẳng hạn, bob có thể chứa chuỗi "123", vốn được tạo thành từ các kí tự 1, 23, nhưng nó không phải là số 123.

    bob = "123"; // hợp lệ 
    bob = 123; // không hợp lệ

2.4  In các biến

Để hiển thị giá trị của một biến, bạn có thể dùng println hoặc print:

class Hello {
  public static void main(String[] args) {
    String firstLine; 
    firstLine = "Hello, again!"; 
    System.out.println(firstLine); 
  } 
}

Chương trình này tạo ra ba biến firstLine, gán nó với giá trị "Hello, again!" rồi in giá trị đó ra. Khi ta nói “in một giá trị,” điều này nghĩa là in giá trị của biến đó. Để in tên của một biến, bạn phải đặt cái tên này trong cặp dấu nháy kép. Chẳng hạn: System.out.println("firstLine");

Ví dụ như, bạn có thể viết

    String firstLine; firstLine = "Hello, again!"; 
    System.out.print("The value of firstLine is "); 
    System.out.println(firstLine);

Kết quả của chương trình này là

The value of firstLine is Hello, again!

Tôi vui mừng thông báo với bạn rằng cú pháp của lệnh in một biến thì giống nhau bất kể kiểu của biến đó là gì.

    int hour, minute; 
    hour = 11; 
    minute = 59; 
    System.out.print("The current time is "); 
    System.out.print(hour); 
    System.out.print(":"); 
    System.out.print(minute); 
    System.out.println(".");

Kết quả của chương trình này là The current time is 11:59.

CẢNH BÁO: Để đặt nhiều giá trị trên cùng một dòng, cách thông dụng là dùng nhiều lệnh print và tiếp theo là println. Nhưng bạn phải nhớ viết println ở cuối. Trong nhiều môi trường lập trình, kết quả của print chỉ lưu giữ mà không được hiển thị đến tận lúc println được gọi, khi đó cả dòng sẽ xuất hiện cùng lúc. Nếu bạn bỏ mất println, chương trình có thể kết thúc mà không hiển thị kết quả đã được lưu trữ!

2.5  Từ khoá

Cách đây vài mục, tôi đã nói rằng bạn có thể đặt một tên tùy ý cho biến, nhưng điều này không hẳn là đúng. Có những từ nhất định được dành riêng trong Java vì chúng được trình biên dịch sử dụng để phân tách cấu trúc của chương trình mà bạn viết; và nếu bạn dùng những từ này đặt cho tên biến thì trình biên dịch sẽ bị lẫn. Các từ như vậy, gọi là từ khóa, bao gồm có public, class, void, int, và nhiều từ khác.

Bạn có thể xem danh sách đầy đủ tại http://download.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html. Trang này, được Oracle cung cấp, có đăng tài liệu về Java mà trong sách này tôi thường xuyên tham khảo đến.

2.6  Toán tử

Toán tử là các kí hiệu đặc biệt để biểu diễn các phép tính như cộng và nhân. Hầu hết các toán tử của Java đều thực hiện theo đúng dự định của bạn vì chúng là những kí hiệu toán học thông dụng. Chẳng hạn, toán tử của phép cộng là +. Phép trừ là -, phép nhân là *, và phép chia là /.

1+1   hour-1   hour*60 + minute   minute/60

Các biểu thức có thể chứa cả tên biến và con số. Các biến đều được thay bằng giá trị của chúng trước khi phép tính được thực hiện.

Hơn nữa, dù phép trừ và phép nhân làm đúng điều bạn muốn, song phép chia có thể làm bạn ngạc nhiên. Chẳng hạn, chương trình này:

    int hour, minute; 
    hour = 11; 
    minute = 59; 
    System.out.print("Number of minutes since midnight: "); 
    System.out.println(hour*60 + minute); 
    System.out.print("Fraction of the hour that has passed: "); 
    System.out.println(minute/60);

phát sinh ra kết quả này:

Number of minutes since midnight: 719
Fraction of the hour that has passed: 0

Dòng đầu tiên thì đúng như mong đợi, nhưng dòng thứ hai thật kì quặc. Giá trị của minute là 59, và 59 chia cho 60 bằng 0.98333, chứ không phải là 0. Vấn đề ở đây là Java đã thực hiện phép chia nguyên.

Khi cả hai toán hạng đều là số nguyên (toán hạng là những đại lượng mà toán tử thực hiện tính toán), thì kết quả cũng sẽ là một số nguyên, và theo quy định chung, phép chia nguyên làm tròn xuống, ngay cả trong trường hợp này khi giá trị sát với số nguyên phía trên hơn.

Một cách làm khác là đi tính phần trăm thay vì một phân số:

    System.out.print("Percentage of the hour that has passed: "); 
    System.out.println(minute*100/60);

Kết quả là:

Percentage of the hour that has passed: 98

Một lần nữa kết quả lại được làm tròn xuống, nhưng lần này đáp số đã gần đúng hơn. Để được kết quả chính xác hơn, ta có thể dùng một kiểu biến khác, gọi là dấu phẩy động, để lưu trữ những giá trị có phần thập phân. Ta sẽ tiếp tục vấn đề này trong chương sau.

2.7  Thứ tự thực hiện

Khi trong biểu thức có nhiều hơn một toán tử, thứ tự định lượng sẽ tuân theo quy tắc ưu tiên. Giải thích đầy đủ quy tắc ưu tiên này có thể phức tạp, nhưng để bắt đầu, bạn chỉ cần nhớ:

  • Các phép nhân và chia phải được thực hiện trước cộng và trừ. Vì vậy 2*3-1 bằng 5 chứ không phải 4, và  2/3-1 được -1, chứ không phải 1 (hãy nhớ rằng phép chia nguyên 2/3 bằng 0).
  • Nếu các toán tử có cùng độ ưu tiên thì chúng được định lượng từ trái sang phải. Vì vậy, trong biểu thức minute*100/60, phép nhân được thực hiện trước, cho ra 5900/60, và cuối cùng là 98. Nếu các phép toán chạy từ phải qua trái, kết quả đã thành 59*1 tức là 59, điều này là sai.
  • Bất cứ khi nào muốn vượt quy tắc ưu tiên (hay khi bạn không chắc quy tắc này như thế nào), bạn có thể dùng cặp ngoặc đơn. Biểu thức trong cặp ngoặc đơn được thực hiện trước, bởi vậy 2 *(3-1) là 4. Bạn cũng có thể dùng cặp ngoặc đơn để biểu thức trở nên dễ đọc, như với (minute * 100) / 60, ngay cả khi không có nó thì kết quả cũng không đổi.

2.8  Các thao tác với chuỗi

Nói chung, bạn không thể thực hiện các phép toán đối với chuỗi, ngay cả khi chuỗi trông giống như những con số. Vì vậy các biểu thức sau đây đều không hợp lệ:

bob - 1    "Hello"/123    bob * "Hello"

Tiện thể, qua những biểu thức trên bạn có phân biệt được liệu bob là số nguyên hay chuỗi không? Không. Cách duy nhất để biết được kiểu của biển là nhìn vào nơi nó được khai báo.

Điều thú vị là toán tử + tác dụng với chuỗi, nhưng có lẽ không hoạt động theo cách bạn mong đợi. Với các String, toán tử + có nhiệm vụ nối, nghĩa là ghép nối tiếp hai toán hạng với nhau. Bởi vậy "Hello, " + "world." sẽ cho ra chuỗi "Hello, world." còn bob + "ism" thì thêm đuôi ism vào bất kì chữ gì mà bob lưu trữ, cách đặt tên này này thật tiện trong tay những người quen tính bài bác.

2.9  Kết hợp

Đến giờ ta đã xem xét những thành phần của ngôn ngữ lập trình—biến, biểu thức, và câu lệnh—một cách biệt lập, mà chưa nói về cách kết hợp chúng.

Một trong những đặc điểm có ích nhất của ngôn ngữ lập trình là khả năng tập hợp những thành phần nhỏ rồi kết hợp chúng lại. Chẳng hạn, ta biết cách tính nhân và biết dùng lệnh in; như vậy hóa ra là có thể kết hợp chúng lại thành một câu lệnh:

    System.out.println(17 * 3);

Bất kì biểu thức nào có số, chuỗi, và biến đều có thể dùng trong lệnh in. Ta đã thấy một ví dụ:

    System.out.println(hour*60 + minute);

Nhưng bạn cũng có thể đặt biểu thức bất kì ở vế phải của một lệnh gán:

    int percentage; 
    percentage = (minute * 100) / 60;

Ngay bây giờ thì tính năng này xem ra chưa có gì ấn tượng, nhưng ta sẽ thấy những ví dụ mà cách kết hợp này biểu diễn những phép tính phức tạp một cách gọn gàng, ngăn nắp.

CẢNH BÁO: Vế trái của một lệnh gán phải là một tên biến, chứ không phải một biểu thức. Đó là vì vế trái dùng để chỉ định vị trí lưu giữ kết quả. Các biểu thức thì không thể hiện vị trí lưu giữ này, mà chỉ thể hiện giá trị. Vì vậy cách viết sau không hợp lệ: minute+1 = hour;.

2.10  Thuật ngữ

biến:
Tên được tham chiếu đến một giá trị.
giá trị:
Một con số hoặc chuỗi kí tự (hoặc những thứ khác sau này được đặt tên) mà lưu trữ được vào trong một biến. Mỗi giá trị thuộc về một kiểu.
kiểu:
Một tập hợp gồm các giá trị. Kiểu của biến quyết định những giá trị nào có thể lưu trữ trong biến đó. Những kiểu mà ta đã gặp bao gồm kiểu số nguyên (int trong Java) và chuỗi (String trong Java).
từ khoá:
Từ dành riêng cho trình biên dịch để phân tách một chương trình. Bạn không thể dùng những từ khoá như publicclass và void để đặt tên biến.
lệnh khai báo:
Câu lệnh nhằm tạo ra một biến mới và quy định kiểu cho nó.
lệnh gán:
Lệnh để gán một giá trị cho một biến.
biểu thức:
Tổ hợp của các biến, toán tử, và giá trị nhằm biểu diễn một giá trị kết quả duy nhất. Biểu thức cũng có kiểu; kiểu này được quyết định bởi các toán tử và toán hạng.
toán tử:
Kí hiệu dùng để biểu diễn một phép tính đơn nhất như cộng, nhân, hoặc nối chuỗi.
toán hạng:
Một trong những giá trị mà toán tử thực hiện với.
ưu tiên:
Thứ tự mà những biểu thức bao gồm nhiều toán tử và toán hạng được định lượng.
nối:
Ghép nối tiếp hai toán hạng.
kết hợp:
Khả năng ghép những biểu thức và câu lệnh đơn giản thành những biểu thức và câu lệnh phức hợp để biểu diễn gọn gàng các thao tác tính toán.

2.11  Bài tập

Bài tập 1

Nếu đang đọc quyển sách này trên lớp, bạn có thể thích bài tập này: hãy tìm một người bạn để chơi trò “Stump the Chump”:

Bắt đầu từ một chương trình biên dịch và chạy được trơn tru. Từng người một quay mặt đi trong lúc người kia gài một lỗi vào chương trình. Sau đó người thứ nhất quay lại rồi cố tìm và sửa lỗi. Nếu tìm được lỗi mà không cần biên dịch, sẽ được hai điểm; tìm được sau khi biên dịch thì được 1 điểm; và nếu không tìm được thì người kia sẽ được một điểm.

Bài tập 2

  1. Hãy tạo ra một chương trình có tên Date.java. Sao chép hoặc gõ vào một chương trình kiểu như “Hello, World” rồi đảm bảo chắc rằng bạn có thể biên dịch và chạy được chương trình.
  2. Làm theo ví dụ ở Mục 2.4, hãy viết một chương trình để tạo ra các biến daydatemonth và yearday sẽ chứa ngày trong tuần còn date thì chứa ngày trong tháng. Từng biến sẽ có kiểu gì? Hãy gán giá trị vào những biến này để biểu diễn ngày hôm nay.
  3. In giá trị từng biến trên mỗi dòng riêng. Đây là một bước trung gian và rất cần để kiểm tra rằng chương trình còn hoạt động ổn thỏa.
  4. Sửa chương trình để in ra ngày theo dạng chuẩn Hoa Kỳ: Saturday, July 16, 2011.
  5. Sửa lại chương trình lần nữa để kết quả thu được là:
American format:
Saturday, July 16, 2011
European format:
Saturday 16 July, 2011

Mục đích của bài tập này là dùng phép nối chuỗi để hiển thị các giá trị có kiểu khác nhau (int và String), đồng thời thực hành kĩ năng phát triển dần chương trình qua việc mỗi lần chỉ thêm vào một vài câu lệnh.

Bài tập 3

  1. Hãy tạo ra một chương trình mới có tên là Time.java. Từ giờ trở đi, tôi không nhắc bạn bắt đầu bằng việc tạo một chương trình nhỏ nhưng chạy được; song bạn nên làm điều này.
  2. Từ ví dụ ở Mục 2.6, hãy tạo ra các biến có tên hourminute và second, rồi gán cho chúng giá trị biểu diễn gần đúng giờ hiện tại. Dùng cách đếm số 24 giờ, theo đó 2 giờ chiều sẽ ứng với giá trị của hour bằng 14.
  3. Làm cho chương trình tính toán rồi in ra số giây kể từ nửa đêm.
  4. Làm cho chương trình tính toán rồi in ra số giây từ giờ đến hết ngày hôm nay.
  5. Làm cho chương trình tính toán rồi in ra số phần trăm thời gian đã trôi qua trong ngày hôm nay.
  6. Thay đổi các giá trị của hourminute và second để phản ánh thời gian hiện tại (coi như có thời gian trôi qua kể từ lần chạy trước), và kiểm tra để đảm bảo rằng chương trình hoạt động được với nhiều giá trị khác nhau.

Mục đích của bài tập này là vận dụng một số phép toán, và bắt đầu suy nghĩ về những dữ liệu phức hợp như thời gian trong ngày, vốn được biểu diễn bởi nhiều giá trị. Đồng thời, bạn cũng có thể vấp phải nhiều vấn đề khi tính phần trăm với các số int; đây chính là lí do dẫn đến việc dùng số có dấu phẩy động trong chương sau.

GỢI Ý: có thể bạn sẽ cần dùng thêm các biến để lưu giữ tạm thời những giá trị trong quá trình tính toán. Các biến kiểu này, vốn được dùng trong tính toán nhưng không bao giờ được in ra, đôi khi được gọi là biến trung gian hoặc biến tạm.

1 bình luận

Filed under Think Java

1 responses to “Chương 2: Biến và kiểu

  1. Pingback: Think Java: Cách suy nghĩ như nhà khoa học máy tính | Blog của Chiến

Bình luận về bài viết này