Thực thi điều kiện

Bài viết giới thiệu về các lệnh thực thi điều kiện trong Java

Giới thiệu về thực thi điều kiện

Mã lệnh đơn giản chạy từ câu lệnh đầu tiên đến câu lệnh cuối cùng mà không đổi hướng thực sự không thể làm được gì nhiều. Để trở nên hữu ích, chương trình của bạn cần đưa ra các quyết định, và có thể hành động khác đi trong những tình huống khác nhau. Giống như bất cứ một ngôn ngữ hữu dụng nào, Java cung cấp cho bạn những công cụ để làm điều đó, dưới dạng các câu lệnh và toán tử. Phần này sẽ đề cập đến các yếu tố sẵn có ấy khi bạn viết mã lệnh Java.

Các toán tử quan hệ và toán tử điều kiện

Java cung cấp cho bạn một vài toán tử và câu lệnh điều khiển luồng để cho phép bạn ra quyết định trong mã lệnh của mình. Thông thường, một quyết định lựa chọn trong mã lệnh bắt đầu bằng một biểu thức logic (là biểu thức được đánh giá bằng hai giá trị đúng hoặc sai – true/false). Những biểu thức đó sử dụng các toán tử quan hệ, chúng so sánh một toán hạng hoặc một biểu thức với một với một toán hạng hay biểu thức khác, và các toán tử điều kiện nữa. Dưới đây là danh sách:

Toán tử

Cách sử dụng

Trả về true nếu...

>

a > b

a lớn hơn b

>=

a >= b

a lớn hơn hay bằng b

<

a < b

a nhỏ hơn b

<=

a <= b

a nhỏ hơn hay bằng b

==

a == b

a bằng b

! =

a != b

a không bằng b

&&

a && b

a và b cả hai đều true, tính b có điều kiện

||

a || b

a hoặc b là true, tính b có điều kiện

!

! a

a là false

&

a & b

a và b cả hai là true, luôn luôn tính b

|

a | b

a hoặc b là true, luôn luôn tính b

^

a ^ b

