Câu lệnh điều kiện trong Java

Bài viết được sự cho phép của tác giả Nhựt Danh

Câu lệnh điều kiện là một phần kiến thức của câu lệnh điều khiển luồng (control flow). Cũng bởi vì kiến thức về câu lệnh điều khiển luồng này hơi nhiều và quan trọng, nên mình tách chúng ra làm hai nhóm. Nhóm thứ nhất bao gồm các câu lệnh điều kiện (hay còn gọi là các câu lệnh rẽ nhánh) mà bạn sẽ làm quen hôm nay. Nhóm còn lại là câu lệnh lặp bạn sẽ được làm quen ở bài sau.

Đầu tiên chúng ta nói về khái niệm chung của hai nhóm, khái niệm câu lệnh điều khiển luồng là gì nhé.

Khái Niệm Câu Lệnh Điều Khiển Luồng

Để dễ hiểu khái niệm này nhất, thì bạn hãy nhớ lại việc code của mình ở các bài học trước xem nào (mặc dù chúng ta chưa code nhiều lắm). Các bạn có thể thấy khi bạn code, và các dòng code đó được IDE thực thi, chúng sẽ được trình biên dịch này đọc và thực hiện một cách tuyến tính từ trên xuống đúng không nào, từ dòng số 1 đến dòng cuối cùng.

Nhưng thực tế không phải lúc nào chúng ta cũng xây dựng một ứng dụng với logic đơn giản như vậy. Các project thực tế đều cần các giải thuật phức tạp hơn, chẳng hạn như cần truy xuất vào cơ sở dữ liệu và in ra console từng thông tin của sinh viên. Thì khi đó việc thực hiện tuyến tính từng dòng code sẽ vô cùng phức tạp, bạn phải viết hàng ngàn dòng code cho việc đọc tuần tự hàng ngàn sinh viên trong cơ sở dữ liệu. Chưa hết nếu với mỗi sinh viên được đọc lên có một số điều kiện nào đó, như chỉ in ra số sinh viên có giới tính nữ, thì việc code và thực thi tuyến tính thật sự là một cơn ác mộng.

Chính vì vậy mà các câu lệnh điều khiển luồng được các ngôn ngữ cho ra đời, nhằm tạo ra một luồng thực thi mới, đó có thể là một luồng lặp, hay luồng rẽ nhánh, sao cho chúng có thể hướng trình biên dịch thực thi một đoạn code nào đó nhiều lần, hoặc bỏ qua không thực thi đoạn code nào đó,… Như đã nói thì bài hôm nay bạn làm quen với nhóm đầu tiên trong câu lệnh điều khiển luồng, đó là nhóm các câu lệnh điều kiện giúp rẽ nhánh luồng.

Trước khi vào làm quen đến các câu lệnh, mình xin bắt đầu nói rõ về hai ký hiệu “thần thánh” mà từ bài đầu tiên bạn đã gặp, hai ký hiệu này giúp ích rất nhiều cho bài học hôm nay và cả việc code của các bạn sau này, đó là ký hiệu { và }. Cặp ngoặc nhọn này giúp tạo thành một khối lệnh (hay còn gọi là block).

  4 tips học Java cơ bản nhanh nhất dành cho Beginner Developer

  Ép Kiểu & Comment Source Code trong Java

Khái Niệm Khối Lệnh (Block)

Như bạn vừa biết thì khối lệnh trong Java được biểu thị bằng cặp dấu ngoặc nhọn ({ và }).

Ngược lại quá khứ quay về các bài trước, bạn sẽ thấy cặp ngoặc này đã xuất hiện trong khai báo class (bạn sẽ học đến class ở các bài viết về OOP sau). Trong trường hợp này cặp ngoặc đã tạo ra một khối lệnh đóng vai trò bao lấy code và cho biết tất cả các code bên trong đó đều là các code của class. Khi đó, chúng (các code trong cặp ngoặc đó) phải tuân theo các nguyên tắc của class (bạn sẽ biết các nguyên tắc này sau). Mọi dòng code nằm ngoài cặp ngoặc nhọn này sẽ không thuộc quyền quản lý của class đó. Cặp ngoặc nhọn mà mình nói đến xuất hiện như hình sau.

Khối Lệnh (Block)
Minh họa cặp ngoặc nhọn bao lấy code của class

Hay cặp ngoặc nhọn xuất hiện ở khai báo phương thức (bạn cũng sẽ học đến phương thức ở bài sau), giúp tạo ra một khối lệnh đóng vai trò bao lấy code cho biết tất cả các code bên trong đó đều là code của phương thức đó. Cũng như trên, mọi dòng code nằm ngoài cặp ngoặc nhọn của phương thức này sẽ nằm ngoài xử lý logic của phương thức đó. Cặp ngoặc nhọn phương thức xuất hiện như sau.

Khối Lệnh (Block)
Minh họa cặp ngoặc nhọn bao lấy code của phương thức

Ngoài các cặp ngoặc nhọn của class và của phương thức ra thì bạn cũng có thể tạo bất cứ khối lệnh nào trong các dòng code của bạn, chỉ cần bao các câu lệnh đó vào một cặp ngoặc nhọn. Việc tạo ra các khối lệnh như thế này có thể giúp cho các dòng code được tổ chức rõ ràng hơn.

Khối Lệnh (Block)
Minh họa cặp ngoặc nhọn bao lấy code của một khối lệnh

Và hiển nhiên khối lệnh còn được áp dụng cho các câu lệnh điều kiện mà chúng ta sẽ làm quen dưới đây nữa. Chính vì vậy mà chúng ta cần làm quen với khối lệnh trước khi đi vào bài học chính thức là vậy.

Nhưng dù cho có sử dụng khối lệnh với mục đích nào đi nữa, thì bạn cũng phải nhớ một điều, là nếu có khai báo dấu { để bắt đầu một khối lệnh, thì phải có dấu } ở đâu đó để đóng khối lệnh lại. Nếu một chương trình mà có tổng số lượng dấu { không bằng với tổng số lượng dấu } sẽ có lỗi xảy ra đấy nhé.

Xem thêm việc làm Tuyển dụng Java hấp dẫn tại TopDev

Phạm Vi Của Biến (Scope)

Chúng ta làm quen với một kiến thức nữa. Vì khi các bạn đã quen với khối lệnh, thì bạn cũng nên biết phạm vi của biến. Vì phạm vi của biến sẽ bị ảnh hưởng rất lớn dựa trên các khối lệnh này.

Chúng ta xác định phạm vi của biến như thế nào? Thực ra mình cũng có đọc nhiều tài liệu về vấn đề này, có nhiều cách để xác định phạm vi, nhưng cách xác định trực quan nhất có lẽ là phân biệt phạm vi của biến dựa trên ảnh hưởng local hay global của nó.

  • Phạm vi local, là phạm vi mà biến đó chỉ ảnh hưởng cục bộ trong một khối lệnh, không thể dùng đến biến đó ở bên ngoài khối lệnh.
  • Phạm vi global, là phạm vi mà biến đó được khai báo ở khối lệnh bên ngoài, khi đó nó có ảnh hưởng đến các khối lệnh bên trong, tức các khối lệnh bên trong có thể dùng được biến global này.

Trong ví dụ dưới đây, bạn có thể thấy là biến name được mình khai báo trong một khối lệnh, nên nó là biến local của khối lệnh đó, bạn không thể dùng lại biến này ở khối lệnh khác (bạn có thể thấy hệ thống báo lỗi như hình dưới). Bạn chỉ có thể dùng được biến tên name này nếu khai báo lại biến ở khối lệnh khác, nhưng lưu ý khi đó hai biến name ở hai khối lệnh khác nhau sẽ chẳng liên quan gì với nhau cả.

Phạm Vi Của Biến (Scope)
Ví dụ biến name là biến local bên trong một khối lệnh

Cũng ví dụ này nhưng mình khai báo biến name ở bên ngoài khối lệnh, khi đó biến name này được xem như biến global của hai khối lệnh con, và vì vậy nó được gọi đến thoải mái mà không bị lỗi.

Phạm Vi Của Biến (Scope)
Ví dụ biến name là biến global của 2 khối lệnh con

Được nước lấn tới, mình tiếp tục ví dụ với biến name được để bên ngoài phương thức main() luôn, khi này nó sẽ được xem là biến global của tất cả các phương thức có trong class này (không riêng gì phương thức main() đâu nhé, và bạn cũng đừng để ý đến khai báo static của biến, khai báo dạng này sẽ được nói đến ở bài này khi bạn học sang OOP).

Phạm Vi Của Biến (Scope)
Ví dụ biến name là biến global các phương thức bên trong class

Hai ví dụ sau cùng trên đây đều cho ra console cùng một kết quả. Bạn cứ code và thực thi thử chương trình để kiểm chứng nhé.

Câu Lệnh if

Đến đây thì chúng ta đã xong kiến thức râu ria rồi, giờ hãy bắt đầu đi vào câu lệnh điều kiện đầu tiên, câu lệnh if. Chỉ với cái tên if thôi nhưng thực chất có tới bốn biến thể của câu lệnh dạng này mà bạn phải nắm, đó là: ifif elseif else if, và ?:. Chúng ta bắt đầu làm quen với từng loại như sau.

if

Cú pháp cho câu lệnh if như sau.

if (biểu_thức_điều_kiện) {
     các_câu_lệnh;
}

Trong đó:

  • biểu_thức_điều_kiện là một biểu thức mà sẽ trả về kết quả là một giá trị boolean.
  • các_câu_lệnh sẽ được thực thi chỉ khi mà biểu_thức_điều_kiện trả về giá trị true mà thôi. Bạn thấy rằng khối lệnh đã được áp dụng để bao lấy các_câu_lệnh.

Ví dụ cho câu lệnh if.

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter your age: ");
int age = scanner.nextInt();

if (age < 18) {
    System.out.printf("You can not access");
}

Mình giải thích một chút ví dụ trên, các bạn thấy biểu_thức_điều_kiện lúc này là age < 18. Nghĩa là nếu biến age mà user nhập từ bàn phím nhỏ hơn 18, thì biểu thức này sẽ trả về true, khi đó trong khối lệnh của câu lệnh if này (dòng in ra console câu thông báo chưa đủ tuổi) sẽ được thực thi. Còn nếu user nhập vào một age lớn hơn 18, sẽ không có chuyện gì xảy ra, ứng dụng kết thúc. Ví dụ này đã bắt đầu dùng đến kiến thức về nhập/xuất trên console rồi đấy nhé.

Một lưu ý nhỏ thôi, là với trường hợp trong khối lệnh của if nếu chỉ có một dòng code như ví dụ trên, nhiều khi người ta bỏ luôn cả dấu { và }, khi đó câu lệnh if trên sẽ như sau.

if (age < 18)
    System.out.printf("You can not access");

Hay thậm chí viết như sau.

if (age < 18) System.out.printf("You can not access");

if else

Cú pháp cho câu lệnh if else như sau.

if (biểu_thức_điều_kiện) {
     các_câu_lệnh_1;
} else {
     các_câu_lệnh_2;
}

Trong đó:

  • biểu_thức_điều_kiện cũng sẽ trả về kết quả là một giá trị boolean.
  • các_câu_lệnh_1 được thực thi trong trường hợp biểu_thức_điều_kiện là true.
  • các_câu_lệnh_2 sẽ được thực thi trong trường hợp biểu_thức_điều_kiện là false.

Ví dụ cho câu lệnh if else.

if (age < 18) {
    System.out.printf("You can not access");
} else {
    System.out.printf("Welcome to our system!");
}

Bạn thấy ví dụ này làm rõ hơn trường hợp user nhập một age lớn hơn 18, khi đó biểu_thức_điều_kiện sẽ trả về kết quả false, và vì vậy các_câu_lệnh_2 sẽ được thực thi, trong ví dụ này là dòng in ra console “Welcome to our system!”.

Cũng bởi khối lệnh của if và else chỉ có một dòng nên bạn có quyền viết thế này.

if (age < 18)
  System.out.printf("You can not access");
else
  System.out.printf("Welcome to our system!");

Hay thế này, nhưng lưu ý code dài quá sẽ khó đọc lắm đấy, mình không khuyến khích viết như vậy.

if (age < 18) System.out.printf("You can not access"); else System.out.printf("Welcome to our system!");

if else if

Cú pháp cho câu lệnh if else if như sau.

if (biểu_thức_điều_kiện_1) {
     các_câu_lệnh_1;
} else if (biểu_thức_điều_kiện_2) {
     các_câu_lệnh_2;
} else if (...) {
     ...
} else if (biểu_thức_điều_kiện_n) {
     các_câu_lệnh_n;
} else {
     các_câu_lệnh_n+1;
}

Đây là dạng mở rộng hơn của câu lệnh if else. Khi đó:

  • các_câu_lệnh_1 được thực thi trong trường hợp biểu_thức_điều_kiện_1 trả về true.
  • các_câu_lệnh_2 được thực thi trong trường hợp biểu_thức_điều_kiện_1 trả về false và biểu_thức_điều_kiện_2 trả về true.
  • các_câu_lệnh_n được thực thi trong trường hợp các biểu_thức_điều_kiện trước nó đều trả về false và biểu_thức_điều_kiện_n trả về true.
  • Nếu không có bất kỳ biểu_thức_điều_kiện nào trả về true cả thì các_câu_lệnh_n+1 sẽ được thực thi.

Ví dụ cho câu lệnh if else if.

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter a number of week (1 is Monday): ");
int day = scanner.nextInt();

if (day == 1) {
    System.out.printf("Monday");
} else if (day == 2) {
    System.out.printf("Tuesday");
} else if (day == 3) {
    System.out.printf("Wednesday");
} else if (day == 4) {
    System.out.printf("Thursday");
} else if (day == 5) {
    System.out.printf("Friday");
} else if (day == 6) {
    System.out.printf("Saturday");
} else if (day == 7) {
    System.out.printf("Sunday");
} else {
    System.out.printf("Invalid number!");
}

?:

Câu lệnh này thực chất không mới, nó như là câu lệnh if else nhưng được biểu diễn ngắn gọn hơn.

Mình thấy nhiều tài liệu gom kiến thức về việc sử dụng ?: này vào bài viết về các Toán tử. Khi đó nó được gọi là toán tử tam nguyên (ternary operator). Vì công dụng của nó được dùng cho mục đích tính toán nhanh giá trị hơn là một câu lệnh giúp điều khiển luồng. Nhưng với mình nó không khác gì if else cả, mình xem nó là một cách viết ngắn gọn hơn của if else nên gộp chung vào mục này. Còn sở dĩ gọi là toán tử tam nguyên là bởi hai ký tự mà bạn nhìn thấy (? và :) giúp tách các thành phần của câu lệnh ra làm ba phần (hay ba toán hạng), các bạn xem cú pháp của nó như sau.

[kết_quả =] biểu_thức_điều_kiện ? câu_lệnh_nếu_true : câu_lệnh_nếu_false;

Trong đó:

  • kết_quả có thể có hoặc không, biến kết_quả này sẽ lưu lại giá trị là kết quả của câu lệnh, nó phải là kiểu dữ liệu của câu_lệnh_nếu_true và câu_lệnh_nếu_false, lý do tại sao thì mời bạn đọc tiếp đoạn sau.
  • biểu_thức_điều_kiện tương tự như ở các câu lệnh if phía trên.
  • câu_lệnh_nếu_true sẽ thực thi khi biểu_thức_điều_kiện trả về true, vế này sẽ trả về một kết quả về cho kết_quả, có thể là kiểu String, hoặc boolean, hoặc int,…
  • Ngược lại câu_lệnh_nếu_false sẽ thực thi khi biểu_thức_điều_kiện trả về false, và vế này cũng sẽ trả kết quả về cho kết_quả.

Ví dụ cho câu lệnh ?: (ví dụ này được viết lại từ ví dụ if else ở trên).

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter your age: ");
int age = scanner.nextInt();

String access = (age < 18) ? "You can not access" : "Welcome to our system!";
System.out.printf(access);

Bạn cũng thấy rằng câu lệnh này chỉ thích hợp thay thế cho if else mà thôi, và thực sự nó giúp chúng ta rút ngắn số dòng code lại, nhưng lại làm cho thuật toán khó đọc hơn đúng không nào. Tùy bạn cân nhắc sử dụng if else hay ?: nhé.

Một chút lưu ý là với code trên, bạn không cần dùng biến kết_quả access mà in trực tiếp ra console từ câu lệnh này luôn cũng được, mình điều chỉnh một tí như sau.

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter your age: ");
int age = scanner.nextInt();

System.out.printf((age < 18) ? "You can not access" : "Welcome to our system!");

Câu Lệnh switch case

Câu lệnh này có thể dùng để thay thế if else if nói trên nếu như các biểu_thức_điều_kiện đều dùng một đối tượng giống nhau để so sánh (ví dụ ở if else if trên đây chúng ta dùng biến day để so sánh đi so sánh lại với các giá trị khác nhau). Khi này bạn nên dùng switch case để giúp cho if else if trông tường minh hơn.

Cú pháp cho câu lệnh switch case như sau:

switch (đối_tượng_so_sánh) {
     case giá_trị_1:
          các_câu_lệnh_1;
          break;
     case giá_trị_2:
          các_câu_lệnh_2;
          break;
     case ...:
          ...;
          break;
     case giá_trị_n:
          các_câu_lệnh_n;
          break;
     default:
          các_câu_lệnh_n+1;
          break;
}

Thay vì so sánh đối tượng ở từng biểu_thức_điều_kiện như ở if else if, bạn chỉ cần truyền nó vào đối_tượng_so_sánh, rồi chỉ định từng giá_trị_x của nó để thực thi kết quả của nó ở các_câu_lệnh_x tương ứng.

Bạn nhớ ở mỗi case đều có kết thúc là câu lệnh đặc biệt break (câu lệnh break này sẽ được nói ở bài sau). Hiện tại, bạn chỉ nên biết là câu lệnh break này giúp bạn dừng việc thực thi ở một khối lệnh của các_câu_lệnh_x nào đó, nếu không có break, hệ thống sẽ đi tiếp qua case tiếp theo để xử lý và như vậy sẽ cho kết quả sai.

Thành phần cuối cùng trong câu lệnh này là từ khóa default, thành phần này giống như else cuối cùng của một if else if, nó biểu thị rằng nếu như các so sánh case không thỏa các giá_trị của đối_tượng_so_sánh, thì các_câu_lệnh_n+1 trong default sẽ được gọi.

Ví dụ cho câu lệnh switch case (ví dụ này được viết lại từ ví dụ if else if ở trên).

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter a number of week (1 is Monday): ");
int day = scanner.nextInt();
  
switch (day) {
case 1:
    System.out.printf("Monday");
    break;
case 2:
    System.out.printf("Tuesday");
    break;
case 3:
    System.out.printf("Wednesday");
    break;
case 4:
    System.out.printf("Thursday");
    break;
case 5:
    System.out.printf("Friday");
    break;
case 6:
    System.out.printf("Saturday");
    break;
case 7:
    System.out.printf("Sunday");
    break;
default:
    System.out.printf("Invalid number!");
    break;
}

Bạn có thấy giống if else if không nào.

Kết Luận

Chúng ta vừa đi qua các câu lệnh điều kiện – Một phần trong câu lệnh điều khiển luồng. Qua đó bạn cũng đã hiểu khối lệnh và phạm vi của biến trong chương trình Java rồi, bạn hãy theo dõi các bài học kế tiếp để nắm rõ hơn ngôn ngữ Java này nhé.

Bài viết gốc được đăng tải tại yellowcodebooks.com

Xem thêm:

Tìm việc làm IT mọi cấp độ tại TopDev