a và b là khác nhau (true nếu a là true và b là false,

 

Thực thi điều kiện với lệnh if

Giờ ta phải dùng những toán tử đó. Hãy thêm một vài biểu thức logic đơn giản vào phương thức walk() của chúng ta:

public String walk(int steps) {

          if (steps > 100)

                   return "I can't walk that far at once";   

         progress = progress + steps;

          return "Just took " + steps + " steps.";

}

Bây giờ logic hoạt động trong phương thức này sẽ kiểm tra xem số lượng steps lớn như thế nào. Nếu quá lớn, phương thức sẽ ngay lập tức trả về và thông báo rằng thế là quá xa. Mỗi phương thức có thể trả về chỉ một lần. Nhưng bạn thấy, có phải có hai giá trị trả về ở đây đúng không? Đúng, nhưng chỉ có một lần trả về được thực thi mà thôi. Điều kiện if của Java có khuôn dạng như sau:

if (  boolean expression ) {

           statements to execute if true...

} [else {

           statements to execute if false...

}]

 

Cặp ngoặc nhọn không bắt buộc nếu chỉ có một lệnh đơn sau từ khóa if hay sau từ khóa else, đó là lý do vì sao mã lệnh của chúng ta không dùng đến cặp ngoặc này. Bạn không bắt buộc phải có mệnh đề else, và trong mã lệnh của ta cũng không có. Chúng ta có thể đặt các mã lệnh còn lại của phương thức trong mệnh đề else nhưng hiệu lực cũng sẽ tương đương và những thứ thêm vào một cách không cần thiết như thế được gọi là gia vị cú pháp vô ích, nó làm giảm tính dễ đọc của mã lệnh.

Phạm vi của biến

Tất cả các biến trong ứng dụng Java đều có một phạm vi (scope), hay là các đặc trưng xác định nơi bạn có thể truy cập biến chỉ bằng tên của nó. Nếu biến nằm trong vùng phạm vi, bạn có thể tương tác với nó bằng tên. Nếu biến nằm ngoài vùng phạm vi thì điều này là không thể.

Có nhiều mức phạm vi trong ngôn ngữ Java, được xác định bởi vị trí khai báo của biến ở đâu (Lưu ý: không phải tất cả đều là chính thức, theo như tôi biết, nhưng chúng thường là những cái tên mà người lập trình vẫn dùng).

public class SomeClass {

           member variable scope  

          public void someMethod(  parameters ) {

                    method parameter scope

                   local variable declaration(s)

                    local scope

                   someStatementWithACodeBlock {

                              block scope

                   }

          }

}

Phạm vi của một biến trải rộng cho đến cuối đoạn (hoặc cuối khối) mã lệnh mà nó được khai báo trong đó. Ví dụ, trong phương thức walk(), chúng ta tham chiếu đến tham số steps bằng cái tên đơn giản của nó, vì nó nằm trong phạm vi. Ở ngoài phương thức này, khi nói đến steps thì trình biên dịch sẽ báo lỗi. Mã lệnh cũng có thể gọi đến các biến đã được khai báo trong phạm vi rộng hơn mã đó. Ví dụ, chúng ta được phép tham chiếu đến biến cá thể progress bên trong phương thức walk().

Các dạng khác của lệnh if

Chúng ta có thể tạo ra một câu lệnh kiểm tra điều kiện đẹp hơn bằng cách viết lệnh if dưới dạng khác:

if (  boolean expression ) {

           statements to execute if true...

} else if (  boolean expression ) {

           statements to execute if false...

} else if (  boolean expression ) {

           statements to execute if false...

} else {

           default statements to execute...

}

Phương thức của chúng ta có thể giống như sau:

if (steps > 100)

          return "I can't walk that far at once";

else if (steps > 50)

          return "That's almost too far";

else {

         progress = progress + steps;

          return "Just took " + steps + " steps.";

}

Có một dạng viết tắt của lệnh if trông hơi xấu, nhưng cùng đạt được mục đích, mặc dù dạng viết tắt này không cho phép có nhiều câu lệnh cả trong phần if lẫn trong phần else. Dó là dùng toán tử tam nguyên ?:. (Toán tử tam nguyên là toán tử có ba toán hạng). Chúng ta có thể viết lại câu lệnh if đơn giản theo cách sau:

return (steps > 100) ? "I can't walk that far at once" : "Just took " + steps + " steps.";

Tuy nhiên, câu lệnh này không đạt được mục đích vì khi steps nhỏ hơn 100, chúng ta muốn trả về một thông điệp và đồng thời muốn cập nhật biến progress. Bởi vậy trong trường hợp này, sử dụng toán tử tắt ?: không phải là một lựa chọn vì chúng ta không thể thực thi nhiều câu lệnh với dạng viết tắt này.

Lệnh switch

Lệnh if chỉ là một trong số các câu lệnh cho phép bạn kiểm tra điều kiện trong mã lệnh. Một câu lệnh khác bạn rất có thể đã gặp là lệnh switch. Nó đánh giá một biểu thức số nguyên, sau đó thực thi một hay nhiều câu lệnh dựa trên giá trị của biểu thức này. Cú pháp của lệnh như sau:

switch ( integer expression ) {

          case 1:

                    statement(s)

                   [break;]

          case 2:

                    statement(s)

                   [break;]

          case n:

                    statement(s)

                   [break;]

          [default:

                    statement(s)

                   break;]

}

JRE đánh giá biểu thức số nguyên, chọn ra trường hợp áp dụng, sau đó thực thi câu lệnh của trường hợp này. Câu lệnh cuối cùng của mỗi trường hợp ngoại trừ trường hợp cuối là break;. Nó là “lệnh thoát” của switch, và điều khiển sẽ tiếp tục xử lý dòng tiếp theo trong mã lệnh sau câu lệnh switch. Về mặt kỹ thuật, không cần có lệnh break;. Câu lệnh break cuối cùng lại càng đặc biệt không cần thiết vì dù gì thì điều khiển cũng tự thoát khỏi câu lệnh. Nhưng cách làm tốt là cứ thêm chúng vào. Nếu bạn không thêm lệnh break; ở cuối mỗi trường hợp, việc thực thi chương trình sẽ rơi vào trường hợp kế tiếp và tiếp tục chạy, cho đến khi gặp một câu lệnh break; hoặc đến khi kết thúc câu lệnh switch. Trường hợp default sẽ được thực hiện nếu giá trị số nguyên không kích hoạt bất cứ trường hợp nào khác. Điều này không bắt buộc.

Về bản chất, câu lệnh switch thực sự là câu lệnh if-else if với điều kiện của lệnh if là số nguyên; nếu điều kiện của bạn dựa trên một giá trị số nguyên đơn lẻ, bạn có thể dùng hoặc là lệnh switch hoặc là lệnh if-else. Vậy chúng ta có thể viết lại điều kiện if của chúng ta trong phương thức walk dưới dạng câu lệnh switch được không? Câu trả lời là không vì chúng ta đã kiểm tra một biểu thức logic ( steps > 100). Câu lệnh switch không cho phép kiểm tra như thế.

Ví dụ về câu lệnh switch

Đây là một ví dụ thông thường về việc sử dụng câu lệnh switch (nó là một ví dụ khá cổ điển):

int month = 3;

switch (month) {

          case 1: System.out.println("January"); break;

          case 2: System.out.println("February"); break;

          case 3: System.out.println("March"); break;

          case 4: System.out.println("April"); break;

          case 5: System.out.println("May"); break;

          case 6: System.out.println("June"); break;

          case 7: System.out.println("July"); break;

          case 8: System.out.println("August"); break;

          case 9: System.out.println("September"); break;

          case 10: System.out.println("October"); break;

          case 11: System.out.println("November"); break;

          case 12: System.out.println("December"); break;

          default:  System.out.println("That's not a valid month number."); break;

}

month là một biến nguyên biểu thị số hiệu của tháng. Vì là số nguyên nên câu lệnh switch ở đây là hợp lệ. Với mỗi trường hợp hợp lệ, chúng ta in ra tên tháng, sau đó thoát khỏi câu lệnh. Trường hợp mặc định quản lý các số nằm ngoài phạm vi hợp lệ của các tháng.

Cuối cùng, đây là một ví dụ về việc dùng nhiều trường hợp thông nhau có thể sẽ là một mẹo nhỏ thú vị:

int month = 3;

switch (month) {

  case 2:

  case 3:

  case 9: System.out.println("My family has someone with a birthday in this month."); break;

|-------10--------20--------30--------40--------50--------60--------70--------80--------9|

|-------- XML error:  The previous line is longer than the max of 90 characters ---------|

  case 1:

  case 4:

  case 5:

  case 6:

  case 7:

  case 8:

  case 10:

  case 11:

  case 12: System.out.println("Nobody in my family has a birthday in this month."); break;

          default: System.out.println("That's not a valid month number."); break;

}

Ở đây ta thấy trường hợp 2, 3 và 9 được xử lý giống nhau; các trường hợp còn lại có một kiểu xử lý khác. Lưu ý rằng các trường hợp này không phải xếp liền theo thứ tự và có nhiều trường hợp thông nhau là những gì chúng ta cần trong tình huống này.

Chạy lại nhiều lần

Đến giờ, chúng ta đã học được nhiều thứ, nhưng vẫn còn những hạn chế. Thỉnh thoảng chúng ta muốn mã lệnh thi hành đi thi hành lại cùng một việc cho đến khi công việc hoàn tất. Ví dụ, giả sử ta muốn đối tượng Adult của chúng ta nói hơn một lần câu “hello”. Điều này khá dễ thực hiện trong mã lệnh Java (mặc dù không dễ thực hiện trong các ngôn ngữ kịch bản lệnh như Groovy chẳng hạn). Java cung cấp cho bạn các cách sau để lặp đi lặp lại mã lệnh, hoặc thực hiện mã lệnh hơn một lần:

  • câu lệnh for
  • câu lệnh do
  • câu lệnh while

Chúng thường được gọi chung là các vòng lặp (loops) (ví dụ, vòng lặp for), vì chúng lặp đi lặp lại cả khối mã lệnh cho đến khi bạn ra lệnh cho chúng dừng lại. Trong các phần tiếp sau, chúng ta sẽ nói ngắn gọn về từng lệnh một và dùng chúng trong phương thức speak() để trò chuyện chút ít.

Vòng lặp for

Cấu trúc lặp cơ bản nhất trong ngôn ngữ Java là câu lệnh for, câu lệnh này cho phép bạn lặp từng bước trên một phạm vi giá trị cho phép xác định số lần thực thi vòng lặp. Cú pháp thường sử dụng nhất của vòng lặp for như sau:

for ( initialization; termination; increment ) {

           statement(s)

}                 

Biểu thức khởi tạo (initialization) xác định vòng lặp bắt đầu từ đâu. Biểu thức dừng (termination) xác định vòng lặp kết thúc ở đâu. Biểu thức tăng (increment) xác định biến khởi tạo sẽ tăng bao nhiêu mỗi lần đi qua vòng lặp. Mỗi lần lặp, vòng lặp sẽ thực hiện các câu lệnh trong khối lệnh, là tập hợp các câu lệnh nằm giữa dấu ngoặc nhọn (hãy nhớ rằng bất kỳ khối nào trong mã lệnh Java cũng được đặt giữa hai dấu ngoặc nhọn, chứ không phải chỉ mã lệnh của vòng lặp for).

Cú pháp và khả năng của vòng lặp for có khác trong Java phiên bản 5.0, do vậy hãy đọc bài viết của John Zukowski về các đặc tính mới, thú vị trong ấn bản mới ra gần đây của ngôn ngữ này.

Sử dụng vòng lặp for

Ta hãy biến đổi phương thức speak() sao cho nó nói từ “hello” ba lần, dùng vòng lặp for. Khi làm việc này, chúng ta sẽ tìm hiểu về một lớp có sẵn của Java, làm việc lắp ghép các chuỗi một cách 'ngon lành':

public String speak() {

          StringBuffer speech = new StringBuffer();

          for (int i = 0; i < 3; i++) {

                   speech.append("hello");

          }

          return speech.toString();

}         

Lớp StringBuffer trong gói java.lang cho phép bạn thao tác các chuỗi dễ dàng và rất tuyệt vời trong việc nối các chuỗi lại với nhau (giống như ta móc chúng lại với nhau). Đơn giản là chúng ta khởi tạo một chuỗi, sau đó gọi phương thức append() mỗi lần muốn thêm một điều gì đó vào lời nói của từng Adult. Vòng lặp for là nơi mọi việc thực sự diễn ra. Trong cặp ngoặc đơn của vòng lặp, chúng ta đã khai báo một biến nguyên i để làm số đếm cho vòng lặp (các ký tự i, j và k thường được dùng làm biến đếm vòng lặp, nhưng bạn có thể đặt bất kỳ tên nào mà bạn muốn cho biến này). Biểu thức tiếp theo cho biết chúng ta sẽ tiếp tục lặp cho đến khi biến này đạt đến một giá trị nhỏ hơn ba. Biểu thức sau cho biết chúng ta sẽ tăng biến đếm lên một sau mỗi lần lặp (hãy nhớ toán tử ++). Mỗi lần đi qua vòng lặp, chúng ta sẽ gọi phương thức append() của speech và dán một từ “hello” khác vào cuối.

Bây giờ, thay phương thức speak() cũ bằng phương thức speak() mới, loại bỏ mọi lệnh println trong main() và thêm một lệnh để gọi phương thức speak() của Adult. Khi bạn thực hiện, lớp có thể giống như sau:

package intro.core;

public class Adult {

          protected int age = 25;

          protected String firstname = "firstname";

          protected String lastname = "lastname";

          protected String race = "inuit";

          protected String gender = "male";

          protected int progress = 0;

          public static void main(String[] args) {

                   Adult myAdult = new Adult();

                   System.out.println(myAdult.speak());

          }

          public int getAge() {

                   return age;

          }

          public void setAge(int anAge) {

                   age = anAge;

          }

          public String getName() {

                   return firstname.concat(" ").concat(lastname);

          }

          public String speak() {

                   StringBuffer speech = new StringBuffer();

                   for (int i = 0; i < 3; i++) {

                             speech.append("hello");

                   }

                   return speech.toString();

          }

          public String walk(int steps) {

                   if (steps > 100)

                             return "I can't walk that far at once";

                   else if (steps > 50)

                             return "That's almost too far";

                   else {

                             progress = progress + steps;

                             return "Just took " + steps + " steps.";

                   }

          }

}

Khi bạn chạy đoạn mã lệnh này, bạn sẽ nhận được kết quả là dòng hellohellohello trên màn hình. Nhưng dùng vòng lặp for chỉ là một cách để làm việc này. Java còn cho bạn hai cấu trúc thay thế khác mà bạn sẽ thấy tiếp theo đây.

Vòng lặp while

Đầu tiên hãy thử vòng lặp while. Phiên bản sau đây của phương thức speak() cho ra cùng một kết quả như cách làm mà bạn thấy ở phần trên:

public String speak() {

          StringBuffer speech = new StringBuffer();

          int i = 0;

          while (i < 3) {

                   speech.append("hello");

                   i++;

          }

          return speech.toString();

}

Cú pháp cơ bản của vòng lặp while như sau:

while ( boolean expression ) {

           statement(s)

}      

Vòng lặp while thực hiện mã lệnh trong khối cho đến khi biểu thức của nó trả lại giá trị là false. Vậy bạn điều khiển vòng lặp như thế nào? Bạn phải đảm bảo là biểu thức sẽ trở thành false tại thời điểm nào đó, nếu không, bạn sẽ có một vòng lặp vô hạn. Trong trường hợp của chúng ta, ta khai báo một biến địa phương là i ở bên ngoài vòng lặp, khởi tạo cho nó giá trị là 0, sau đó kiểm tra giá trị của nó trong biểu thức lặp. Mỗi lần đi qua vòng lặp, chúng ta lại tăng i lên. Khi nó không còn nhỏ hơn 3 nữa, vòng lặp sẽ kết thúc và chúng ta sẽ trả lại xâu ký tự lưu trong bộ đệm.

Ở đây ta thấy vòng for thuận tiện hơn. Khi dùng vòng for, chúng ta khai báo và khởi tạo biến điều khiển, kiểm tra giá trị của nó và tăng giá trị của nó chỉ bằng một dòng mã lệnh. Dùng vòng while đòi hỏi nhiều việc hơn. Nếu chúng ta quên tăng biến đếm, chúng ta sẽ có một vòng lặp vô hạn. Nếu ta không khởi tạo biến đếm, trình biên dịch sẽ nhắc nhở. Nhưng vòng while lại rất tiện lợi nếu bạn phải kiểm tra một biểu thức logic phức tạp (đóng hộp tất cả vào vòng lặp for thú vị ấy sẽ làm cho nó rất khó đọc).

Bây giờ ta đã thấy vòng lặp for và while, nhưng phần tiếp theo sẽ minh họa cho ta thấy còn một cách thứ ba nữa.

Vòng lặp do

Đoạn mã lệnh sau đây sẽ thực thi chính xác những điều mà ta thấy ở hai vòng lặp trên:

public String speak() {

          StringBuffer speech = new StringBuffer();

          int i = 0;

          do {

                   speech.append("hello");

                   i++;

          } while (i < 3) ;

          return speech.toString();

}

Cú pháp cơ bản của vòng lặp do như sau:

do {

           statement(s)

} while ( boolean expression ) ;

Vòng lặp do gần như giống hệt vòng lặp while, ngoại trừ việc nó kiểm tra biểu thức logic sau khi thực thi khối lệnh lặp. Với vòng lặp while, chuyện gì sẽ xảy ra nếu biểu thức cho giá trị false ngay lần đầu tiên kiểm tra? Vòng lặp sẽ không thực hiện dù chỉ một lần. Còn với vòng lặp do, bạn sẽ được đảm bảo là vòng lặp sẽ thực hiện ít nhất 1 lần. Sự khác biệt này sẽ có ích trong nhiều trường hợp.

Trước khi tạm biệt các vòng lặp, hãy xem lại hai câu lệnh rẽ nhánh. Chúng ta đã thấy lệnh break khi ta nói đến câu lệnh switch. Nó cũng có hiệu quả tương tự trong vòng lặp: nó dừng vòng lặp. Mặt khác, lệnh continue giúp dùng ngay lần lặp hiện tại và chuyển tới lần lặp tiếp theo. Đây là một ví dụ thường thấy:

for (int i = 0; i < 3; i++) {

          if (i < 2) {

                   System.out.println("Haven't hit 2 yet...");

                   continue;

          }


          if (i == 2) {

                   System.out.println("Hit 2...");

                   break;

          }

}

Nếu bạn đưa đoạn mã này vào trong phương thức main() và chạy, bạn sẽ nhận được kết quả như sau:

Haven't hit 2 yet...

Haven't hit 2 yet...

Hit 2...

Hai lần đầu đi qua vòng lặp, i đều nhỏ hơn 2, do vậy chúng ta in ra dòng chữ “Haven't hit 2 yet...”, sau đó thực hiện lệnh continue, lệnh này chuyển luôn đến lần lặp tiếp theo. Khi i bằng 2, khối mã lệnh trong phần lệnh if đầu tiên không được thi hành. Chúng ta nhảy đến lệnh if thứ hai, in ra dòng chữ "Hit 2...", sau đó lệnh break thoát khỏi vòng lặp.

Trong phần tiếp theo, chúng ta sẽ tăng thêm sự phong phú của các hành vi có thể bổ sung thêm thông qua việc trình bày về việc xử lý các bộ sưu tập (Collections).

Theo dõi http://laptrinhtot.com để tiếp tục theo dõi các loạt bài mới nhất về Java nhé!

Tags: