
Yellow Code Books
JAVA Các Bài Học Cơ Sở
Phiên bản 1.1
Yellow Code Books
Phiên bản 1.1
Trước hết mình xin cảm ơn các bạn đã luôn ủng hộ và theo dõi các bài viết của mình trên Yellow Code Books.
Nói về Yellow Code Books. Đây là một trang blog về lập trình, đặc biệt hướng đến lập trình trên các thiết bị di động, vì đó là sở trường của mình. Yellow Code Books được mình tạo nên với mong muốn lưu trữ lại những kiến thức và kỹ năng lập trình của cá nhân, đồng thời kiêm luôn chức năng chia sẻ kiến thức đó cho tất cả các bạn. Vì như mình có nói trên blog rằng “lĩnh hội kiến thức rồi để đó không mang ra chia sẻ là một tội ác”.
Nói về Java. Chắc các bạn cũng biết, đây là một trong những ngôn ngữ lập trình phổ biến trên thế giới. Java rất dễ học. Vì cấu trúc của nó rõ ràng. Nó kế thừa từ ngôn ngữ lập trình C/C++, nhưng hướng đối tượng hoàn toàn và loại bỏ đi những gì khó khăn mà ngôn ngữ tiền nhiệm này đang mắc phải. Chính vì vậy, việc tiếp cận Java khi bạn còn chưa biết phải học ngôn ngữ lập trình nào cả, theo mình là một điều đúng đắn. Một khi bạn nắm vững Java rồi, bạn hoàn toàn có thể tiếp thu nhanh chóng các ngôn ngữ lập trình khác, hoặc tiến thêm bước nữa để học cách xây dựng các ứng dụng cho hệ điều hành Android.
Thôi, mình không nói nhiều nữa. Tất cả sẽ phụ thuộc vào nỗ lực của bạn bây giờ. Hãy bắt đầu bước qua các trang sau rồi cùng đọc và thực hành lập trình Java với mình nhé.
Nói một tí về cấu trúc của bài học. Quyển ebook này được chia ra làm ba phần: Java - Các Bài Học Cơ Sở, Java - Các Bài Học Hướng Đối Tượng, và Java - Các Bài Học Nâng Cao. Tất cả đều bám theo các bài viết trên blog Yellow Code Books, và được cập nhật phiên bản mới thường xuyên. Bạn nên lưu trữ quyển ebook này trên thiết bị di động, trên máy tính, hay in ra giấy để có thể học tập mọi lúc mọi nơi. Nếu có thắc mắc hay thông tin gì cần bổ sung, thì bạn hãy online, và khi đó có các cách liên lạc hiệu quả sau giúp bạn có được một sự phản hồi nhanh
chóng nhất từ mình.
Một lần nữa, cảm ơn các bạn đã ủng hộ bằng cách đọc, theo dõi, chia sẻ, bình luận và đánh giá 5 sao trên các bài viết của mình. Hi vọng sẽ nhận được nhiều hơn các phản hồi từ phía các bạn.
Chúc tất cả thành công!
Ký tên
Chào mừng các bạn đến với loạt bài học về lập trình ngôn ngữ Java của Yellow Code Books. chúng ta bắt đầu làm quen với chương trình học này thông qua việc tìm hiểu sơ lược về ngôn ngữ Java nhé.
Java được tạo ra bởi James Gosling và cộng sự của ông ở Sun Microsystem vào năm 1991 (sau này Oracle mua lại Sun Microsystem vào năm 2010). Ban đầu ngôn ngữ này có tên là Oak (cây sồi) do bên ngoài công ty lúc bấy giờ có trồng nhiều cây này (các tài liệu khác nói vậy, mình chỉ copy thôi). Oak chính thức được đổi tên thành Java vào năm 1995, chắc do mấy cây sồi bị đốn sạch.
Java là một Ngôn Ngữ Lập Trình Hướng Đối Tượng (OOP). Do đó khi lập trình với ngôn ngữ này bạn sẽ phải làm việc với các lớp (Class). Cú pháp của Java được vay mượn nhiều từ C/C++ nhưng lại có đặc tính hướng đối tượng đơn giản hơn và ít tính năng xử lý cấp thấp hơn, nên việc tiếp cận Java sẽ dễ dàng hơn C/C++, thêm nữa nếu bạn nào đã có nền tảng về C/C++ thì chắc chắn sẽ dễ dàng đón nhận và tiếp cận Java hơn nữa.
Khẩu hiệu nổi tiếng của Java chắc bạn cũng biết, đó là "Viết một lần, Chạy mọi nơi". Viết ở đây là viết code, còn chạy nghĩa là thực thi ứng dụng. Điều này có nghĩa là, phần mềm được viết bằng ngôn ngữ Java có thể chạy được trên mọi nền tảng (platform) khác nhau. Để làm được điều này thì Java đưa ra khái niệm máy ảo JVM (Java Virtual Machine), khi bạn biên dịch một chương trình, thay vì mã nguồn sẽ được dịch trực tiếp ra mã máy như nhiều ngôn ngữ khác, thì với Java mã nguồn đó sẽ được dịch thành mã bytecode, bytecode này sẽ được bạn phân phối đến các thiết bị khác nhau, chính JVM được cài sẵn ở các thiết bị đó sẽ
dịch tiếp bytecode này thành mã máy giúp bạn. Có thể mô tả quá trình biên dịch này bằng sơ đồ sau.
Như mình đã nói ở trên đây, do Java được kế thừa từ C/C++, nên sẽ vẫn giữ được sự đơn giản ở cú pháp so với những gì C/C++ đã đạt được.
Mặt khác Java còn giảm bớt các khái niệm "đau đầu" mà C/C++ đang có, làm cho ngôn ngữ này trở nên đơn giản và dễ sử dụng hơn nữa. Có thể kể đến một vài sự giảm bớt này như là: bỏ đi các câu lệnh Goto, không còn khái niệm Nạp Chồng Toán Tử (Overload Operator), và còn bỏ đi khái niệm Con Trỏ (Pointer), bỏ file Header, bỏ luôn Union, Struct,...
Cũng có nhiều ý kiến xoay quanh hai chữ "hoàn toàn" này, thực tế thì chỉ có các kiểu dữ liệu nguyên thủy của Java như int, long, float,... thì không hướng đối
tượng. Ngoài các kiểu dữ liệu nguyên thủy đó ra thì khi tiếp xúc với Java, bạn luôn luôn phải suy nghĩ và làm việc theo hướng đối tượng. Vậy hướng đối tượng là gì? Bạn sẽ hiểu rõ thôi vì chúng ta sẽ bắt đầu nói đến hướng đối tượng từ bài học số 15.
Như đã nói ở trên, khẩu hiệu của Java là "Viết một lần, Chạy mọi nơi". Điều này đã giúp cho ngôn ngữ Java được độc lập với nền tảng phần cứng. Khi lập trình với Java, bạn sẽ không phải suy nghĩ đến sự tương thích với kiến trúc của từng loại hệ điều hành hay phần cứng, chính JVM sẽ giúp các bạn lo điều này.
Nói Java mạnh mẽ là bởi vì ngôn ngữ này hỗ trợ lập trình viên rất nhiều điều. Đầu tiên, như mình có nhắc đến ở trên, Java có thể chạy trên nhiều nền tảng. Java còn có Bộ Dọn Rác (Garbage Collection) giúp tự động dọn dẹp các đối tượng đã qua sử dụng để giải phóng bộ nhớ, mà với các ngôn ngữ khác, lập trình viên phải thực hiện việc giải phóng này một cách thủ công. Java còn hỗ trợ chạy đa nhiệm (Multithread) rất tốt. Và còn nhiều thứ khác chúng ta sẽ cùng nhau tìm hiểu.
Cũng giống như mình đã định nghĩa bên các bài học Android: Môi Trường Phát Triển là một môi trường mà ở đó nhà Phát Triển Phần Mềm có được những công cụ cần thiết nhất để viết ra một ứng dụng hoàn chỉnh. Vì bài học liên quan đến lập trình Java, do đó chúng ta sẽ tập trung vào tìm hiểu Môi Trường Phát Triển Phần Mềm Java (Java Development Environment) sẽ bao gồm những gì.
Dù cho bạn đang lập trình dựa trên hệ điều hành nào: Windows, Linux hay Mac. Thì bạn đều có thể cài đặt được một Môi Trường Phát Triển Phần Mềm cho Java.
Bộ Công Cụ Phát Triển Cho Java (JDK), bộ công cụ này sẽ cung cấp cho bạn các công cụ cần thiết để biên dịch, thực thi, và có cả môi trường để ứng dụng
Java của bạn có thể chạy lên nữa.
Công Cụ Biên Dịch mà mình muốn nói đến ở đây chính là một IDE (Integrated Development Environment), hay nói cách khác là một công cụ để bạn có thể viết code Java lên đó, công cụ này có thể đủ mạnh để ngoài việc bạn có thể code được, nó còn giúp kiểm tra lỗi cú pháp khi code, giúp liên kết với JDK để tự động biên dịch và thực thi chương trình.
Không giống như bên bài học Android mình chỉ định các lập trình viên sử dụng Android Studio để biên dịch. Với Java các bạn có nhiều chọn lựa hơn, các bạn có thể sử dụng một trong các công cụ sau đây.
Netbeans
Netbeans là một IDE mã nguồn mở, mạnh mẽ, miễn phí. Tuy nhiên công cụ này ngày nay có phần hơi lỗi thời, bạn có thể không cần phải biết đến.
Eclipse
Eclipse cũng là một IDE mã nguồn mở, sự phổ biến của nó không kém gì Netbeans, thậm chí có thời gian Eclipse còn được sử dụng phổ biến hơn. Và tất nhiên công cụ này cũng miễn phí. Nhưng có thể nói rằng dù cho Eclipse một thời rất được cộng đồng Java sử dụng, thì với công cụ InteliJ mới mẻ mà mình sẽ nói đến ở mục dưới đây, có thể sẽ làm cho Eclipse dần bị lỗi thời không kém gì Netbeans.
InteliJ
Dù sinh sau đẻ muộn, nhưng InteliJ nhanh chóng chiếm cảm tình của cộng đồng lập trình Java nhờ vào sự mạnh mẽ và giao diện hiện đại của nó. Đặc biệt từ khi Android Studio chính thức được Google giới thiệu là công cụ lập trình ứng dụng Android chính thống, mà Android Studio lại được xây dựng từ InteliJ, nên IDE này bỗng dưng trở nên rất hot.
Ngoài 3 IDE phổ biến được nói ở trên, còn có nhiều công cụ khác hỗ trợ lập trình Java. Bạn có thể chọn cho mình một IDE, nhưng các bài học trong chương trình của mình sẽ thống nhất chọn Eclipse. Bạn có thể không nhất thiết phải dùng giống mình, bạn có thể xem code của các bài học, nhưng vẫn biên dịch trên một công cụ khác, InteliJ chẳng hạn.
Bạn vừa được làm quen với ngôn ngữ Java, hi vọng bạn sẽ yêu thích ngôn ngữ này và cùng mình đi hết chương trình học. Với việc tiếp cận đầy đủ ngôn ngữ Java, bạn sẽ dễ dàng làm quen với lập trình ứng dụng Android ở các bài học về Android nữa đấy.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Qua bài học trước, thì các bạn đều biết rằng, để có thể code được Java, nhất định cần phải xây dựng một môi trường lập trình cho nó. Và bạn cũng đã biết môi trường lập trình cho Java sẽ phải cần đến những công cụ gì rồi đúng không nào. Vậy thì không cần phải nói nhiều, chúng ta hãy cùng bắt tay vào xây dựng từng công cụ đi thôi.
Tương tự như phần hướng dẫn cài đặt JDK ở bên bài học Android, nếu bạn nào đã làm theo hướng dẫn đó rồi thì có thể bỏ qua phần này mà đến thẳng việc cài đặt Eclipse bên dưới, vì 2 phần hướng dẫn của 2 bài giống nhau.
Còn nếu chưa cài đặt JDK bao giờ thì bạn hãy vào trang của Oracle để download file cài đặt, đường dẫn này đây (hoặc bạn có thể search Google với từ khóa JDK Download). Khi vào link cài đặt rồi thì bạn hãy tìm và chọn JDK Download.
Ngoài ra nếu bạn nào muốn lập trình Java trên Netbeans thì có thể chọn vào biểu tượng Netbeans để download cả Netbeans lẫn JDK mới nhất trong cùng một lần download và cài đặt. Nhưng với bài học này mình chỉ hướng dẫn download JDK, rồi download và cài đặt Eclipse riêng ở bước kế tiếp.
Quay lại bài học, sau khi bạn nhấn chọn JDK Download, bước tiếp theo bạn sẽ
thấy danh sách với nhiều gói cài đặt được chia theo version, bạn nên chọn gói đầu tiên nhất, vì thông thường gói này sẽ mới và ổn định nhất. Bạn nhớ check vào Accept License Agreement và chọn đúng phiên bản JDK dành cho hệ điều hành, như trường hợp của mình thì mình sẽ chọn như hình sau.
Sau khi download JDK về thì bạn tiến hành cài đặt nhé, khi cài đặt xong bạn có thể đọc sơ qua bước nhỏ dưới đây.
Bước này không thực sự quan trọng, bạn không cần thực hiện cũng được, vì PATH sẽ cần thiết chỉ khi bạn dùng dòng lệnh (command line) để code và thực thi ứng dụng. JAVA_HOME càng không quan trọng trong bài học Java này. Nhưng nếu bạn có mong muốn học tiếp Android, và bạn đang dùng hệ điều hành Windows, thì mời bạn xem các bước sau.
Cách thiết lập PATH & JAVA_HOME cho Windows như sau:
1. Đầu tiên bạn phải nhớ đường dẫn lúc nãy cài đặt JDK ở đâu, copy đường dẫn này lại, chẳng hạn như C:\Program Files\Java\jdk1.8.0_101.
2. Với Windows 7, nhấn chuột phải vào My Computer và chọn Properties > Advanced. Với Windows 8 (hoặc mới hơn), vào Control Panel > System > Advanced System Settings.
3. Click vào nút Environment Variables.
4. Bạn chú ý đến khung System variables ở phía dưới cửa sổ, hãy nhấn nút New… bên dưới khung này.
5. Khi hộp thoại New System Variable xuất hiện thì bạn điền thông tin như sau:
+ Ở field Variable name gõ vào JAVA_HOME, nhớ phải gõ chính xác tên nhé bạn
+ Ở field Variable value gõ vào đường dẫn cài đặt JDK mà bạn vừa copy lúc nãy rồi nhấn OK. Bạn có thể xem dữ liệu được điền vào hình dưới.
6. Tiếp theo vẫn ở cửa sổ Environment Variable, cũng ở phía dưới khung System variables của cửa sổ này, bạn cuộn xuống và nhấn chọn vào field có Variable là Path, sau đó nhấn nút Edit….
7. Khi hộp thoại Edit System Variable xuất hiện, bạn vào field Variable value, đừng xóa gì hết nhé, bạn chỉ cần thêm vào cuối field đó một đoạn text sau đây ;%JAVA_HOME%\bin. Bạn có thể tham khảo hình bên dưới.
8. Nhấn OK, OK,… và OK (hoặc Apply Changes) để chấp nhận và đóng các hộp thoại. Bạn đã thành công.
Như đã nói ở bài trước, mình sẽ dùng Eclipse cho các bài học Java.
Eclipse là một IDE đa năng và dễ sử dụng, ngoài mục đích chính là hỗ trợ Java,
công cụ này còn hỗ trợ phát triển cho các ngôn ngữ khác như C/C++, JavaScript, PHP và nhiều ngôn ngữ khác nữa. Có một thời gian các lập trình
viên Android cũng dùng công cụ này để phát triển các ứng dụng Android, thông
qua việc cài đặt thêm plugin Android cho Eclipse, nhưng giờ thì đã có Android Studio rồi. Quay lại việc cài đặt Eclipse, thì trước hết bạn cần download phiên bản Eclipse mới nhất bằng cách vào trang này.
Mình xin phép được nói ngoài lề xíu, mình muốn nói về các phiên bản của Eclipse từ trước tới giờ, vì mình nghĩ cách đặt tên của Eclipse rất là thú vị mà bạn cũng nên biết. Có thể nói Eclipse rất thích đặt tên cho các phiên bản của họ liên quan đến khoa học. Các phiên bản đầu tiên có tên lần lượt là Callisto, Europa và Ganymede, đây đều là tên các mặt trăng của sao Mộc. Phiên bản tiếp theo đó được đặt tên một nhà Thiên văn học nổi tiếng, Galileo. Sau đó Eclipse có 2 phiên bản dường như liên quan đến bầu trời: Helios là Thần Mặt Trời trong thần thoại Hy Lạp, còn Indigo là một trong bảy màu sắc của cầu vồng - Màu chàm. Phiên bản tên Juno tiếp sau đó có thể giải thích ở cả 3 nghĩa: Juno là tên một nữ thần trong thần thoại La Mã, Juno còn là tên của một tiểu hành tinh, cuối cùng Juno là tên của một tàu không gian thám hiểm sao Mộc. Kepler, Luna và Mars cũng liên quan đến Thiên văn học. Và bây giờ là Neon và Oxygen, là các nguyên tố hóa học. Phiên bản các bạn đang chuẩn bị download chính là Oxygen.
Thú vị phải không nào, quay lại với bài học, sau khi đến trang download, bạn click vào nơi giống như hình sau.
Có thể một số bạn sẽ gặp phải màn hình yêu cầu chọn lựa một phiên bản Eclipse phù hợp ở bước này trước khi nhìn thấy nút Download như trên. Trong trường
hợp đó, bạn hãy xem qua bước chọn lựa như dưới đây. Trường hợp của mình thì mình thấy nút download luôn, khi nhấn vào nút này thì một file nén được down về, giải nén file đó sẽ chứa một ứng dụng Eclipse Installer, chính Eclipse Installer này giúp mình chọn lựa phiên bản Eclipse tương ứng như sau.
Bạn nhớ là hãy chọn Eclipse IDE for Java Developers nhé. Sau đó màn hình tiếp theo như dưới đây xuất hiện, các bạn cứ để nội dung Installation Folder ở màn hình này theo mặc định, đó là đường dẫn đến thư mục mà Eclipse sẽ được
cài đặt vào đó. Bạn nhấn INSTALL ở màn hình này.
Màn hình tiếp theo như dưới đây sẽ hiển thị các điều khoản, nếu giỏi tiếng Anh thì bạn cứ việc đọc. Còn không thể đọc hết thì bạn cứ nhấn Accept Now để chấp nhận điều khoản và bắt đầu cài đặt.
Sau một lúc chờ đợi thì việc cài đặt cũng hoàn thành. Lúc này bạn có thể nhấn LAUNCH để chạy ứng dụng.
Lần đầu tiên chạy Eclipse các bạn sẽ nhìn thấy hộp thoại như sau.
Hộp thoại này hỏi chúng ta đường dẫn của Workspace, bạn có thể chấp nhận đường dẫn có sẵn, hoặc tự thiết lập lại một đường dẫn khác. Mục đích của việc đòi một đường dẫn Workspace là giúp Eclipse có thể dùng thư mục này để chứa các ứng dụng của bạn (bạn không cần căng thẳng quá, vì hoàn toàn có thể thay đổi đường dẫn này trong phần cài đặt của Eclipse sau này). Nếu bạn đã chọn cho mình một thư mục Workspace ưng ý và không muốn hộp thoại này hiện lên một lần nào nữa, thì check chọn ô Use this as the default and do not ask again. Sau đó thì bạn nhấn OK.
Lần đầu tiên chạy Eclipse bạn sẽ thấy màn hình Welcome, bạn chỉ việc nhấn vào nút x phía bên phải chữ Welcome để đóng nó đi.
Xin chúc mừng, bạn đã cài đặt thành công các công cụ cần thiết để phát triển ứng dụng Java.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Với việc tìm hiểu về ngôn ngữ và cách thức cài đặt một môi trường lập trình Java từ hai bài trước. Hôm nay chúng ta cùng mở Eclipse lên để bắt đầu làm quen với IDE và với đoạn code Java đầu tiên của bạn. Nếu bạn không thấy ứng dụng Eclipse sau khi cài đặt ở bài trước đâu, thì hãy tìm đến thư mục mà bạn đã chỉ định trước khi cài đặt, bạn sẽ thấy ứng dụng Eclipse của bạn nằm trong đó. Khi tìm thấy Eclipse rồi, thì bạn phải tạo shortcut ngay và luôn nếu bạn đang dùng Windows, còn nếu bạn đang dùng Mac thì có thể "ghim" Eclipse này vào dock cho các lần mở sau, như hình dưới đây.
Sau khi bạn đã mở Eclipse lên rồi thì chúng ta bắt đầu vào bài học số 3 này.
Lần đầu tiên khi mở Eclipse lên, bạn sẽ thấy màn hình chính như sau, các thành phần con của màn hình này được mình đánh các con số cho bạn dễ tiếp cận.
1. Toolbar: đây là thanh công cụ của Eclipse, thanh này chứa các nút điều khiển, như Tạo mới project, Lưu project, run/debug project,...
2. Editor: là nơi bạn sẽ code các dòng code Java vào cửa sổ này.
3. View windows: các cửa sổ theo dõi, các log hay cây thư mục project sẽ hiển thị ở các cửa sổ này.
4. Status bar: thanh trạng thái thỉnh thoảng sẽ hiển thị trạng thái của ứng dụng.
Đối với từng cửa sổ con, bạn hoàn toàn có thể nhấn vào dấu để thu nhỏ chúng lại. Mình thì nghĩ bạn nên thu nhỏ hết tất cả luôn, chỉ chừa mỗi
cửa sổ Editor thôi cho dễ code, như bạn thấy ở hình dưới. Bạn hoàn toàn yên tâm là có thể hiển thị chúng lại kích cỡ cũ khi cần bằng cách nhấn lại vào các nút được mình khoanh tròn màu đỏ.
Có các cách sau để bạn tạo mới một project. Hãy chọn cho mình cách mà bạn thích nhất.
1. Vào menu File > New > Java Project.
2. Vào menu File > New > Project.... Cửa sổ tiếp theo xuất hiện, bạn nhấn vào thư mục có tên Java, ở các thành phần được xổ ra sau đó, bạn chọn Java Project và nhấn Next > như hình sau.
3. Nếu không dùng menu bạn có thể nhìn vào icon hình cửa sổ với dấu + ở thanh Toolbar như hình dưới đây, nhưng nhớ hãy click vào nút hình tam giác kế bên icon đó nhé. Khi đó bạn cũng có hai tùy chọn như cách thứ 2 trên đây.
Bạn chọn cách nào trong ba cách trên cũng được, còn mình thì lười hơn, mình nhấn tổ hợp phím Command+N (đó là với Mac, với Windows là Ctrl+N) cho nó nhanh. ;)
Màn hình kế tiếp trông như sau.
Ở mục Project name bạn gõ vào tên của project, mình sẽ đặt tên project này là HelloWorld, bạn có thể gõ khoảng trắng hay in hoa tùy thích. Vậy tại sao lại đặt là "HelloWorld"? Sở dĩ project đầu tiên của bạn có tên như vậy vì nó thể hiện rằng đây là dấu ấn của bạn với một ngôn ngữ lập trình mới, mà với lập trình viên, dấu ấn đầu tiên đó được xem như một sự chào hỏi của
bạn đến với thế giới. Nghe hoành tráng ha, thực ra thì mình cũng đùa một tí, câu chào hello world luôn được các cuốn sách hay các trang web hướng dẫn lập trình sử dụng khi hướng dẫn mọi người ở bài học đầu tiên, nó mang ý nghĩa bắt đầu cho những điều hay ho phía trước. Và bài học của mình cũng không ngoại lệ, cũng hello world! Bạn có quyền đặt bất kỳ cái tên nào ở bài hôm nay cũng được.
Quay trở lại bài học, sau khi đặt tên cho project như trên thì bạn nhấn Finish.
Ở bước này bạn sẽ tạo một class. Bạn sẽ thắc mắc class là gì? Như ở bài trước mình cũng có nói Java là một ngôn ngữ hướng đối tượng (OOP), ngay khi làm việc với Java bạn buộc phải suy nghĩ và làm việc theo hướng đối tượng dù bạn có là người mới vào hay không. Và class là một trong những khái niệm của hướng đối tượng. Bạn sẽ bắt đầu biết đến class từ bài học số 16.
Nhưng không phải cứ làm việc theo hướng đối tượng là phải biết về OOP, ở các bài đâu tiên này bạn cứ chấp nhận chuyện tạo mới một class. Bạn chỉ cần biết class là nơi mà chúng ta sẽ code vào đó, hệ thống sẽ tìm kiếm đến các class để mà biên dịch source code thành mã có thể thực thi được, mọi dòng code để bên ngoài class đều không hợp lệ và hệ thống sẽ báo lỗi ngay.
Trước khi tạo mới một class, bạn chắc rằng cửa sổ nhỏ bên trái Eclipse được mở, cửa sổ này có tên Package Explorer, là nơi hiển thị tất cả các file và folder trong project của bạn theo kiểu cây thư mục, với project bạn vừa tạo xong, Package Explorer hiển thị như sau.
Để tạo class, bạn có thể chọn theo menu File > New > Class, hoặc nhấn chuột phải vào project trong cửa sổ Package Explorer và chọn New > Class.
Cửa sổ tiếp theo xuất hiện, bạn đặt tên cho class ở mục Name, như hình sau mình đặt tên cho class này là MyFirstClass. Và bạn nhớ check chọn public static void main(String args), với tùy chọn này được check, hệ thống sẽ tạo sẵn cho bạn một phương thức main trong class vừa tạo. Phương thức main này là phương thức mà hệ thống sẽ tìm đến đầu tiên nhất và bắt đầu thực thi các dòng code từ đây cho bạn. Nếu không có phương thức main thì hệ thống sẽ không biết ứng dụng của bạn bắt đầu từ đâu, và vì vậy không có dòng code nào được thực thi hết. Bạn sẽ biết rõ hơn về khái niệm Phuơng thức cũng như được hiểu rõ về phương thức main và các phương thức khác ở các bài học sau.
Sau khi nhấn Finish, bạn sẽ thấy class MyFirstClass.java xuất hiện trong cửa sổ Package Explorer, nội dung class này cũng được mở sẵn trong cửa sổ Editor, bạn cũng thấy phương thức main được tạo ra sẵn khi bạn có check vào checkbox ở bước trên. Nếu bạn không check vào checkbox đó mà muốn tự mình code, thì bạn nên code cho đúng từng chữ như hình chụp bên dưới nhé, bạn code sai chữ nào là hệ thống hoặc sẽ báo lỗi hoặc sẽ không chạy được đấy.
Bây giờ là lúc bạn code dòng code Java đầu tiên. với class MyFirstClass.java được mở như trên, bạn code vào trong phương thức main câu lệnh như sau, nhớ là kết thúc câu lệnh phải có dấu ";" nhé.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub System.out.println("Hello World!"); } }
Có thể có bạn sẽ lười bằng cách thay vì code thì bạn lại copy/paste code từ trang web này vào. Có thể lắm, có đúng là bạn không? Nếu đúng thì bạn nên bỏ các dòng code vừa paste đó đi nhé, hãy code từ chính đôi tay của bạn. Ở các bài học sau cũng vậy, khi gặp các dòng code hay các yêu cầu buộc bạn phải code, thì bạn cũng đừng nên copy, mà hãy đọc trước yêu cầu, rồi thử code trước. Nhưng nếu bạn không biết code ra sao nữa thì có thể nhìn các dòng code mẫu và code lại. Sau đó bạn thử thực thi chương trình xem kết quả có đúng hay không. Nếu là do bạn tự code, và kết quả thực thi của bạn không đúng với mình, thì hoặc là bạn
sai, hoặc mình sai, và bạn có thể để lại bình luận bên dưới bài học để nhắc nhở mình. Còn nếu kết quả thực thi của bạn và mình quá chuẩn, nhưng code có khác nhau, cũng đâu có sao, lập trình là một tư duy mở, và mỗi chúng ta có một cách thức suy luận khác nhau, miễn sao cùng đi đến một kết quả chung là được. Bạn đã hiểu sơ về cách thức học lập trình rồi đúng không nào.
Có một lưu ý hay là trong quá trình code, sau khi bạn gõ dấu chấm (.) thì hệ thống thường hay gợi ý các phương thức, khi đó bạn có thể nhấn enter (máy Mac là return) để chọn nhanh phương thức gợi ý đó, hoặc dùng phím mũi tên để chọn các phương thức khác tương ứng.
Nếu gõ lệnh mà bạn thấy có xuất hiện icon dấu x nằm trong vòng tròn màu đỏ, icon này xuất hiện ở thanh bên trái của Editor như hình trên, thì hoặc dòng code bị lỗi đâu đó, hoặc bạn code chưa xong, chưa kết thúc câu lệnh bằng ";", thậm chí là chưa save class đó lại,... Bất cứ lỗi nào xảy ra mà bạn không biết cách khắc phục thì hãy để lại bình luận bên dưới bài này nhé, mình sẽ giúp bạn.
Sau khi tự tin code xong rồi, bạn nhớ phải save lại bằng cách nhấn chọn icon
trên thanh công cụ. Sau đó nhấn icon để thực thi chương trình.
Rất nhanh, bạn sẽ thấy cửa sổ Console xuất hiện với nội dung Hello World mà bạn vừa code lúc nãy, vì câu lệnh System.out.println() là để in log ra console.
Xin chúc mừng, bạn vừa code xong chương trình Java đầu tiên của mình. Bạn vẫn chưa biết rõ ý nghĩa của các câu lệnh, hay cấu trúc của Java là gì đâu, bạn sẽ sớm được hiểu rõ ở các bài kế tiếp.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Với project HelloWorld mà bạn đã tạo ra ở bài trước. Hôm nay bạn hãy bắt đầu tìm hiểu về ngôn ngữ Java bằng việc tiếp cận hai khái niệm biến và hằng, với bài học bạn cũng sẽ được thực hành khai báo một số biến và hằng cơ bản. Và để dễ tiếp cận nhất có thể, với mỗi đoạn code thực hành, mình cũng sẽ đưa ra các trường hợp ĐÚNG và SAI của code, để các bạn vừa hiểu nhanh mà cũng vừa hiểu chắc bài học. Mời các bạn bắt đầu bài học.
Việc đầu tiên tiếp cận với một ngôn ngữ lập trình, không riêng gì Java, bạn phải làm quen với khái niệm biến, các tài liệu tiếng Anh gọi biến là variable.
Biến là các định nghĩa từ bạn, nó giúp hệ thống tạo ra các vùng nhớ để lưu giữ các giá trị trong ứng dụng. Mỗi biến đều có một kiểu dữ liệu riêng, kiểu dữ liệu này sẽ báo cho hệ thống biết biến đó có "độ lớn" bao nhiêu. Độ lớn của biến sẽ cho biết khả năng lưu trữ giá trị của biến. Bạn sẽ được nắm rõ về độ lớn lưu trữ của mỗi loại biến ở bài học này. Ngoài ra thì mỗi biến còn phải được bạn đặt một cái tên giúp hệ thống có thể quản lý, truy xuất dữ liệu trong vùng nhớ của biến đó.
Như khái niệm trên đây chắc bạn cũng đã nắm phần nào, việc khai báo một biến thực chất là việc bạn sẽ đặt tên cho biến, rồi đặt một kiểu dữ liệu cho biến, và có thể lưu trữ giá trị ban đầu vào cùng nhớ của biến.
Để khai báo một biến trong Java, bạn hãy ghi nhớ cú pháp sau:
kiểu_dữ_liệu tên_biến;
hoặc
kiểu_dữ_liệu tên_biến = giá_trị;
Mình giải thích một chút về cú pháp trên.
- kiểu_dữ_liệu sẽ quyết định đến độ lớn của biến, các loại kiểu_dữ_liệu và các cách dùng sẽ được nói rõ hơn ở bên dưới bài học.
- tên_biến là... tên của biến :) , cách đặt tên cho biến sẽ được mình nói rõ hơn ở phần bên dưới luôn.
- Khi khai báo biến, bạn có thể "gán" cho biến một giá_trị ban đầu, việc gán giá trị này sẽ làm cho biến có được dữ liệu ban đầu ngay khi được tạo ra. Nếu một biến được tạo ra mà không có giá trị gì được gán, nó sẽ được gán giá trị mặc định, giá trị mặc định này sẽ tùy theo kiểu_dữ_liệu của biến mà chúng ta sẽ nói đến bên dưới.
- Cuối cùng, sau mỗi khai báo biến bạn nhớ có dấu ";" để báo cho hệ thống biết là kết thúc việc khai báo, nếu không có ";" hệ thống sẽ báo lỗi ngay.
Nào, giờ thì bạn hãy mở project HelloWorld vừa tạo ra ở bài trước, bạn thử gõ các dòng khai báo biến như sau nhé. Chỉ là gõ cho quen thôi. Hãy chú ý đến các dòng được tô sáng. Bạn sẽ có cơ hội được khai báo các biến ở các mục phía dưới.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub System.out.println("Hello World!"); double salary; int count = 0;
boolean done = false; long earthPopulation; } }
Như ví dụ ở trên, các tên biến được đặt tên là salary, count, done hay earthPopulation. Việc đặt tên cho biến đơn giản đúng không nào, đặt như thế nào cũng được, miễn sao bạn đọc lên được (ngôn ngữ của con người), và bạn thấy nó dễ hiểu và đúng với công năng của biến đó là được rồi.
Tuy nhiên thì việc đặt tên cho biến cũng không hoàn toàn tự do, có một số "luật" và "lệ" đặt tên sau đây, nếu bạn mắc phải một trong các quy tắc đặt tên này thì hệ thống sẽ báo lỗi ngay đấy.
Quy Tắc 1
Ký tự bắt đầu của tên biến phải là chữ cái, hoặc dấu gạch dưới (_), hoặc ký tự đô la ($). Ví dụ cho việc đặt tên biến đúng như sau.
Đúng |
int count; int Count; int _count; int $count; |
Còn đặt tên biến như sau sẽ bị lỗi. Vì các ký tự đầu tiên của chúng hoặc là một số, hoặc là một ký tự đặc biệt không được phép. Nếu không tin bạn cứ thử gõ vào Eclipse xem sao nhé.
Sai |
int 5count; int 5Count; int #count; int /count; |
Quy Tắc 2
Không được có khoảng trắng giữa các ký tự của biến. Ví dụ đặt tên biến như sau là sai.
Sai |
int this is count; |
Nếu bạn muốn tên biến trên được tách biệt rõ ràng từng chữ cho rõ nghĩa thì có thể đặt như sau.
Đúng |
int thisIsCount; int this_is_count; int This_Is_Count; |
Đến đây thì cho mình nói ngoài lề xíu: trong trường hợp biến có nhiều từ như ví dụ trên, việc bạn chọn muốn đặt theo kiểu nào trong 3 cách đúng như trên đều được, nhưng mình khuyên bạn nên chọn cách đầu tiên thisIsCount. Với cách này thì chữ đầu tiên this được viết thường, các chữ cái ở mỗi từ tiếp theo sẽ viết hoa IsCount. Cách này không phải bắt buộc nhưng nó thành "lệ" chung cho lập trình Java, nếu bạn thực hiện cách đặt tên này thì việc bạn đọc code Java của người khác sẽ dễ dàng hơn nhiều do họ cũng sẽ viết giống như bạn. Lưu ý là đừng có chữ nào cũng viết hoa hết như ThisIsCount vì nó rất dễ nhầm lẫn với đặt tên cho class mà bạn sẽ học ở các bài sau, hay THIS_IS_COUNT vì nó lại nhầm với hằng số mà bạn cũng sắp được làm quen ở bài hôm nay.
Quy Tắc 3
Không chứa ký tự đặc biệt bên trong tên biến như !@#%^&*. Ví dụ đặt tên biến như sau là sai.
Sai |
int c@unt; int count#; int count*count; |
Quy Tắc 4
Không được đặt tên biến trùng với keyword. Keyword là các từ khóa mà ngôn ngữ Java dành riêng cho một số mục đích của hệ thống. Các keyword của Java được liệt kê trong bảng sau.
abstract | continue | for | new | switch |
assert | default | goto | package | synchronized |
boolean | do | if | private | this |
break | double | implements | protected | throw |
byte | else | import | public | throws |
case | enum | instanceof | return | transient |
catch | extends | int | short | try |
char | final | interface | static | void |
class | finally | long | strictfp | volatile |
const | float | native | super | while |
Ví dụ đặt tên biến như sau là sai.
Sai |
boolean continue = true; long class; int final; |
Tuy nhiên bạn có thể đặt tên biến có chứa keyword bên trong đó mà không bị bắt lỗi, như ví dụ sau.
Đúng |
boolean continue1 = true; long classMySchool; int finalTarget; |
Như đã nói ở trên, mỗi biến đều phải có một kiểu_dữ_liệu kèm theo nó. Kiểu dữ liệu sẽ báo cho hệ thống biết biến đó có "độ lớn" bao nhiêu, độ lớn này sẽ cho biết khả năng lưu trữ giá trị của biến.
Việc chọn một kiểu dữ liệu cho biến là phụ thuộc ở bạn. Thông thường bạn nên dựa trên công năng của biến đó, chẳng hạn như biến thuộc kiểu ký tự hay kiểu
số. Hoặc bạn có thể dựa trên khả năng lưu trữ giá trị của biến đó, như biến là số integer hay số long.
Chúng ta hãy bắt đầu làm quen với 8 kiểu dữ liệu "nguyên thủy" của biến. Gọi là kiểu dữ liệu nguyên thủy vì chúng là các kiểu dữ liệu cơ bản và được cung cấp sẵn của Java. Về mặt lưu trữ thì kiểu dữ liệu nguyên thủy lưu trữ dữ liệu trong chính bản thân nó, việc sử dụng kiểu này cũng rất đơn giản, và không dính líu gì đến hướng đối tượng cả. Ngược lại với kiểu dữ liệu nguyên thủy là kiểu dữ liệu "mở rộng" sẽ được mình nói ở các bài sau khi bạn đã nắm được kiến thức căn bản của Java. Các kiểu dữ liệu nguyên thủy của bài học hôm nay được liệt kê theo sơ đồ sau.
Nhìn nhiều vậy thôi chứ các kiểu dữ liệu nguyên thủy mà chúng ta cần quan tâm chính là các ô màu xanh lá cây, bao gồm các kiểu int, short, long, byte, float, double, char, và boolean. Các ô màu khác chỉ là gom nhóm các kiểu dữ liệu lại để chúng ta dễ nhớ và dễ sử dụng hơn thôi.
Chúng ta cùng nhau đi vào từng loại kiểu dữ liệu để biết rõ hơn.
Kiểu Số Nguyên
Kiểu này dùng để lưu trữ và tính toán các số nguyên, bao gồm các số nguyên có giá trị âm, các số nguyên có giá trị dương và số 0. Sở dĩ có nhiều kiểu số nguyên như trên, là vì tùy vào độ lớn của biến mà bạn sẽ khai báo kiểu dữ liệu tương ứng, chúng ta cùng xem qua bảng giá trị của các kiểu dữ liệu số nguyên như sau.
Kiểu Dữ Liệu | Bộ Nhớ Lưu trữ | Độ Lớn Của Biến |
byte | 1 byte | Từ –128 Đến 127 |
short | 2 bytes | Từ –32,768 Đến 32,767 |
int | 4 bytes | Từ –32,768 Đến 32,767 |
long | 8 bytes | Từ –9,223,372,036,854,775,808 Đến 9,223,372,036,854,775,807 |
Theo như bảng trên thì bạn thấy việc chọn lựa độ lớn cho kiểu là rất quan trọng, như với kiểu là byte, hệ thống sẽ chỉ cấp phát cho chúng ta vùng nhớ lưu trữ đúng 1 byte dữ liệu, với vùng nhớ này bạn chỉ được phép sử dụng độ lớn của biến từ -128 đến 127 mà thôi.
Ví dụ bạn khai báo biến như sau là hợp lệ.
Đúng |
byte month = 5; short salaryUSD = 2000; |
Còn khai báo như sau sẽ vượt ra ngoài khả năng lưu trữ của biến và vì vậy bạn sẽ bị báo lỗi ngay.
Sai |
byte day = 365; short salaryVND = 40000000; |
Do đó khi sử dụng kiểu dữ liệu cho biến bạn phải cân nhắc đến độ lớn lưu trữ của chúng. Bạn không nên lúc nào cũng sử dụng kiểu dữ liệu long cho tất cả các trường hợp vì như vậy sẽ làm tiêu hao bộ nhớ của hệ thống khi chương trình được khởi chạy. Bạn nên biết giới hạn của biến để đưa ra kiểu dữ liệu phù hợp. Ví dụ nếu dùng biến để lưu trữ các tháng trong năm, thì kiểu byte là đủ. Hoặc dùng biến để lưu tiền lương của nhân viên theo VND, thì dùng kiểu int, nhưng nếu chương trình của bạn lưu trữ toàn người giàu thì lương có thể được sử dụng kiểu long :)
Nếu không khai báo giá trị cho biến, thì biến kiểu số nguyên sẽ có giá trị mặc định là 0 (hoặc 0L đối với kiểu long).
Kiểu Số Thực
Kiểu này dùng để lưu trữ và tính toán các số thực, chính là các số có dấu chấm động. Cũng giống như kiểu số nguyên, kiểu số thực cũng được chia ra thành nhiều loại với nhiều độ lớn khác nhau tùy theo từng mục đích, như bảng sau.
Kiểu Dữ Liệu | Bộ Nhớ Lưu trữ | Độ Lớn Của Biến |
float | 4 bytes | Xấp xỉ ±3.40282347E+38F |
double | 8 bytes | Xấp xỉ ±3.40282347E+38F |
Với sự thoải mái trong cấp phát bộ nhớ của kiểu số thực thì bạn không quá lo lắng đến việc phân biệt khi nào nên dùng kiểu float hay double. Ví dụ sau đây cho thấy trường hợp dùng kiểu số thực.
Đúng |
float rating = 3.5f; double radius = 34.162; |
Sở dĩ với kiểu float ở trên phải để chữ f vào cuối khai báo float rating = 3.5f; là vì thỉnh thoảng chúng ta phải nhấn mạnh cho hệ thống biết rằng chúng ta đang xài kiểu float chứ không phải kiểu double, nếu bạn không để thêm f vào cuối giá trị thì hệ thống sẽ báo lỗi. Tương tự bạn cũng có thể khai báo double radious = 34.162d; nhưng vì hệ thống đã hiểu đây là số double rồi nên bạn không có chữ d trong trường hợp này cũng không sao.
Nếu không khai báo giá trị cho biến, thì biến kiểu số thực sẽ có giá trị mặc định là 0.0f đối với kiểu float và 0.0d đối với kiểu double.
Kiểu char
Kiểu char dùng để khai báo và chứa đựng một ký tự. Bạn có thể gán một ký tự cho kiểu này trong một cặp dấu nháy đơn như 'a' hay 'B' như ví dụ sau.
Đúng |
char thisChar = 'a'; |
Bạn phải luôn nhớ là ký tự 'a' được khai báo ở trên phải nằm trong cặp nháy đơn chứ không phải nháy kép nhé. Nháy kép là dành cho chuỗi sẽ
được nói đến ở bài học sau. Và nhớ là nếu không có nháy cũng sai. Ví dụ sau khai
báo sai kiểu char.
Sai |
char thisCharFail1 = "a"; char thisCharFail2 = b; char thisCharFail3 = 'ab'; |
Ngoài việc khai báo như trên, kiểu char còn được dùng theo mã Unicode. Mã Unicode bắt đầu bằng '\u0000' và kết thúc bằng '\uffff'. Lưu ý là với cách dùng kiểu mã Unicode này, bạn vẫn phải dùng nháy đơn cho việc khai báo. Ký hiệu \u cho biết bạn đang dùng với mã Unicode chứ không phải ký tự bình thường. Với cách khai báo theo kiểu mã Unicode này, bạn có thể đưa vào chương trình một số ký tự đặc biệt, hãy thử thực hành code như sau và in ra log, bạn sẽ thấy hiệu quả của Unicode nhé.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub
char testUnicode1 = '\u2122'; char testUnicode2 = '\u03C0'; System.out.println("See this character " + testUnicode1 + " and this character " + testUnicode2); } }
Kết quả in ra console như sau.
Kiểu boolean
Khác với C/C++, kiểu boolean trong ngôn ngữ Java chỉ được biểu diễn bởi hai giá trị là true và false mà thôi. Do đó kiểu dữ liệu này chỉ được dùng trong việc kiểm tra các điều kiện logic, chứ không dùng trong tính toán, và bạn cũng không thể gán một kiểu số nguyên về kiểu boolean như trong C/C++ được.
Khai báo một kiểu boolean đúng như sau.
Đúng |
boolean male = true; boolean graduated = false; |
Việc khai báo một biến boolean như sau là sai.
Sai |
boolean male = 1; |
Nếu không khai báo giá trị cho biến, thì biến kiểu boolean sẽ có giá trị mặc định là false.
Hằng Số trong tài liệu tiếng Anh gọi là const, viết tắt của từ constant. Hằng cũng tương tự như biến, nhưng đặc biệt ở chỗ nếu một biến được khai báo là hằng thì nó sẽ không được thay đổi giá trị trong suốt chương trình.
Vì các ví dụ trên đây chỉ giúp cho các bạn khai báo một biến mà không đá động gì đến việc thay đổi giá trị biến đó, nhưng thực ra một biến có thể bị thay đổi giá trị nhiều lần trong suốt quá trình thực thi của ứng dụng, bạn sẽ làm quen với việc thay đổi giá trị của biến ở các bài sau. Còn hằng thì không có sự thay đổi nào cả, nếu bạn cố tình thay đổi hay gán lại giá trị mới của hằng sau khi nó được khai báo, bạn sẽ nhận được thông báo lỗi.
Để khai báo một hằng số, bạn cũng khai báo giống như biến nhưng thêm final vào trước khai báo. Bạn sẽ được biết nhiều hơn đến hằng số qua bài học riêng về từ khóa final. Giờ thì bạn có thể xem ví dụ sau để hiểu sơ về hằng.
final float PI = 3.14f; final char FIRST_CHARACTER = 'a'; final int VIP_TYPE = 1;
Bạn nên tập khai báo hằng bằng các ký tự viết hoa như ví dụ trên đây, điều đó giúp chúng ta dễ dàng phân biệt được đâu là biến và đâu là hằng sau này.
Chúng ta vừa xem qua cách khai báo các biến và hằng. Nếu bạn vẫn còn mông lung về ngôn ngữ Java ở bài học này cũng không sao, mọi thứ chỉ mới bắt đầu, và rất cần nỗ lực từ phía bạn. Bạn sẽ được học sâu hơn các kiến thức về ngôn ngữ Java này ở các bài sau.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Nếu như ở bài trước các bạn đã làm quen với việc khai báo và sử dụng các biến và hằng trong Java. Thì đến bài hôm nay chúng ta cùng học cách vận dụng các biến và hằng này vào các logic tính toán trong chương trình, thông qua việc tìm hiểu về các Toán Tử.
Trước khi vào làm quen với các Toán Tử, mình muốn nói về khái niệm Biểu Thức cái đã.
Bạn nên biết là lập trình không khác gì làm toán cả, tất cả chúng ta, những Lập Trình Viên, chỉ đơn giản là đang vận dụng các phép toán mà chúng ta đã từng được học vào trong việc lập trình ra các ứng dụng mà thôi. Và để làm quen lại với kiến thức toán, chúng ta cùng quay về với khái niệm Biểu Thức. Trong toán học định nghĩa rằng Biểu Thức là sự kết hợp giữa các Toán Tử (Operator) và các Toán Hạng (Operand) theo đúng một trật tự nhất định. Trong đó mỗi Toán Hạng có thể là một Hằng, một Biến hoặc một Biểu Thức khác.
Mình xin minh họa một Biểu Thức như sau.
Qua đó * và + là các Toán Tử. 2, y và 5 là các Toán Hạng, trong đó 2 và 5 là các Hằng, còn y là Biến.
Cũng giống như trong toán học, bạn có thể dùng cặp dấu ngoặc đơn () để gom nhóm các Biểu Thức lại, và khi đó thì Biểu Thức trong dấu ngoặc đơn này sẽ được ưu tiên thực hiện trước.
Ví dụ minh họa cho một Biểu Thức có dấu ngoặc đơn.
2 * (y + 5)
Giờ chúng ta cùng ra xa khỏi toán học một chút để đi sâu vào kiến thức lập trình. Thì ngoài các Toán Tử cộng, trừ, nhân chia trong toán học ra, chúng ta còn có nhiều Toán Tử đặc thù khác của lập trình, một trong những Toán Tử chúng ta làm quen đầu tiên hôm nay đó là Toán Tử Gán.
Trong Java, Toán Tử Gán được thực hiện thông qua ký hiệu "=". Quen lắm phải không nào, vì bài trước bạn đã thực hành khai báo một biến như sau int count = 5; thì dấu "=" ở đây chính là một phép gán. Phép gán được định nghĩa cụ thể thông qua cú pháp.
tên_biến = biểu_thức
Khi đó kết quả của một biểu_thức (biểu_thức này có thể là một Biểu Thức gồm nhiều Toán Tử và Toán Hạng, hay có thể chỉ là một con số như các ví dụ ở bài trước) sẽ được gán vào tên_biến. Tức là tên_biến sẽ chứa đựng giá trị bằng với biểu_thức mang đến thông qua phép gán này.
Với bài thực hành này, bạn hãy thực hiện phép gán sau đây và cho biết kết quả in ra log sau khi gán giá trị cho biến thisYear là gì nhé.
final int THIS_YEAR = 2016; int thisYear = 2000; thisYear = THIS_YEAR;
System.out.println("This year is " + thisYear);
Kết quả như sau.
This year is 2016
Trong trường hợp bạn có một giá trị giống nhau được gán cho nhiều biến khác nhau, thay vì như ví dụ dưới đây bạn phải gán giá trị đó cho từng biến một.
int x = 10; int y = 10; int z = 10;
Thì bạn có thể thực hiện việc gán chỉ với một dòng như dưới đây. Đảm bảo đúng.
Đúng |
int x; int y; int z; x = y = z = 10; |
Hoặc bạn có thể viết ngắn gọn hơn, cách viết này gom các khai báo biến với cùng một kiểu dữ liệu vào cùng một dòng.
Đúng |
int x, y, z; x = y = z = 10; |
Nhưng lưu ý bạn không thể viết như vậy được.
Sai |
int x = int y = int z = 10; |
Chúng ta làm quen với Toán Tử thứ hai của lập trình, Toán Tử này thì hoàn toàn
giống với toán học, đó là các phép toán cộng, trừ, nhân, chia. Các phép toán này được gom lại chung vào một Toán Tử, gọi là Toán Tử Số Học. Cụ thể về các Toán Tử Số Học được mình liệt kê ở bảng sau.
Toán Tử | Ý Nghĩa |
---|---|
+ | Toán Tử Cộng, có thể cộng các số, và còn có thể cộng Chuỗi sẽ được mình nói cụ thể ở bài học chuỗi sau. |
- | Toán Tử Trừ. |
* | Toán Tử Nhân. |
/ | Toán Tử Chia lấy phần nguyên, ví dụ 5/2 sẽ bằng 2, phần dư bị loại bỏ. |
% | Toán Tử Chia lấy phần dư, ví dụ 5%2 sẽ bằng 1, đó chính là số dư của phép chia. |
Bạn hãy thử code để biết kết quả in ra của phép gán từ một Biểu Thức tới biến age với các Toán Tử Số Học như sau.
int age; int thisYear = 2016;
int yearOfBirth = 1990; age = thisYear - yearOfBirth; System.out.println("I am " + age + " years old");
Kết quả in ra như sau.
I am 26 years old
Bạn thử đoán xem kết quả in ra của các các so1, so2, và so3 như ví dụ bên dưới, và xem kết quả có đúng với bạn đoán không nhé.
int so1; int so2; float so3; so1 = 15 / 6; so2 = 15 % 6; so3 = 15 / 6;
System.out.println("Ket qua so1 la " + so1 + ", so2 la " + so2 + ", so3 la " + so3);
Và kết quả.
Ket qua so1 la 2, so2 la 3, so3 la 2.0
Một Toán Tử đặc trưng của lập trình nữa, đó là Toán Tử Một Ngôi. Với Toán Tử này, thì chỉ cần một Toán Tử kết hợp với một Toán Hạng thôi là đã có thể cho ra kết quả. Các bạn hãy nhìn vào bảng sau.
Toán Tử | Ý Nghĩa |
---|
+ | Toán Tử Cộng, toán tử cộng này khác với toán tử cộng ở bảng Số Học trên, toán tử này biểu diễn số dương. Vì các số dương bình thường không cần phải hiển thị toán tử này, nên bạn cũng sẽ không thấy công dụng của nó. |
- | Toán Tử Trừ, ngược lại với toán tử cộng ở trên, toán tử trừ này biểu diễn số âm. |
++ | Toán Tử Tăng, toán tử này sẽ làm tăng giá trị của toán hạng lên 1 đơn vị. |
- - | Toán Tử Giảm, toán tử này sẽ làm giảm giá trị của toán hạng đi 1 đơn vị. |
! | Toán Tử Phủ định logic. Toán tử này sẽ đảo ngược giá trị của biến biểu thức logic. Nếu biểu thức logic đang là true thì sẽ bị đảo ngược thành false và ngược lại |
Bạn hãy nhìn biểu thức dưới đây, soGi sẽ được gán bằng một Biểu Thức, mà ở đó soDuong được cộng với một Toán Tử Một Ngôi biểu thị số âm.
iint soDuong = 4; int soGi = soDuong + -10; System.out.println("Ket qua la " + soGi);
Và kết quả như sau chắc bạn cũng dễ dàng đoán được.
iKet qua la -6
Bạn hãy chú ý vào các Toán Tử "++" và "- -" sau đây, như đã nói, các Toán Tử này sẽ làm Toán Hạng đứng sau nó tăng lên hoặc giảm đi 1 rồi mới thực hiện các phép toán khác.
int so1 = 4; int so2 = 10; int soKetQua = ++so1 + --so2; System.out.println("So1 la " + so1 + ", so2 la " + so2 + ", ket qua la " + soKetQua);
Và kết quả là.
So1 la 5, so2 la 9, ket qua la 14
Mình xin giải thích một chút ở Bài Thực Hành trên đây, vì Toán Tử Một Ngôi dạng này sẽ hơi lạ với các bạn mới làm quen với lập trình. Với việc thực hiện Biểu Thức soKetQua = ++so1 + - -so2; hệ thống sẽ thực hiện biểu thức ++so1 trước và so1 sau đó mang giá trị là 5 (++so1 lúc bấy giờ tương tự như biểu thức so1 = so1 + 1; vậy), rồi hệ thống cũng thực hiện tiếp - -so2 ra giá trị 9 (tương tự thì - -so2 cũng sẽ giống như so2 = so2 - 1;), sau cùng thì tiến hành cộng hai số này lại và gán cho biến soKetQua.
Có bạn nào thắc mắc là hai cách viết ++so1 và so1++ có khác nhau không? Câu trả lời là Có. Cũng giống như C/C++, khi bạn viết ++so1, Java sẽ thực hiện việc tăng giá trị của so1 trước khi lấy giá trị đó dùng vào trong Biểu Thức. Còn nếu viết so1++, Java sẽ lấy giá trị của so1 dùng vào trong Biểu Thức trước khi tăng giá trị của nó.
Để hiểu rõ hơn bạn thử sửa code trên lại một chút như sau và chạy lại nhé.
int so1 = 4; int so2 = 10; int soKetQua = so1++ + so2; System.out.println("So1 la " + so1 + ", so2 la " + so2 + ", ket qua la " + soKetQua);
Kết quả in ra là.
So1 la 5, so2 la 10, ket qua la 14
Với thử nghiệm trên, bạn thấy so1++ không hề được áp dụng tăng giá trị của so1 vào biểu thức, mà so1 vẫn mang giá trị 4 rồi cộng với so2 là 10 ra kết quả 14. Sau khi thự hiện xong Biểu Thức thì so1 mới được tăng giá trị
và vì vậy bạn thấy in ra so1 là 5.
Toán Tử Quan Hệ sẽ so sánh các Biểu Thức với nhau để cho ra một giá trị boolean, tức là cho ra một trong hai giá trị là true hay false. Bạn hãy nhìn vào bảng để xem các Toán Tử Quan Hệ sau.
Toán Tử | Ý Nghĩa |
---|---|
== | Bằng |
!= | Không bằng (khác) |
> | Lớn hơn |
>= | Lớn hơn hoặc bằng |
< | Nhỏ hơn |
<= | Nhỏ hơn hoặc bằng |
Chú ý rằng kết quả của Toán Tử này luôn trả về một giá trị boolean nhé.
Bạn thử ngẫm xem kết quả in ra sẽ là giá trị true hay false nhé.
double weight = 71.23; int height = 191; boolean married = false; boolean attached = false; char gender = 'm'; System.out.println("Ket qua 1: " + (!married == attached)); System.out.println("Ket qua 2: " + (gender != 'f')); System.out.println("Ket qua 3: " + (height >= 180));
System.out.println("Ket qua 4: " + (weight > 90));
Bạn đã có kết quả của riêng mình chưa? Nếu có rồi thì so sánh với kết quả dưới đây.
Ket qua 1: false Ket qua 2: true Ket qua 3: true Ket qua 4: false
Các Toán Tử Điều Kiện sẽ so sánh các Biểu Thức mang giá trị true và false với nhau. Toán Tử này còn được gọi là Toán Tử Logic. Bạn cứ tưởng tượng toán tử này sẽ hoạt động theo kiểu câu nói "nếu trời không mưa và tôi có tiền thì tôi sẽ đi chơi hôm nay", trong đó "nếu trời không mưa" mang một giá trị boolean, "tôi có tiền" cũng là một giá trị boolean, hai giá trị này được so sánh bởi Toán Tử "và". Vậy nếu "trời không mưa" và "tôi có tiền" đều mang giá trị true thì kết quả so sánh sẽ là true, tức "tôi sẽ đi chơi hôm nay" là true, tức là tôi sẽ đi chơi. Ngược lại nếu một trong hai vế đầu là false thì kết quả sẽ là false, tức tôi sẽ không đi chơi. Các Toán Tử Điều Kiện được nêu trong bảng sau.
Toán Tử | Ý Nghĩa |
---|---|
&& | So sánh AND. |
|| | So sánh OR. |
?: | So sánh theo điều kiện, sẽ được nhắc đến khi chúng ta học đến cách viết điều kiện if..else.. ở bài học sau. |
Sau đây là Bảng chân trị, tức các kết quả so sánh điều kiện của các Toán Tử && và || như sau.
p | q | p && q | p || q |
---|---|---|---|
false | false | false | false |
false | true | false | true |
true | false | false | true |
true | true | true | true |
Bạn thử gõ các câu lệnh sau và ngẫm kết quả của nó nhé.
int age = 18;
double weight = 71.23; int height = 191; boolean married = false; boolean attached = false; char gender = 'm'; System.out.println(!married && !attached && (gender == 'm')); System.out.println(married && (gender == 'f')); System.out.println((height >= 180) && (weight >= 65) && (weight <= 80));
System.out.println((height >= 180) || (weight >= 90));
Kết quả như sau.
true false true true
Chúng ta thử giải nghĩa dòng in log thứ nhất để hiểu rõ hơn. Đầu tiên biến married có giá trị false, !married đảo ngược giá trị thành true. Tương tự attached là false và !attached là true, (gender == 'm') sẽ là true. Tóm lại chúng ta có true && true && true, và kết quả cuối cùng theo bảng chân trị là true.
Dòng thứ hai, ưu tiên biểu thức trong ngoặc đơn trước (gender == 'f') sẽ cho kết quả false. Biến married được khai báo là false. false && false kết quả theo bảng chân trị là false.
Bạn cứ giải nghĩa tương tự cho các dòng còn lại.
Toán Tử Bitwise là Toán Tử sẽ tương tác trực tiếp đến các bit của Toán Hạng. Để hiểu về việc làm việc với các bit là gì thì dài dòng lắm, có thể mình sẽ nói rõ hơn ở các bài khác. Nhưng bạn có thể hiểu là bản chất của tất cả dữ liệu mà máy tính có thể hiểu và làm việc chính là các dữ liệu dạng nhị phân được biểu diễn chỉ bởi hai giá trị là 0 và 1. Mỗi một giá trị 0 hay 1 như vậy là một bit, do đó có thể nói Toán Tử Bitwise là Toán Tử tương tác trực tiếp đến các bit là vậy.
Lưu ý là Toán Tử Bitwise này sẽ không làm việc được với các dữ liệu thuộc kiểu số thực (float và double).
Bảng sau là Toán Tử Bitwise.
Toán Tử | Ý Nghĩa |
---|---|
& | AND |
| | OR |
^ | XOR |
~ | NOT, đảo ngược bit 0 thành 1, và ngược lại 1 thành 0. |
>> | Dịch phải |
<< | Dịch trái |
Bạn nên nhớ AND ở Toán Tử Bitwise chỉ có một dấu &, còn AND ở Toán Tử Điều Kiện có hai dấu &&. Và một điều khác nhau nữa là && chỉ so sánh các Biểu Thức boolean với nhau, còn & so sánh dựa trên các bit, và vì vậy & có thể làm việc với mọi kiểu dữ liệu nguyên thủy trong Java trừ kiểu số thực đã nói ở trên. Tương tự cho khác nhau giữa || và |.
Vậy trước khi đi vào bài thực hành Toán Tử Bitwise, bạn hãy xem qua Bảng chân trị cho Bitwise như sau.
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
Ở bài thực hành này chúng ta cùng code thử các Toán Tử Bitwise như sau, mình sẽ giải thích kết quả tại sao ở bên dưới bài thực hành này.
byte so1 = 5; byte so2 = 12; System.out.println("Toan tu &: " + (so1 & so2)); System.out.println("Toan tu |: " + (so1 | so2)); System.out.println("Toan tu ^: " + (so1 ^ so2)); System.out.println("Toan tu ~: " + (so1 & ~so2)); System.out.println("Toan tu <<: " + (so1<<1)); System.out.println("Toan tu >>: " + (so2>>2));
Kết quả ở dưới đây.
Toan tu &: 4 Toan tu |: 13 Toan tu ^: 9 Toan tu ~: 1 Toan tu <<: 10 Toan tu >>: 3
Giờ hãy cùng nhau giải thích ý nghĩa của các phép toán. Chúng ta đều biết kiểu dữ liệu byte được hệ thống cấp phát cho 1 byte bộ nhớ, và 1 byte = 8 bits, vậy hai biến so1 và so2 có giá trị lần lượt là 5 và 12 sẽ được biểu diễn dạng bit như sau.
5 = 0000 0101
12 = 0000 1100
so1 & so2: nếu bạn lấy 0000 0101 & 0000 1100, bạn hãy áp dụng bảng chân trị vào so sánh từng bit với nhau, bạn sẽ có kết quả là 0000 0100, đây là biểu diễn bit của số 4, và là kết quả của dòng log đầu tiên.
so1 | so2: tương tự như trên 0000 0101 | 0000 1100 sẽ là 0000 1101, là dạng
bit của số 13.
so1 ^ so2: 0000 0101 ^ 0000 1100 sẽ là 0000 1001, là dạng bit của số 9.
so1 & ~so2: ta xét từ ~so2, ~ là toán tử NOT, nó sẽ đảo ngược các bit của so2 thành 1111 0011, vậy so1 & ~so2 sẽ là 0000 0101 & 1111 0011, sẽ là 0000 0001, là dạng bit của số 1.
so1<<1: tức là lấy dãy bit của so1 dịch trái đi 1 bit, sẽ thành 0000 1010, là dạng bit của số 10.
so2>>2: tức là lấy dãy bit của so2 dịch phải đi 2 bit, sẽ thành 0000 0011, là dạng bit của số 3.
Chúng ta vừa mới đi qua bài học về các Toán Tử trong Java, qua bài học hôm nay bạn đã hiểu rõ hơn ngôn ngữ Java hơn rồi đúng không nào. Các Toán Tử hôm nay sẽ được ứng dụng khá nhiều vào các kiến thức và bài thực hành sau này. Bạn hãy cố gắng theo dõi các bài học thú vị phía trước nhé.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Bài hôm nay chúng ta sẽ nói về 2 vấn đề "nhỏ", đó là ép kiểu và comment source code, bạn cũng nên biết nhỏ ở đây là nhỏ về tổng số chữ viết, chứ thực ra 2 vấn đề hôm nay đều rất quan trọng cho các bài học kế tiếp đấy nhé.
Trước hết mình xin nhắc lại một chút, là chúng ta đã từng làm quen với việc khai báo một biến (hoặc hằng), khi đó bạn cần chỉ định một kiểu dữ liệu cho biến hoặc hằng đó trước khi sử dụng, việc khai báo một kiểu dữ liệu ban đầu như vậy mang tính tĩnh. Có nghĩa là nếu bạn định nghĩa biến đó là kiểu int, nó sẽ mãi là kiểu int, nếu bạn định nghĩa nó là kiểu float, nó sẽ mãi là kiểu float. Có bao giờ bạn thắc mắc nếu đem hai biến có kiểu dữ liệu khác nhau này vào tính toán với nhau, liệu nó sẽ tạo ra một biến kiểu gì? Và liệu chúng ta có thể thay đổi kiểu dữ liệu của một biến, hay một giá trị đã được khai báo hay không?
Bài này chúng ta sẽ trả lời các câu hỏi trên thông qua khái niệm Ép kiểu.
Như mình đã nói khái quát ở trên, chắc bạn cũng nắm được sơ sơ khái niệm của nó rồi. Cụ thể ép kiểu là hình thức chuyển đổi kiểu dữ liệu của một biến sang một biến mới có kiểu dữ liệu khác. Vậy việc ép kiểu này không làm thay đổi kiểu dữ liệu của biến cũ, nó chỉ giúp bạn tạo ra một biến mới với kiểu dữ liệu mới. Khái niệm là vậy, còn mục đích là gì các bạn hãy đọc tiếp bài học hôm nay nhé.
Nên nhớ là vì các bạn chỉ mới làm quen với kiểu dữ liệu nguyên thủy, nên ép kiểu hôm nay cũng chỉ nói đến ép kiểu dữ liệu nguyên thủy mà thôi.
Nếu phân loại ép kiểu dựa vào khả năng lưu trữ của biến, thì chúng ta có hai loại
ép kiểu sau.
- Nới rộng (widening) khả năng lưu trữ: việc ép kiểu này sẽ làm chuyển đổi dữ liệu từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu dữ liệu có kích thước lớn hơn. Điều này không làm mất đi giá trị của dữ liệu. Ví dụ ban đầu bạn có một biến kiểu int, có giá trị là 6, bạn ép dữ liệu từ kiểu int sang float, rồi gán vào biến mới float, thì biến mới float sẽ mang giá trị 6.0. Việc ép kiểu theo dạng này thông thường người ta cứ để cho hệ thống thực hiện một cách ngầm định. Bạn sẽ hiểu ngầm định là gì ở bên dưới
- Thu hẹp (narrowwing) khả năng lưu trữ: việc ép kiểu này sẽ làm chuyển đổi dữ liệu từ kiểu dữ liệu có kích thướng lớn hơn sang kiểu dữ liệu có kích thước nhỏ hơn. Điều này có thể làm mất đi giá trị của dữ liệu. Ví dụ ban đầu bạn có một biến kiểu float, có giá trị là 6.5, bạn ép dữ liệu từ kiểu float sang int, rồi gán vào biến mới int, thì biến mới int sẽ mang giá trị 6. Việc ép kiểu này không thể để cho hệ thống thực hiện một cách ngầm định được, lúc này hệ thống sẽ báo lỗi, bạn phải thực hiện ép kiểu tường minh cho nó.
Như vậy, cho dễ nhớ, bạn có thể phân loại ép kiểu làm hai dạng, đó là ngầm định, và tường minh cũng được.
- Ép kiểu ngầm định: việc ép kiểu này có thể diễn ra một cách tự động bởi hệ thống, khi hệ thống biết lúc nào cần phải nới rộng khả năng lưu trữ, thì hệ thống sẽ làm. Như mình có nhắc đến ở ngay trên đây, đó là kiểu nới rộng này sẽ không làm mất đi giá trị của biến. Khi bắt đầu thực hiện ép kiểu, hệ thống sẽ kiểm tra các nguyên tắc sau, nếu thỏa sẽ ép.
+ byte có thể ép kiểu sang short, int, long, float, double.
+ short có thể ép kiểu sang int, long, float, double.
+ int có thể ép kiểu sang long, float, double.
+ long có thể ép kiểu sang float, double.
+ float có thể ép kiểu sang double.
- Ép kiểu tường minh: khi không thỏa điều kiện để có thể tự động ép kiểu, thì hệ thống sẽ báo lỗi. Việc còn lại là bạn phải chỉ định ép kiểu một cách
tường minh. Vì cách ép kiểu này có thể sẽ làm mất đi giá trị của dữ liệu, cho nên rất cần bạn đưa ra quyết định, chứ hệ thống không dám tự quyết. Bạn sẽ chỉ định
như sau khi muốn thực hiện việc ép kiểu tường minh này.
(kiểu_dữ_liệu_cần_ép) tên_biến;
Với cách ép kiểu này bạn chú ý là chúng ta không cần làm gì cả, cứ code đi rồi hệ thống sẽ tự thực hiện việc ép kiểu thôi. Bạn hãy nhìn code sau.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub byte i = 50; short j = i; int k = j; long l = k; float m = l; double n = m; System.out.println("This is a byte: " + i); System.out.println("This is a short: " + j); System.out.println("This is a int: " + k); System.out.println("This is a long: " + l); System.out.println("This is a float: " + m); System.out.println("This is a double: " + n); } }
Bạn thấy với i ban đầu được khai báo là kiểu byte với giá trị 50, sau phép gán cho biến j (giá trị là short) thì giá trị 50 trong biểu thức này được chuyển tự động thành kiểu giữ liệu short cao hơn và gán vào biến j. Tương tự cho các phép gán vào k, l, m, n. Kết quả in ra như hình sau.
Nhưng nếu bạn thử trắc nghiệm bằng cách kêu hệ thống ép một kiểu dữ liệu lớn hơn về kiểu nhỏ hơn xem. Hệ thống sẽ báo lỗi ngay. Và vì vậy bạn cần phải can thiệp mục kế tiếp dưới đây.
int i = 50; short j = i;
Quay lại ví dụ báo lỗi trên đây, hệ thống đã từ chối tự động ép kiểu ngầm định, vậy nếu vẫn có nhu cầu muốn ép kiểu thì bạn hãy ép kiểu tường minh cho nó, lúc này bạn cần dùng đến cấu trúc ép kiểu tường minh mà mình có đưa ra ở trên kia, như vầy.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub int i = 50; short j = (short) i; System.out.println("This is a short: " + j); } }
Bạn hãy nhìn vào dòng short j = (short) i;, dòng này sẽ ép kiểu giá trị của i về short và gán cho biến j. Việc bạn chỉ định ép kiểu với (short) nhìn vào là biết ý đồ ngay, vì vậy mới có cái tên là tường minh.
Bạn hãy xem một ví dụ nữa thực tế hơn, có một số trường hợp bạn muốn bỏ dấu thập phân của một kiểu số thực, cách nhanh nhất để làm điều này là ép kiểu số thực này về một kiểu số nguyên. Cách ép kiểu này thực chất là làm mất giá trị của biến một cách... cố tình.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub double d = 7.5; int i = (int) d; System.out.println("This is a int: " + i); System.out.println("This is a double: " + d); } }
Kết quả của việc ép kiểu này được in ra như sau, bạn chú ý dòng in ra kiểu int nhé, mất tiêu phần thập phân rồi.
Vì kiến thức về ép kiểu ngắn quá, nên mình nói luôn về Comment ở bài này. Hai kiến thức về ép kiểu và comment nó không ăn nhập gì với nhau hết, nhưng nó sẽ giúp bổ trợ tốt cho bạn trong quá trình code đấy.
Sở dĩ mình dùng từ comment chứ không dịch sang tiếng Việt là vì để tránh hiểu lầm thôi, vì đa số các bạn đều biết comment nghĩa là "bình luận", nhưng thực ra mục đích của comment trong việc code lại chính là "ghi chú" hay "chú thích", nó giúp bạn giải nghĩa cho một số dòng code, bạn có thể comment thoải mái ở bất kỳ đâu trong source code, trình biên dịch sẽ bỏ qua các comment này khi build, do đó sẽ không có lỗi xảy ra với chúng.
Bạn có 3 cách comment như sau.
- // text - Khi trình biên dịch gặp ký hiệu //, nó sẽ bỏ qua tất cả các ký tự từ // đến ký tự cuối cùng của dòng đó. Như vậy với mỗi // sẽ giúp bạn comment được 1 dòng.
- /* text */ - Khi trình biên dịch gặp ký hiệu /*, nó sẽ bỏ qua tất cả các ký tự từ /* đến hết */. Như vậy cách comment này có thể giúp bạn comment được nhiều dòng cùng lúc.
- /** document */ - Tương tự như trên nhưng bạn chú ý có hai dấu * khi bắt đầu, cách comment này giúp trích xuất ra các tài liệu hướng dẫn. Người ta gọi là document. Cách comment này sẽ được mình nói rõ hơn ở một bài viết khác nhé.
Bạn hãy nhìn vào ví dụ "3 trong 1" sau đây, mình đã gộp cả ba cách comment được nói ở trên vào một chương trình. Thực ra thì bạn không cần phải comment quá nhiều như ví dụ, bạn nên comment khi cần thiết phải giải nghĩa cho một dòng code lạ nào đó, hoặc ghi chú tác giả dòng code đó là bạn, hoặc ngày sửa chữa dòng code này,...
public class MyFirstClass { /** * Kiểu comment cho document, sẽ được nói đến sau * * @param args */ public static void main(String args) { // Comment một dòng // Muốn dòng thứ 2 thì như thế này double d = 7.5; int i = (int) d; // Cast the double into int /* * Các câu lệnh bên dưới sẽ in ra console, * và có thể comment nhiều dòng như sau * một dòng nữa, chỉ cần enter mà thôi */ System.out.println("This is a int: " + i); System.out.println("This is a double: " + d); } }
Bạn vừa xem qua các cách thức ép kiểu trong Java, và các cách thức comment source code. Bài học này tuy nhẹ nhưng rất quan trọng nhé, bạn sẽ sử dụng việc ép kiểu và thực hiện việc comment thường xuyên trong các project của mình.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Thực ra bài hôm nay mình sẽ nói hơi xa hơn kiến thức của các bạn đã làm quen từ các bài học trước, kiến thức hôm nay có liên quan nhiều đến OOP (hướng đối tượng) mà bạn sẽ học ở các bài sau.
Ồ đừng vội nản nhé. Bạn có thể xem bài hôm nay sẽ hướng dẫn bạn sử dụng một "công cụ" để các bạn tiến hành nhập/xuất thông qua Console. Công cụ này giúp cho các bạn sau khi nhập dữ liệu vào từ bàn phím, nó sẽ lấy dữ liệu được nhập này chuyển vào chương trình của bạn, để chương trình xử lý, rồi xuất ra kết quả thông qua màn hình Console.
Sau bài học hôm nay thì các bạn có thể test thoải mái các dòng code của mình, để hỗ trợ tốt vào quá trình học Java của bạn. Nhưng trước hết, chúng ta hãy cùng tìm hiểu sâu hơn về khái niệm Console này.
Đầu tiên bạn có thể hiểu Console như là một bảng điều khiển chuyên dụng, như các Game Console chính là các thiết bị chuyên cho việc điều khiển game, hay các trang web quản lý như quản lý nhân viên cũng có thể coi là các Console chuyên dụng cho việc quản lý.
Trong ngôn ngữ lập trình, Console được biết đến như là một cách điều khiển ứng dụng đơn thuần nhất thông qua các dòng text (dòng lệnh), nó được phân biệt với điều khiển bằng UI.
À sẵn tiện mình muốn nói rõ luôn, đó là tất cả các bài học trong chương trình học Java của chúng ta sẽ dựa trên việc điều khiển bằng Console, do đó nó sẽ khác với các ứng dụng Java có giao diện cụ thể bạn nhé, các bài học chủ yếu mang đến cho bạn kiến thức làm thế nào sử dụng ngôn ngữ
Java (để có thể lập trình ứng dụng Android), hơn là lập trình ứng dụng Java nên
sẽ không có giao diện hoành tráng. Bạn nên nhớ điều này.
Và Console trong chương trình học của chúng ta là đây, ở các bài làm quen sơ sơ trước, khi bạn thực thi chương trình thì cũng đã biết tới cửa sổ Console này rồi đúng không nào.
Chúng ta nói đến các cách thức để bạn nhập dữ liệu từ Console trước. Để bắt đầu, bạn hãy mở Eclipse lên. Đầu tiên mình muốn bạn thử khai báo một đối tượng Scanner như code bên dưới. Bạn code đi rồi mình sẽ nói rõ hơn lý do tại sao phải code như vậy ở bên dưới dòng code này.
Scanner scanner = new Scanner(System.in);
Các khái niệm lớp (class) hay đối tượng gì gì đó thì bạn sẽ được làm quen sau này, nó thuộc về kiến thức OOP như mình có nói đến ở đầu bài học. Nhưng bạn có thể hiểu trước là đối tượng cũng giống như biến vậy, bạn có thể đặt tên cho nó tùy ý (theo quy tắc đặt tên của biến). Như ở trên chúng ta đặt tên cho đối tượng của Scanner là scanner. Chỉ khác biến ở chỗ là đối tượng của lớp phải được khởi tạo bằng từ khóa new như trên. Khi đó tổng thể code của bạn như sau.
public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub Scanner scanner = new Scanner(System.in); } }
Mục này cho phép mình nói lan man về kinh nghiệm code một tí xíu. Đó là nhiều bạn sau khi gõ xong dòng code trên đây, có thể sẽ có báo lỗi từ hệ thống như hình sau.
Báo lỗi là vì hệ thống đang không biết lớp Scanner là gì. :D . Đó là bởi vì trước đây bạn chỉ sử dụng các biến nguyên thủy, hoặc các lớp nằm trong package java.lang, mà chúng ta không cần phải quan tâm. Chà vậy chúng ta cần quan tâm gì? Thực ra trong Java, mỗi khi bạn code, bạn phải chỉ định nguồn gốc của các lớp bạn sẽ dùng, thông qua từ khóa import để ở đầu file, theo sau import là package có chứa lớp cần dùng. Bạn có thể xem thêm kiến thức về package ở đây.
Theo đó, ở ví dụ trên chúng ta cần đến lớp Scanner. Do đó chúng ta cần import đúng package mà hệ thống muốn.
Để import đúng package có lớp Scanner cần dùng trên đây là một điều cực kỳ dễ dàng, có nhiều cách để thực hiện điều này, bạn hãy chọn một trong những cách sau.
Crl + Space
Tổ hợp phím thần thánh này ai lập trình cũng phải biết, khi bạn đang gõ tên một lớp nào đó của hệ thống mà muốn chúng được tự động điền các chữ còn lại, và tất nhiên với Eclipse tổ hợp phím này cũng sẽ khuyến mãi cho bạn việc import package của đối tượng đó một cách tự động luôn.
Giả sử bạn đang gõ dòng code khai báo Scanner trên đây, thay vì gõ hết tất tần tật như code mẫu trên, bạn thử gõ vài chữ rồi nhấn Ctr + Space, bạn sẽ thấy gợi ý từ hệ thống, giống như khi bạn nhấn dấu (.) ở các dòng code đầu tiên bạn đã làm quen vậy.
Vậy với vệt sáng đang ở dòng đầu tiên Scanner, bạn nhấn Enter, kết quả dòng code được tự động hoàn thành và hệ thống cũng import sẵn package java.util.Scanner cho bạn mà bạn không cần phải nhớ chúng là ai và từ đâu đến đúng không nào. Bạn thử gõ hết câu lệnh trên nhưng lần này siêng nhấn Ctr + Space ở mỗi từ (scanner, new, Scanner, System) xem sao nhé. Kết quả sẽ không còn lỗi nữa.
cứ để ý nếu thanh bên trái của editor xuất hiện hình cái bóng đèn cùng với dấu X màu đỏ, là khi Eclipse đã xác định ra lỗi và biết cách sửa lỗi cho bạn rồi, bạn chỉ cần click chuột vào bóng đèn này sẽ thấy các gợi ý sửa lỗi như hình sau.
Còn chờ gì nữa mà không chọn Import 'Scanner' (java.util), điều này có nghĩa là Eclipse đã hỏi bạn cho phép import package java.util.Scanner hay không rồi đó.
Import Thủ Công
Tất nhiên cách này dành cho bạn nào biết chính xác lớp này nằm trong package nào rồi nên bạn có thể gõ import một cách thủ công.
Lan man hơi dài rồi, quay trở lại với việc bạn vừa khai báo đối tượng của lớp Scanner trên kia. Dòng tiếp theo bạn chỉ cần gọi scanner.nextXxx(); thì khi bạn chạy chương trình, khi hệ thống thực thi đến dòng này, nó sẽ dừng lại chờ, và lúc đó Console sẽ xuất hiện con nháy chờ người dùng nhập vào một giá trị có kiểu dữ liệu là Xxx rồi mới tiến hành gán giá trị này vào biến tương ứng và thực hiện tiếp các câu lệnh bên dưới.
Bạn hãy thử vài ví dụ sau cho từng scanner.nextXxx(); cụ thể nhé.
Với ví dụ này bạn thử cho người dùng nhập vào tên của họ từ Console rồi in ra dòng xin chào ngay trên Console như sau.
Để đợi người dùng nhập vào tên bạn gõ dòng sau vào sau khi khai báo scanner.
String name = scanner.nextLine();
Khi đó tên người dùng nhập vào từ Console sẽ được gán vào biến kiểu String (đây là kiểu chuỗi mà bạn sẽ được làm quen sau) có tên là name. Nhưng để dễ dàng hơn cho người dùng, chúng ta nên có các dòng System.out để in ra chỉ dẫn cho người dùng, và in ra dòng chào hỏi cuối chương trình. Code tổng thể của chúng ta như sau.
import java.util.Scanner;; public class MyFirstClass { public static void main(String args) { // TODO Auto-generated method stub Scanner scanner = new Scanner(System.in); System.out.println("Please enter your name here: "); String name = scanner.nextLine();
System.out.println("Hello! " + name); } }
Khi này nếu bạn chạy chương trình, chỉ có dòng text "Please enter your name here: " xuất hiện, đừng tắt cửa sổ Console nhé, hãy tiếp tục nhập vào một text, sau khi Enter bạn sẽ nhận được một text nữa in ra với nội dung "Hello!" và text bạn vừa nhập, text in ra đó chính là nội dung biến name được scanner lấy dữ liệu từ Console rồi gán vào đấy.
Scanner hỗ trợ nhập cho tất cả các kiểu dữ liệu nguyên thủy, ví dụ này nhập vào kiểu float và bạn hoàn toàn có thể áp dụng cho các kiểu dữ liệu nguyên thủy còn
lại.
System.out.println("How about your salary? "); float salary = scanner.nextFloat(); System.out.println("Your salary is: " + salary);
Chắc bạn cũng biết, để xuất dữ liệu ra Console thì cứ gọi System.out.println()
thôi chứ gì. Dễ quá, các bài trước và bài hôm nay bạn đã làm quen rồi. Nhưng kiến thức về xuất dữ liệu ra Consolse còn vài thứ vui vẻ nữa, mình xin được liệt kê vắn tắt như sau.
- Bạn có thể dùng câu lệnh print() thay vì println(). Nếu như với println() thì các bạn đã biết nó giúp xuất dữ liệu ra rồi kèm theo xuống hàng sau khi xuất. Thì print() chỉ xuất dữ liệu thôi và không xuống hàng. Bạn có thể thử áp dụng print() cho các thông báo chỉ dẫn như "Please enter your name here: " trên kia để xem sự khác biệt nhé.
- Nếu bạn muốn hiển thị các ký tự đặc biệt sau: dấu nháy đơn ('), dấu nháy kép ("), và dấu gạch chéo (\). Thì bạn cứ kèm theo một gạch chéo (\) nữa ở trước các ký tự này. Chẳng hạn bạn muốn hiển thị dòng Thư mục chứa file "ấy" là C:\Location\Ay, thì bạn gõ lệnh xất như sau System.out.print("Thư mục chứa file \"ấy\" là C:\\Location\\Ay");.
- Ngoài ra bạn còn có thể chèn thêm các ký tự giúp định dạng dữ liệu xuất, như "\t" sẽ chèn thêm một tab, hay "\n" sẽ giúp xuống dòng. Bạn thử tự kiểm chứng bằng cách gõ xòng lệnh này nhé System.out.print("\tHello\nWorld");.
Chúng ta vừa thực hành các câu lệnh nhập/xuất đáng giá từ console, hãy xem chúng giúp ích gì cho bạn ở các bài học kế tiếp nhé.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Từ bài học hôm nay chúng ta đã có thể vận dụng tốt kiến thức về nhập/xuất trên console rồi nhé.
Và bài học hôm nay chúng ta sẽ nói đến các Câu Lệnh Điều Khiển Luồng (Control Flow). Mình cũng xin nói trước là vì các câu lệnh 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é.
Để 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 Eclipse 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.
Thực ra thì đến khi bạn cần một giải thuật phức tạp hơn cho ứng dụng, 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).
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 đó) 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.
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.
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.
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é.
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, 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. Nghe quen mà lạ đúng không bạn, vì chắc chắn bạn đã biết đến biến rồi, nhiệm vụ hôm nay của bạn là biết về phạm vi của nó nữa là chuẩn.
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ó.
Với phạm vi local, là phạm vi mà biến đó chỉ hoạt độ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.
Còn với phạm vi global, là phạm vi mà biến đó được khai báo ở khối lệnh bên ngoài, khi đó các khối lệnh bên trong khối lệnh đó cũng 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ả.
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 xài thoải mái mà không bị lỗi.
Đượ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).
Xong kiến thức râu ria rồi, chúng ta 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 câu lệnh dạng này mà bạn phải nắm, đó là: if, if else, if else if, và ?:. Chúng ta bắt đầu làm quen với từng câu lệnh như sau.
Cú pháp cho câu lệnh if.
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 lại.
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 rất đơn giản, nếu biến age 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 chỉ có một dòng in ra console câu thông báo chưa đủ tuổi, dòng code này 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à }, tức khối lệnh mặc định chỉ có 1 dòng, khi đó câu lệnh if trên sẽ như sau.
if (age < 18) System.out.printf("You can not access");
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ú 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 { các_câu_lệnh_n; }
Đây là dạng mở rộng hơn của câu lệnh if else. Khi đó biểu_thức_điều_kiện_1 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_1 là true. Nếu biểu_thức_điều_kiện_1 trả về false, hệ thống sẽ kiểm tra tiếp biểu_thức_điều_kiện_2, các_câu_lệnh_2 được thực thi trong trường hợp biểu_thức_điều_kiện_2 là true. Nếu bieu_thuc_dieu_kien_2 trả về false, mọi thứ sẽ được kiểm tra tiếp tục... cho đến khi không còn thỏa bất kỳ biểu_thức_điều_kiện nào cả (không thằng nào trả về true) thì các_câu_lệnh_n 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. Câu lệnh này còn được gọi là Toán Tử Tam Nguyên, vì 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, boolean, 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 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; ... default: các_câu_lệnh_n; 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ý.
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 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.
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ạn có thể theo dõi trực tiếp bài học ở link gốc này.
Bài trước chúng ta đã làm quen với phần một của các Câu Lệnh Điều Khiển Luồng (Control Flow), đó là các Câu Lệnh Điều Kiện (hay Câu Lệnh Rẽ Nhánh). Bài hôm nay chúng ta đi tiếp phần còn lại đó là các Câu Lệnh Lặp.
Lặp (tiếng Anh gọi là Loop) trong lập trình là một hành động lặp đi lặp lại một khối lệnh nào đó khi mà một điều kiện nào đó còn thỏa (thỏa - hay còn hiểu là kết quả của biểu thức đó là true). Nếu như với các Câu Lệnh Điều Kiện giúp bạn rẽ nhánh các dòng code, thì các Câu Lệnh Lặp bài này lại giúp bạn lặp lại các dòng code nào đó.
Mình ví dụ có một yêu cầu bắt bạn in ra màn hình 1000 con số từ 1 đến 1000, chẳng lẽ bạn lại gọi 1000 lần câu lệnh System.out.println()?
Ví dụ thực tế hơn, nếu như có yêu cầu muốn bạn in ra tên tất cả sinh viên của trường bạn (giả sử bạn đã biết câu lệnh đọc một thông tin sinh viên lên từ cơ sở dữ liệu), chẳng lẽ bạn lại viết code đọc dữ liệu của từng sinh viên và in ra màn hình?
Các ví dụ trên cho chúng ta thấy khái niệm thực tế rõ ràng và sự cần thiết khi sử dụng đến các Câu Lệnh Lặp trong bài hôm nay.
Chúng ta có 3 loại câu lệnh lặp cần làm rõ trong bài hôm nay, đó là: while, do while và for.
Cú pháp cho câu lệnh while như sau.
while (điều_kiện_lặp) { các_câu_lệnh; }
Cú pháp của câu lệnh while khá đơn giản, ban đầu chương trình sẽ kiểm tra điều_kiện_lặp, nếu điều kiện này trả về kết quả true, thì các_câu_lệnh sẽ được thực thi, rồi sau đó chương trình sẽ lại kiểm tra điều_kiện_lặp. Vòng lặp while chỉ được kết thúc khi điều_kiện_lặp trả về kết quả false.
Ví dụ.
int i = 0; while (i < 10) { System.out.println("Hello!"); i++; }
Bạn thấy ví dụ trên đây phải khởi tạo biến i (người ta gọi đây là biến đếm, vì đây là biến dùng để điều khiển số lần lặp của vòng while). Bắt đầu vào while, bạn thấy điều_kiện_lặp được đưa ra là nếu biến i còn nhỏ hơn 10 thì các_câu_lệnh bên trong được thực hiện, ở ví dụ này chỉ là hàm in ra màn hình câu chào "Hello!". Bạn chú ý một điều, trong thân hàm while bạn luôn phải thay đổi giá trị của biến đếm, trong ví dụ này i++; giúp tăng biến đếm lên 1 đơn vị, để sao cho đến một lúc nào đó điều_kiện_lặp phải bị phá vỡ, trường hợp này là i bằng 10, thì hàm while mới kết thúc.
Nếu lấy can đảm bỏ dòng i++; ở ví dụ trên đi rồi chạy lại, bạn sẽ thấy dòng in ra màn hình được gọi mãi mãi, người ta gọi trường hợp này là lặp vô tận.
Bạn thử áp dụng vòng lặp while để thực hiện yêu cầu sau: hãy in ra console tổng các số chẵn từ dãy số nguyên có độ lớn từ 1 đến 10.
Lưu ý, kiểm tra một số chẵn bằng cách thực hiện phép chia dư số đó với 2, nếu kết quả phép chia dư là 0 thì đó là số chẵn.
Bạn hãy thử code, rồi so sánh với kết quả sau nhé.
int i = 1; int sumEven = 0; while (i <= 10) { if (i % 2 == 0) sumEven += i; i++; } System.out.println("Sum: " + sumEven);
Hãy dùng vòng lặp while để tìm ra các số nguyên tố trong dãy số nguyên từ 1 đến 100 và in chúng ra console.
Lưu ý số nguyên tố là các số chỉ chia hết cho 1 và chính nó. Ví dụ như số 2, 3, 5, 7, 11, 13,...
Bạn hãy thử code trước rồi so sánh với kết quả sau nhé.
int number = 1; // Các số tăng dần từ 1 đến 100 để kiểm tra while (number <= 100) { int count = 0; // Đếm số lượng số mà number chia hết, // luôn phải khởi tạo là 0 int j = 1; // Biến chạy từ 1 đến number để kiểm tra while (j <= number) { if (number % j == 0) {
// Tìm thấy một số mà number chia hết, // tăng biến đếm lên 1 để đếm count++; } j++; // Nhớ dòng này } if (count == 2) { // Nếu count là 2, tức là số đó chỉ chia hết // cho 2 số là 1 và chính nó System.out.println(number); }
number++; // Nhớ tăng number để kiểm tra số tiếp theo
}
Cú pháp cho câu lệnh do while như sau.
do { các_câu_lệnh; } while (điều_kiện_lặp);
Bạn có để ý thấy là cú pháp do while khác với while chỗ nào không? Đó là nếu với while, hệ thống phải kiểm tra điều_kiện_lặp trước, nếu thỏa thì mới thực hiện các_câu_lệnh. Còn với do while, hệ thống sẽ thực hiện các_câu_lệnh trước rồi mới kiểm tra điều_kiện_lặp xem có cần thực hiện việc lặp lại hay không.
Như vậy với do while thì các_câu_lệnh được thực hiện ít nhất 1 lần, còn với while thì các_câu_lệnh có thể sẽ không được thực hiện bao giờ nếu điều_kiện_lặp không thỏa.
do while sẽ ít khi được dùng hơn là while. Bạn không cần phải xem ví dụ cho do while, hãy bắt tay vào thử vài bài tập như sau.
Bạn hãy in ra console thông báo kêu người dùng nhập vào một con số từ console, rồi sau đó cho biết các thứ trong tuần tương ứng. Với 1 thì in ra "Monday",... 7 in ra "Sunday". Chương trình sẽ hỏi người dùng nhập số hoài cho đến khi họ nhập vào một số không phải giá trị từ 1 đến 7.
Code tham khảo như sau.
Scanner scanner = new Scanner(System.in); int getNumber = 0; do { System.out.print("Enter a number: "); getNumber = scanner.nextInt();
switch (getNumber) {
case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; case 4: System.out.println("Thursday"); break; case 5: System.out.println("Friday"); break; case 6: System.out.println("Saturday"); break; case 7: System.out.println("Sunday"); break; default: System.out.println("Invalid number!"); break; } } while (getNumber >= 1 && getNumber <= 7);
Cú pháp cho câu lệnh for như sau.
for (khởi_tạo_biến_đếm; điều_kiện_lặp; điều_chỉnh_biến_đếm) { các_câu_lệnh; }
Câu lệnh for sẽ lặp các_câu_lệnh bên trong nó dựa trên việc kiểm tra 3 thành
phần truyền vào, các thành phần này được phân cách bởi các dấu ;. Trong đó.
- khởi_tạo_biến_đếm cũng giống như bạn khai báo biến và khởi tạo (gán cho nó một giá trị) mà bạn đã học ở bài 4. biến_đếm này sẽ là căn cứ để chương trình lặp lại dòng code của bạn bao nhiêu lần.
- điều_kiện_lặp là điều kiện đặt ra cho vòng lặp kiểm tra xem có nên lặp tiếp một vòng các_câu_lệnh nữa hay không.
- điều_chỉnh_biến_đếm giúp thay đổi giá trị của biến_đếm mỗi khi các_câu_lệnh được thực hiện xong một lần lặp, tại sao? Vì cũng giống như các câu lệnh lặp trên đây, nếu không có việc điều chỉnh lại biến đếm, thì các biến đếm sẽ không bao giờ bị thay đổi giá trị, và điều_kiện_lặp sẽ luôn luôn trả về cùng một giá trị, và vì vậy vòng lặp có thể sẽ bị lặp vô tận, hoặc sẽ không thực hiện bất kỳ lần lặp nào.
Bạn sẽ hiểu thêm câu lệnh for qua ví dụ sau đây.
for (int i = 0; i < 10; i++) { System.out.println("Hello!"); }
Ví dụ trên rất đơn giản, khởi_tạo_biến_đếm là khai báo và khởi tạo cho biến i giá trị ban đầu là 0. điều_kiện_lặp sẽ lặp hoài khi mà biến i còn nhỏ hơn 10. điều_chỉnh_biến_đếm sẽ tăng i lên 1 đơn vị sau khi thực hiện các_câu_lệnh. các_câu_lệnh trong vòng for này chỉ là hàm in ra console dòng chữ "Hello!". Vậy bạn thử nghĩ xem có bao nhiêu dòng "Hello!" được in ra màn hình ở ví dụ trên?
Bạn hãy viết lại Bài Thực Hành Câu Lệnh while - 1 trên kia bằng vòng lặp for nhé.
int sumEven = 0; for(int i = 1; i <= 10; i++) { if (i % 2 == 0) sumEven += i; }
System.out.println("Sum: " + sumEven);
Bạn có thể thấy là với for chúng ta có thể khai báo biến đếm i vào trong vòng lặp và khởi tạo cho nó trong đó, khác với while phải khởi tạo bên ngoài. Và việc tăng
biến i lên được để ở thành phần thứ 3 của vòng for chứ không nằm trong thân hàm như với while nữa.
Bạn hãy viết lại Bài Thực Hành Câu Lệnh while - 2 trên kia bằng vòng lặp for nhé.
for (int number = 1; number <= 100; number++) { int count = 0; // Đếm số lượng số mà number chia hết, // luôn phải khởi tạo là 0 for(int j = 1; j <= number; j++) { if (number % j == 0) { // Tìm thấy một số mà number chia hết, // tăng biến đếm lên 1 để đếm count++; } } if (count == 2) { // Nếu count là 2, tức là số đó chỉ chia hết // cho 2 số là 1 và chính nó
System.out.println(number); } }
Đa số các bạn sẽ gặp khó khăn khi bước đầu làm quen với for, mình cũng vậy, từ lúc bắt đầu biết đến for cho đến một thời gian về sau mình mới xài tốt for, trước đó mình thường dùng while thay cho for vì while dễ tiếp cận và dễ nhớ hơn, nhưng như bạn thấy while lại chiếm nhiều dòng code hơn, cái nào cũng có giá của nó :)
Mục mở rộng này dành cho bạn nào đã am hiểu về for rồi và muốn xem thêm for có sức mạnh nào khác không nhé.
Thứ nhất, mặc dù for được thiết kế để truyền vào 3 thành phần, nhưng bạn vẫn có thể khuyết một thành phần.
Chẳng hạn code dưới đây khuyết điều_chỉnh_biến_đếm, khi đó bạn có quyền điều khiển biến đếm bên trong thân hàm for.
for (int i = 1; i <= 10;) { System.out.println("Hello!"); i++; }
Tiếp theo, bạn có thể khuyết cả 2 thành phần nào đó, và bạn có quyền điều khiển sự khiếm khuyết này thông qua các điều khiển bên trong và ngoài for. Như ví dụ sau.
int i = 1; for (; i <= 10;) { System.out.println("Hello!"); i++; }
Dã man hơn, bạn có thể khuyết luôn 3 thành phần, người ta hay dùng kiểu vòng for này để lặp vô tận, dùng cho một số tình huống không xác định rõ khi nào cần dừng vòng lặp, lặp sẽ chạy đến điều kiện nào đó cần dừng thì có các hàm "nhảy" ra khỏi vòng lặp mà chúng ta sẽ cùng làm quen ở bài kế tiếp. Bạn hãy xem ví dụ về for vô tận này ở code bên dưới. Và nhớ, đừng thử vòng for vô tận này ở nhà.
for (;;) { System.out.println("Please don't try at home"); }
Bạn vừa cùng mình đi qua phần còn lại trong tổng thể Câu Lệnh Điều Khiển Luồng, đó là các Câu Lệnh Lặp. Bạn sẽ được nói đến một ý nhỏ liên quan đến các câu lệnh này, đó là các lệnh dùng để thoát khỏi vòng lặp trong khi điều kiện lặp vẫn còn, sẽ rất hữu ích trong nhiều trường hợp thực tế.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Mình xin giải thích một chút ý nghĩa của bài hôm nay. Bài học hôm nay đáng lý ra phải nằm ở bài 9 - Bài nói về các câu lệnh lặp - Vì kiến thức của bài hôm nay có liên quan các câu lệnh lặp đó.
Tuy nhiên mình nghĩ nếu nói về kiến thức này ở bài trước sẽ làm cho bài học bị dài ra, vừa khó để các bạn nhớ, vừa khó để thực hành, lại khó cho mình khi phải viết một bài quá dài. Chính vì vậy mình tách ra thành một bài học hôm nay.
Chúng ta sẽ nói đến 2 câu lệnh nhảy trong bài hôm nay, đó là break và continue. Trong khi có một câu lệnh nhảy khác là return sẽ được trao đổi ở bài viết về Phương Thức sau nhé.
Sở dĩ Java dùng từ "nhảy" không phải vì nó làm cho các vòng lặp của bạn thêm nhảy nhót. Nhảy ở đây là nhảy ra khỏi vòng lặp, hay nhảy đến một lần lặp tiếp theo mà bỏ qua các câu lệnh còn lại bên trong thân vòng lặp đó. Một số tài liệu khác gọi đây là Các câu lệnh điều khiển, nhưng mình thích từ nhảy hơn, tiếng Việt nghe hơi chuối nhưng tiếng Anh họ gọi là Jump Statements, nghe rõ nghĩa hơn.
Chúng ta cùng xem qua nhé.
Bạn hiểu nghĩa break là đập vỡ cũng đúng, nhưng trong tình huống này nó có nghĩa là dừng thì hay hơn.
Theo đúng tên gọi, khi câu lệnh này xuất hiện ở đâu đó trong vòng lặp, chúng sẽ
làm phá vỡ, hay dừng vòng lặp đó dù cho vẫn còn các câu lệnh khác bên trong vòng lặp chưa được xử lý.
Nếu bạn còn nhớ, ở bài 8 chúng ta cùng nói qua câu lệnh break này cho cấu trúc switch case đúng không nào. Vâng, break ở switch case và break ở câu lệnh lặp cũng có công dụng tương tự như nhau thôi.
Bài thực hành này muốn bạn in ra console tất cả các số nguyên tố từ 1 đến 10.000.
Khoan! Có gì đó sai sai! In số nguyên tố đã được thực hành ở bài trước rồi mà! Và in số nguyên tố thì có liên quan gì đến break!?!
Bạn yên tâm, in số nguyên tố ở bài trước chỉ là một giải thuật gà. Trong lập trình, đặc biệt là lập trình các thuật toán, sẽ luôn luôn hoan nghênh các bạn có những giải thuật cải tiến sao cho ứng dụng chạy nhanh, mượt. Muốn được như vậy thì các thuật toán của các bạn phải gọn, bắt máy tính làm việc ít hơn, số lượng vòng lặp giảm thiểu,... Có lẽ chúng ta sẽ nói đến chủ đề này ở bài khác.
Quay lại bài thực hành, chúng ta sẽ cải tiến giải thuật tìm số nguyên tố, kết hợp với câu lệnh break để dừng việc kiểm tra sớm một khi đã biết số đó không phải là số nguyên tố.
Bạn đã biết số nguyên tố là số chỉ chia hết cho 1 và chính nó. Để làm vậy, với cách cũ bạn duyệt qua các số hạng từ 1 đến chính nó, đếm xem có bao nhiêu số hạng mà nó chia hết cho, nếu đếm thấy 2 số hạng thì nó đúng là số nguyên tố. Với cách này bạn luôn phải cho vòng lặp chạy hết tất cả các số hạng. Cụ thể, với việc kiểm tra số 10.000, thì ứng dụng của bạn sẽ phải lặp 10.000 lần để đếm. Cách này mình đo thấy ứng dụng tốn 376 ms (mili giây) để chạy, tức là gần nửa giây. Bạn có thể áp dụng break vào việc giảm số lần lặp bằng 2 cách sau.
- Đừng cho vòng lặp chạy từ 1 đến chính nó, mà hãy cho vòng lặp chạy từ số 2 đến chính nó giảm đi 1 đơn vị, tức là chạy trong khoảng [2, chính nó). Hễ tìm được một số nào đó trong khoảng này mà chính nó chia hết, gọi lệnh break ngay để kết thúc lặp, không cần lặp nữa vì chắc chắn nó không phải số nguyên tố. Với giải thuật này thì giả sử để kiểm tra số 10.000, ứng dụng của bạn chỉ cần chạy
đến số hạng 2 là đã break rồi. Cách này tốn 167 ms để in ra hết các số nguyên tố, chỉ tốn một nửa thời gian so với cách thứ nhất. Ôi cool quá!
- Cách này hay hơn nữa, theo nghiên cứu (chưa rõ từ nguồn nào) thì bạn chỉ cần kiểm tra trong khoảng 2 đến căn bậc hai của chính nó, tức là cho vòng lặp chạy trong khoảng [2, căn bậc 2 chính nó]. Nếu tìm thấy một số trong khoảng này mà nó chia hết cho, thì nó không phải là số nguyên tố. Trong Java, hàm lấy căn bậc hai một số là Math.sqrt(số_thực);. Giải thuật thứ ba này chỉ mất có 15 ms để chạy thôi nhé, nhanh gấp 25 lần so với cách thứ nhất.
Mình áp dụng cách chạy nhanh nhất trên đây vào code bên dưới, bạn áp dụng cách nào? Hãy thử code nhé.
for (int number = 2; number <= 10000; number++) { boolean isPrime = true; // Thay vì biến count, dùng // biến này để kiểm tra // số nguyên tố for(int j = 2; j <= Math.sqrt(number); j++) { if (number % j == 0) { // Chỉ cần một giá trị được tìm thấy trong // khoảng này, thì number không phải // số nguyên tố isPrime = false; break; // Thoát ngay và luôn khỏi for // (vòng for bên ngoài vẫn chạy) } } if (isPrime) { // Nếu isPrime còn giữ được sự // "trong trắng" đến cùng thì đó là // số nguyên tố System.out.println(number); }
}
Lại một lần nữa nghĩa và công dụng của từ này bị lẫn lộn. Bạn giỏi tiếng Anh nên hiểu nghĩa của continue là tiếp tục cũng không sai, nhưng tình huống này chúng ta hiểu là bỏ qua.
Bỏ qua có nghĩa là bỏ các câu lệnh còn lại bên trong vòng lặp để thực hiện một chu trình lặp mới. Câu lệnh này không làm dừng vòng lặp như break, mà chỉ thực hiện vòng lặp mới, vòng lặp có thể bị dừng bởi continue khi mà việc thực hiện vòng lặp mới sẽ kiểm tra và thấy điều_kiện_lặp không còn thỏa mà thôi.
Có một lưu ý nhỏ khi bạn dùng continue cho for và while/do while như sau. Bởi vì continue sẽ tự thực hiện một vòng lặp mới nên có khả năng biến đếm sẽ bị bỏ qua nếu bạn để câu lệnh tăng biến đếm ở sau continue, như vậy sẽ có trường hợp bạn bị rơi vào vòng lặp vô tận, điều này dễ gặp ở while và do while. Nhưng với for thì bạn yên tâm, câu lệnh continue ở vòng lặp này bao gồm việc tăng biến đếm (nếu bạn có định nghĩa việc tăng biến đếm này ở thành phần thứ ba của for) rồi mới thực hiện vòng lặp mới nên dùng continue trong for sẽ an toàn hơn nhé.
Bài này bạn sẽ phải in ra console ngược lại với bài thực hành ở trên, kết hợp với continue. Bạn hãy viết chương trình in ra console tất cả các số KHÔNG PHẢI LÀ SỐ NGUYÊN TỐ trong khoảng từ 1 đến 100.
Chúng ta sẽ áp dụng giải thuật tìm số nguyên tố, hễ biết đó là số nguyên tố thì sẽ dùng continue "lướt" qua lần lặp mới, các câu lệnh in ra console sẽ để bên dưới câu lệnh continue để đảm bảo in ra những gì không thỏa điều kiện của continue. Code chúng ta như sau.
for (int number = 1; number <= 100; number++) { if (number == 1) { // 1 không phải số nguyên tố, in ra rồi biến System.out.println(number);
continue;
} boolean isPrime = true; // Biến kiểm tra số nguyên tố for(int j = 2; j <= Math.sqrt(number); j++) {
if (number % j == 0) { // Chỉ cần một giá trị được tìm thấy trong // khoảng này, thì number không phải // số nguyên tố isPrime = false; break; // Thoát ngay và luôn khỏi for // (vòng for bên ngoài vẫn chạy) } } if (isPrime) { // Nếu isPrime cho biết đây là số nguyên tố // bỏ qua câu lệnh dưới đây mà qua lần lặp kế continue; } System.out.println(number); }
Bạn vừa cùng mình đi qua các câu lệnh còn lại có ảnh hưởng đến cấu trúc lặp mà các bạn vừa học qua. Bài kế tiếp chúng ta cùng nhau tìm hiểu về Mảng trong Java.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Bài hôm nay sẽ tập trung nói về Mảng trong Java.
Đầu tiên mình nói một chút về Mảng trước khi đi vào chi tiết. Có một số tài liệu hoặc chương trình học Java nói rất muộn về Mảng, thường thì họ dành để nói về lập trình hướng đối tượng (OOP) trước khi nói về Mảng. Theo mình nghĩ thì điều này hợp lý, vì trong Java, Mảng không phải là kiểu dữ liệu nguyên thủy, mảng là một cấu trúc dữ liệu mới dựa trên các nền tảng của hướng đối tượng, vì vậy, biết về hướng đối tượng rồi mới tới Mảng thì không sai tí nào.
Tuy nhiên để hiểu và sử dụng tốt Mảng trong Java, mình không nghĩ bạn phải cần một kiến thức sâu rộng về OOP. Vả lại Mảng rất hiệu quả, mà nếu sử dụng Mảng trễ quá về sau, mình e rằng bạn sẽ đánh mất một số cơ hội tốt để tiếp cận một số kiến thức thú vị của Java. Thêm nữa Mảng cũng có thể xem như nền tảng để tạo thành khái niệm Chuỗi (String) mà chúng ta cùng nói đến ở bài sau nữa, một lần nữa String cũng là một khái niệm của OOP mà chúng ta cũng sẽ phải làm quen sớm.
Mảng, hay còn gọi là Array, là một cấu trúc dữ liệu, dùng để chứa một tập hợp các phần tử có kiểu dữ liệu tương tự nhau. Giả sử chúng ta có mảng các số nguyên, mảng này sẽ chứa tập hợp các phần tử có cùng kiểu dữ liệu là int. Ngoài tập hợp số nguyên như ví dụ vừa nêu, hay tập hợp các kiểu dữ liệu nguyên thủy mà bạn cũng dần được làm quen, Mảng còn chứa đựng tập hợp các dữ liệu "không nguyên thủy" mà chúng ta sẽ nói đến ở các bài sau nữa.
Khi bạn khai báo một mảng, bạn phải chỉ định độ lớn cho nó, tức là chỉ định số phần tử tối đa mà mảng đó có thể chứa. Mỗi một phần tử trong mảng sẽ được quản lý theo Chỉ Số (Index), chỉ số sẽ bắt đầu bằng số 0 (bạn sẽ được làm
quen với cách truy cập các phần tử mảng dựa vào chỉ số ở các ví dụ phía dưới).
Các bạn chưa cần biết gì nhiều, hãy thư giãn, để đầu óc nhẹ nhàng tiếp cận vào Mảng. Giả sử mình muốn tạo một Mảng có tối đa 10 phần tử kiểu int, và khi lưu trữ mảng này vào bộ nhớ, chúng sẽ được sắp xếp gần nhau theo mô hình giả lập sau, mỗi ô là một phần tử kiểu int với các số trong đó là giá trị được lưu trữ vào.
Bạn đã rõ hơn về Mảng, vậy tại sao phải dùng cấu trúc này?
Ý nghĩa của phần này muốn nói đến lợi ích của việc dùng Mảng.
Đúng như tên gọi và định nghĩa Mảng trên đây, cấu trúc dữ liệu Mảng sẽ giúp bạn lưu trữ danh sách các phần tử, mình ví dụ như bạn sẽ cần lưu danh sách các sinh viên. Danh sách này sẽ được tổ chức sao cho bạn có thể truy xuất ngẫu nhiên nhanh chóng đến từng phần tử con, ví dụ như bạn muốn lấy thông tin sinh viên ở vị trí thứ 10. Pùm! Có ngay!. Danh sách này có thể được sắp xếp lại trật tự theo một tiêu chí nào đó, do đó bạn có thể nhanh chóng đọc ra top 10 sinh viên có điểm số cao nhất. Hay một lợi thế nữa của Mảng đó là với việc tổ chức các phần tử có kiểu dữ liệu tương tự nhau như vậy sẽ làm cho code của chúng ta tường minh và dễ quản lý hơn, giúp tối ưu code.
Tuy nhiên Mảng cũng có một bất lợi là bạn phải khai báo độ lớn, tức khai báo sẵn số lượng phần tử mà Mảng này sẽ sử dụng. Điều này làm code của bạn kém linh động vì có khi chúng ta muốn một mảng nhiều số lượng phần tử hơn, hoặc làm hiệu năng của hệ thống bị giảm do bạn khai báo sẵn một mảng với độ lớn quá
cao mà không sử dụng hết số lượng vùng nhớ đã khai báo. Tất nhiên để khắc phục nhược điểm này của Mảng thì Java cũng có khái niệm ArrayList mà chúng ta sẽ làm quen ở một bài học khác.
Cũng giống như khi bạn làm quen đến khái niệm biến, bạn phải biết cách khai báo, gán dữ liệu, sử dụng biến đó trong biểu thức. Với Mảng cũng vậy, nhưng bởi vì Mảng hơi đặc biệt là nó chứa đựng các phần tử con, cho nên ngoài những gì bạn có thể vận dùng từ việc sử dụng biến, sẽ có một vài cái mới ở đây mà bạn phải làm quen. Chúng ta cùng đi qua các bước trong việc sử dụng Mảng như sau nhé.
Bạn có thể chọn một trong hai cách khai báo sau.
kiểu_dữ_liệu[] tên_mảng;
hoặc
kiểu_dữ_liệu tên_mảng[];
Bạn nhớ là phải có cặp [] ở phần kiểu_dữ_liệu hoặc tên_mảng. Sở dĩ Java định nghĩa cả 2 cách khai báo như trên là vì cách thứ nhất là đúng chuẩn Java, trong khi cách thứ 2 thì Java hỗ trợ cách khai báo Mảng từ C/C++.
Cặp [] cũng thể hiện rằng đây là mảng, bạn có để ý thấy là nếu không có cặp ngoặc đó thì khai báo trên đây không khác gì việc khai báo một biến cả đúng không nào.
Uhm... Nhưng mà khác với biến ở chỗ Mảng ở bước khai báo này vẫn chưa sử dụng được, như đã nói, chúng ta cần chỉ định độ lớn của mảng, vậy thì cùng qua bước sau nhé.
Bây giờ là một ví dụ. Để khai báo một mảng các số nguyên, chúng ta code như sau.
int[] myArray;
Lại một lần nữa bạn phải dùng từ khóa new để cấp phát bộ nhớ. Lần trước bạn dùng đến new là ở Bài 7, khi đó bạn khởi tạo một biến scanner, bạn có nhớ không nào.
Với Mảng, việc cấp phát bộ nhớ cũng sẽ tương tự, như sau.
tên_mảng = new kiểu_dữ_liệu[kích_cỡ_mảng];
Ở bước này rất cần thiết phải có kích_cỡ_mảng, nếu không truyền vào tham số này, việc cấp phát sẽ phát sinh lỗi. Kích cỡ này là do bạn quyết định, thông thường trong một ứng dụng, nếu biết chính xác thông số kích cỡ này và kích cỡ này sẽ không bị thay đổi trong suốt quá trình sống của ứng dụng, thì bạn hãy sử dụng mảng, nếu không biết chính xác hoặc kích cỡ luôn luôn thay đổi thì chúng ta nên dùng đến ArrayList như bài học sau này sẽ nói đến.
Tiếp nối ví dụ khai báo ở trên kia, chúng ta thêm phần cấp phát như sau.
int[] myArray = new int[10];
Khi ứng dụng chạy đến bước này, nó sẽ tạo ra một mảng với 10 phần tử số nguyên "rỗng" như minh họa sau.
Như bạn thấy, bước cấp phát trên đây chỉ như một thao tác giữ chỗ trong hệ thống một vùng nhớ đủ rộng để chứa 10 phần tử số nguyên. Việc kế tiếp để mảng hoạt động là khởi tạo, hay còn hiểu là chứa dữ liệu vào trong các ô trống đó.
Có nhiều cách khởi tạo giá trị cho mảng.
Cách Thứ Nhất
Khởi tạo ngay khi vừa cấp phát bộ nhớ cho mảng, khi đó bạn không cần dùng từ khóa new nữa, chỉ khai báo và khởi tạo trên một dòng như ví dụ dưới. Dùng cách này khi bạn đã biết trước mình cần khởi tạo một mảng như thế nào ngay từ ban đầu. Với ví dụ mảng kiểu int như trên kia mình sẽ khởi tạo mảng theo cách thứ nhất như sau.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5};
Cách Thứ Hai
Khởi tạo từng giá trị cho mảng bằng cách truy xuất đến chúng dựa vào chỉ số và gán cho chúng giá trị. Cách này được sử dụng khi bạn không biết ban đầu nên khởi tạo mảng ra sao, nhưng sau đó, với logic của ứng dụng, bạn sẽ điền tuần tự từng phần tử theo chỉ số của nó.
int[] myArray = new int[10]; myArray[0] = 3;
myArray[1] = 5; myArray[2] = 7; myArray[3] = 30; myArray[4] = 10; myArray[5] = 5; myArray[6] = 8; myArray[7] = 23; myArray[8] = 0; myArray[9] = -5;
Cả 2 cách trên đây đều cho ra mảng trong hệ thống diễn đạt như sơ đồ sau.
Cách Khác
Ngoài ra thì đôi khi bạn còn dùng cả vòng lặp để khởi tạo giá trị, dùng trong trường hợp các giá trị mảng giống nhau, hay theo một trật tự nào đó, mình ví dụ với khởi tạo như sau.
int arraySize = 10; int[] myArray = new int[arraySize]; for (int i = 0; i < arraySize; i++) { myArray = i; }
Khi này mảng sẽ chứa danh sách các giá trị giống như vầy.
Như bạn đã làm quen với những cách trên đây, chúng ta sẽ truy cập vào mảng dựa vào chỉ số. Bạn luôn phải nhớ rằng chỉ số đầu tiên (chỉ số thứ nhất) của phần tử mảng bắt đầu từ 0, và chỉ số của phần tử thứ n sẽ là n-1.
Ví dụ như bạn muốn in ra console giá trị phần tử cuối cùng trong mảng 10 phần tử trên đây.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; System.out.println("Phan tu thu 10: " + myArray[9]);
Hay nếu bạn muốn in ra tất cả các giá trị phần tử trong mảng, cách tốt nhất là hãy dùng vòng for.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; for (int i = 0; i < myArray.length; i++) { System.out.println("Phan tu thu " + i + ": " + myArray); }
Lưu ý là nếu bạn dùng myArray.length thì hệ thống sẽ trả ra cho bạn độ lớn của mảng, trong trường hợp của ví dụ này độ lớn là 10. Toán tử "." trong biểu thức trên sẽ được nói rõ hơn ở mục OOP.
Bạn hãy tạo MẢNG CÁC SỐ NGUYÊN và khởi tạo giá trị cho mảng như sau
{3, 5, 7, 30, 10, 5, 8, 23, 0, -5}. Hãy in ra console TỔNG và TRUNG BÌNH CỘNG của các giá trị phần tử trong mảng.
Và đây là code của bài thực hành.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; int sum = 0; double avg; int count = myArray.length; for (int i = 0; i < count; i++) { sum += myArray[i]; } avg = (double) sum / count; System.out.println("Sum is " + sum);
System.out.println("Avegare is " + avg);
Với mảng các số nguyên như bài thực hành trên. Bạn hãy in ra VỊ TRÍ (thứ tự) của các phần tử nhỏ hơn hay bằng 0.
Và code của chương trình.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; int count = myArray.length; boolean isFound = false; for(int i = 0; i < count; i++) { if (myArray[i] <= 0) { System.out.println("The position below or equal zero is: " + i); isFound = true; } } if (!isFound) { System.out.println("Can not find the position you
need"); }
Cũng với mảng các số nguyên như bài thực hành trên. Giờ bạn hãy sắp xếp lại các phần thử mảng theo thứ tự TĂNG DẦN, sao cho khi in ra console nội dung sẽ như vầy "-5 0 3 5 5 7 8 10 23 30".
Và code như sau.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; for
(int i = 0; i < myArray.length - 1; i++) { for (int j = i; j <= myArray.length - 1; j++) { if (myArray[i] > myArray[j]) {
// Thao tác này đổi chỗ 2 giá trị ở 2
// vị trí i, j của mảng int temp; temp = myArray[i]; myArray[i] = myArray[j]; myArray[j] = temp; } } } for (int i = 0; i < myArray.length; i++) { System.out.print(myArray[i] + " "); }
Chúng ta lại cùng nhau đi qua một kiến thức mới nữa trong Java, kiến thức Mảng. Tuy vậy kiến thức Mảng đã nói ở bài hôm nay vẫn còn một tí tẹo nữa vẫn chưa nói đến vì bài viết cũng khá dài, và... dài quá thì bạn lười đọc lắm :). Nói vậy thôi chứ tổng cộng kiến thức của Mảng cũng không nhiều và khó đâu, bao nhiêu đây cũng đủ cho bạn thử nghiệm giải các bài toán trên mạng hay trên lớp rồi nhé,
phần sau chỉ nói thêm cách sử dụng foreach
trên mảng, và hơi đau đầu một tí đó là mảng nhiều chiều, các bạn cùng đón đọc nhé.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Còn nhớ bài trước chúng ta cùng nói về khái niệm và cách thức sử dụng Mảng trong Java, khi đó mình cũng có nói đến cách thức hiệu quả nhất để duyệt qua các phần tử trong Mảng là dùng vòng lặp for. Hôm nay chúng ta tìm hiểu một "biến thể" khác của for dành riêng cho Mảng, giúp bạn có thể duyệt qua Mảng nhanh hơn những gì mà chúng ta cùng nói với nhau ở bài học về for và bài học về Mảng, đó gọi là foreach. Và còn một phần nâng cao của Mảng nữa cũng sẽ được nói đến ở bài hôm nay, đó là Mảng Nhiều Chiều.
Câu hỏi đầu tiên: tại sao mình không nói về foreach ở bài học về for? Câu trả lời là, đúng là foreach cũng là for... vì nó là sự kết hợp giữa for và each... cú pháp của foreach cũng kế thừa từ for, và cách thức hoạt động của foreach dĩ nhiên cũng sẽ giống for rồi. Nhưng foreach lại sinh ra để làm việc chung với Mảng, foreach giúp thi triển các dòng code để duyệt trên mảng được dễ dàng hơn. Chính vì vậy mà foreach luôn được nói kèm với khái niệm về Mảng.
Câu hỏi tiếp theo là: foreach có quan trọng không? Câu trả lời là, không quan trọng nên sẽ không bắt buộc bạn phải dùng foreach đâu nhé. Như nói ở câu trả lời trên thì foreach chỉ giúp cho việc thi triển code trên vòng lặp được dễ dàng hơn thôi, bạn hoàn toàn có thể dùng for truyền thống để duyệt qua Mảng mà không cần đến foreach.
Trước hết chúng ta nói về cú pháp của foreach.
for (khai_báo_phần_tử_lặp : mảng) {
các_câu_lệnh; }
Bạn có thấy giống với for không nào. Với for bạn cần đưa vào 3 thành phần để định nghĩa các quy luật lặp cho nó. Còn với foreach, hiển nhiên vòng lặp này sẽ tự động biết nó phải đi qua lần lượt các phần tử trong mảng rồi, nên việc tham số cho vòng lặp này lại trở nên đơn giản hơn for nhiều. Mời bạn làm quen từng thành phần trong cú pháp foreach này.
- khai_báo_các_phần_tử_lặp là nơi bạn sẽ khai báo một biến mới để dùng trong thân hàm foreach này. Biến này sẽ chứa đựng giá trị của phần tử mà vòng lặp này đang lặp đến, do đó nó phải có kiểu dữ liệu giống như kiểu dữ liệu của từng phần tử mảng.
- mảng chính là Mảng bạn cần lặp. Đây có thể là một hàm trả về giá trị Mảng. Hàm, hay còn gọi là Phương thức, sẽ được nói đến ở bài học sau nhé.
- Dấu hai chấm (:) được hiểu như là "trong", vậy nghĩa cho tất cả các tham số trong foreach này là "từng phần tử trong mảng".
- các_câu_lệnh chính là nơi bạn dùng đến biến ở khai_báo_phần_tử_lặp ra dùng.
Chúng ta cùng làm lại bài thực hành số 1 ở bài Mảng hôm trước và thực hiện lại với foreach ở bài hôm nay. Mình copy lại nội dung bài thực hành hôm trước như sau.
Bạn hãy tạo MẢNG CÁC SỐ NGUYÊN và khởi tạo giá trị cho mảng như sau {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}. Hãy in ra console TỔNG và TRUNG BÌNH CỘNG của các giá trị phần tử trong mảng.
Và đây là code của bài thực hành.
int[] myArray = {3, 5, 7, 30, 10, 5, 8, 23, 0, -5}; int sum = 0; double avg; for (int i : myArray) {
sum += i;
} avg = (double) sum / myArray.length; System.out.println("Sum is " + sum); System.out.println("Avegare is " + avg);
Mặc dù foreach khá hay, mình cũng thích dùng nó, nhưng nên nhớ không phải lúc nào bạn cũng dùng foreach được đâu nhé, và sau đây là các trường hợp ngoại lệ đó.
- Không được dùng foreach để remove một phần tử nào đó khỏi Danh Sách (mình dùng từ "Danh Sách" chứ không phải "Mảng", vì Mảng không cho phép bạn thêm hay bớt một phần tử trong nó, chỉ có Danh Sách mới cho phép, và vì foreach cũng sẽ làm việc trên Danh Sách tương tự như Mảng nên mình nhắc đến ý này ở đây, về sau khi nói đến Danh Sách mình sẽ nhắc lại cho bạn nhớ).
- Vì foreach tự duyệt tuần tự trên các phần tử mảng, nên nó rất dở trong việc xác định vị trí (chỉ số) của từng phần tử. Do đó đừng bắt foreach hoạt động khi bạn muốn truy xuất nhanh đến vị trí của bất kỳ phần tử nào đó.
Đến bước này chắc chắn bạn đã hiểu về khái niệm Mảng. Khi người ta nói tới Mảng, thì có nghĩa họ đang nói đến "Mảng một chiều", như bạn biết thì Mảng này được xem như một danh sách (cố định) các phần tử trải dài theo một chiều duy nhất. Như minh họa bài trước thì Mảng (một chiều) nó như thế này.
Bạn nghĩ sao nếu có một biểu diễn Mảng theo một chiều mới, khi này Mảng của bạn được gọi là Mảng hai chiều, mà người ta còn gọi là Ma Trận. Biểu diễn của
Mảng hai chiều này trông như sau.
Tương tự như khai báo Mảng, khai báo một Mảng hai chiều tương tự như vậy nhưng sẽ cần đến hai cặp ngoặc vuông .
kiểu_dữ_liệu[][] tên_mảng;
hoặc
kiểu_dữ_liệu tên_mảng[][];
Chúng ta thử khai báo Mảng hai chiều cho minh họa trên đây.
int[][] myMatrix;
Cũng như Mảng. Cú pháp cấp phát cho Mảng hai chiều như sau.
tên_mảng = new kiểu_dữ_liệu[số_lượng_dòng][số_lượng_cột];
Vậy với ví dụ khai báo cho Mảng hai chiều ở trên, chúng ta tiến hình cấp phát như sau.
int[][] myMatrix = new int[4][5];
Lại cũng giống như Mảng, Mảng hai chiều cũng có nhiều hình thức khởi tạo. Giả sử mình muốn tạo ra một Mảng hai chiều với các giá trị như sau.
Cách Thứ Nhất
Dùng khi bạn biết trước dữ liệu cần khởi tạo cho Mảng hai chiều, như là dữ liệu mình đưa ra trên đây, bạn khởi tạo như sau.
int[][] myMatrix = { {3, 5, 7, 30, 10}, {5, 8, 23, 0, -5}, {100, -9, 4, 2, 55}, {-80, -22, 11, 1, 12}};
Cách Thứ Hai
Dùng cách này khi không biết dữ liệu ban đầu như thế nào, về sau tùy logic chương trình mà Mảng hai chiều sẽ được điền dữ liệu từ từ, khi đó bạn phải làm việc với chỉ số của nó. Một lưu ý là với Mảng hai chiều, chỉ số của nó
được thể hiện như sau.
Và để làm việc với chỉ số, thì bạn chú ý cách sử dụng chỉ số như khai báo sau nhé.
int[][] myMatrix = new int[4][5]; myMatrix[0][0] = 3; myMatrix[0][1] = 5; myMatrix[0][2] = 7; myMatrix[0][3] = 30; myMatrix[0][4] = 10; myMatrix[1][0] = 5; myMatrix[1][1] = 8; myMatrix[1][2] = 23;
myMatrix[1][3] = 0; myMatrix[1][4] = -5; myMatrix[2][0] = 100; myMatrix[2][1] = -9; myMatrix[2][2] = 4; myMatrix[2][3] = 2; myMatrix[2][4] = 55; myMatrix[3][0] = -80; myMatrix[3][1] = -22; myMatrix[3][2] = 11; myMatrix[3][3] = 1; myMatrix[3][4] = 12;
Cách Khác
Cũng giống như Mảng, Mảng hai chiều cũng có thể được khởi tạo bằng vòng lặp for, tất nhiên là bằng cách lồng hai for vào nhau rồi. Dùng cách này cho việc khởi tạo các giá trị cho Mảng hai chiều theo một trật tự nào đó. Bạn hãy xem ví dụ sau.
int row = 4; int column = 5; int[][] myMatrix = new int[row][column]; for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { myMatrix = i + j; } }
Bạn có biết code trên đây sẽ tạo ra một Ma Trận như thế nào không. Ma Trận bạn tạo ra chính là hình minh họa bên dưới.
Chắc chắn đến đây bạn đã hiểu rõ Mảng hai chiều hay Ma Trận rồi, tuy nhiên nếu có thắc mắc thì hãy để lại bình luận bên dưới bài học này cho mình nhé. Bây giờ chúng ta qua phần thực hành cho Ma Trận.
Bài này chúng ta sẽ thử thao tác với Ma Trận. Nhưng thay vì khai báo và khởi tạo sẵn một Ma Trận, bạn hãy thử nhập chúng từ console xem sao, sẽ rất thú vị đấy, nội dung của bài thực hành này như sau.
Tạo một Ma Trận các số nguyên bằng cách.
- In ra console dòng "Please enter number of row:" và đợi người dùng nhập vào số lượng hàng của Ma Trận.
- In ra console dòng "Please enter number of columns:" và đợi người dùng nhập vào số lượng cột của Ma Trận.
- In ra console từng dòng yêu cầu người dùng nhập từng phần tử của Ma
Trận.
Với Ma Trận do người dùng nhập vào ở trên, thực hiện thao tác sau.
- In ra ma trận mà người dùng vừa mới nhập.
- In ra dòng và cột có tổng lớn nhất trong Ma Trận.
Nếu bạn đã hiểu yêu cầu bài thực hành thì tiến hành code nhé, code xong rồi thì mời bạn so sánh với kết quả của mình bên dưới. Hoan nghênh các bạn chia sẻ những suy nghĩ hoặc những giải thuật của bạn về bài thực hành này ở phần bên dưới bài học.
Đại loại của chương trình khi chạy, và khi tương tác với user sẽ như sau.
Và code như sau.
Scanner scanner = new Scanner(System.in);
// Nhập số lượng hàng System.out.print("Please enter number of row: "); int row = scanner.nextInt();
// Nhập số lượng cột System.out.print("Please enter number of column: "); int column = scanner.nextInt(); int[][] myMatrix = new int[row][column]; // Kêu user nhập vào từng phần tử của Ma Trận for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { System.out.print("Matrix[" + i + "] [" + j + "] = "); myMatrix[i][j] = scanner.nextInt(); } } // In ra ma trận user mới nhập System.out.println("Your Matix here"); for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { System.out.print(myMatrix[i][j] + " "); } System.out.println(); // Đơn giản là xuống dòng } // Tìm dòng lớn nhất int[] sumRow = new int[row]; // Mảng chứa tổng từng dòng for (int ro = 0; ro < row; ro++) { for (int co = 0; co < column; co++) { sumRow[ro] += myMatrix[ro][co]; } }
int maxIndexRow = 0; int maxSumRow =
sumRow[maxIndexRow]; // Giả sử dòng 0 là // dòng lớn nhất for (int i = 1; i < row; i++) { if (maxSumRow < sumRow[i]) { maxSumRow = sumRow[i]; maxIndexRow = i; // Lưu lại chỉ số dòng của // số lớn nhất } } System.out.println("Max row in Matrix is row: " + maxIndexRow); // Tìm cột lớn nhất int[] sumColumn = new int[column]; // Mảng chứa tổng // từng cột for (int ro = 0; ro < row; ro++) { for (int co = 0; co < column; co++) { sumColumn[co] += myMatrix[ro][co]; } } int maxIndexColumn = 0; int maxSumColumn = sumColumn[maxIndexColumn]; // Giả sử cột // 0 là cột lớn nhất for (int i = 1; i < column; i++) { if (maxSumColumn < sumColumn[i]) { maxSumColumn = sumColumn[i]; maxIndexColumn = i; // Lưu lại chỉ số cột của số // lớn nhất } } System.out.println("Max column in Matrix is column: " + maxIndexColumn);
Phần râu ria còn lại của bài học hôm nay mình xin gom hết vào đây, mình chỉ nói
qua một lượt mà không có thực hành, cũng không cần các bạn phải hiểu rõ, vì phần này nói về các thể loại mảng còn lại trong Java mà hầu như chúng ta sẽ không dùng đến nó. Sự thật là mình chưa bao giờ dùng đến các thể loại mảng "cờ hó" này, nhưng biết đâu được ở đâu đó bạn sẽ chạm trán với nó, vậy thì hãy thử xem qua nó là gì nhé.
Vâng, Mảng hai chiều đã đủ làm bạn chóng mặt, nay còn có cái thể loại Mảng ba chiều, thậm chí 4 chiều,... nhưng khoan khoan... bạn không cần phải dùng đến mảng nhiều chiều quá vậy đâu, hãy thử xem với mảng 3 chiều thì thao tác trên mảng sẽ như sau, đủ để bạn nản lòng rồi đấy.
int[][][] array3D = new int[4][5][3]; for (int row = 0; row < 4; row++) { for (int col = 0; col < 5; col++) { for (int ver = 0; ver < 3; ver++) { array3D[row][col][ver] = row + col + ver; } } }
Ặc cái thể loại mảng này, mình cũng tránh xa. Cơ bản là mảng này được biểu diễn không "đều", chẳng hạn như bạn có một Ma Trận có m dòng, mà mỗi dòng lại có số lượng cột khác nhau.
int[][] myJaggedArr = {{3, 4, 5} , {77, 50}}; for(int i = 0; i < myJaggedArr.length; i++) { for (int j = 0; j < myJaggedArr[i].length; j++) { System.out.print(myJaggedArr[i][j] + " "); } System.out.println(); }
Cuối cùng thì các kiến thức liên quan đến Mảng cũng đã xong, hi vọng các bạn có được một kiến thức vững và tốt để ứng dụng vào các bài học sau của Java, hoặc
thực hiện xây dựng một ứng dụng Android của mình dựa trên nền tảng Java này.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Các bài trước chúng ta đã dành hết công lực để nói và học về Mảng. Các bạn cũng đã biết rằng Mảng là một cấu trúc dữ liệu khá mạnh và hiệu quả, chúng giúp quản lý danh sách các phần tử có cùng một kiểu dữ liệu, và còn giúp truy xuất rất nhanh đến một phần tử bất kỳ nữa. Hôm nay bạn lại được làm quen với một ứng dụng khá hay nữa của Mảng, đó là Mảng các ký tự, hay được gọi với một cái tên ngắn gọn và dễ hiểu hơn, đó là Chuỗi.
Chuỗi, hay còn gọi là String đơn giản là một Mảng các ký tự. Tuy nhiên về cơ bản thì là vậy, còn về mặt cấu trúc thì Chuỗi là một Đối Tượng (khái niệm mới mẻ này chúng ta sẽ nói đến ở mục Hướng Đối Tượng - OOP). Và vì nó là Đối Tượng, nó sẽ được hệ thống xây dựng sẵn các Phương Thức hữu ích, việc của chúng ta là làm quen với các Phương Thức này và lấy ra dùng khi cần thiết, như phương thức so sánh chuỗi, cắt chuỗi, thay thế, tìm độ dài, tìm chuỗi con....
Khi làm quen với Chuỗi, bạn sẽ được nghe đâu đó nói rằng Chuỗi là không thể thay đổi được, tiếng Anh gọi là Immutable. Bạn có thể hiểu là một khi bạn khởi tạo một Chuỗi thì giá trị khởi tạo đó sẽ là cố định. Mỗi khi bạn thay đổi giá trị cho Chuỗi, hệ thống sẽ tạo ra một Chuỗi mới. Do đó sẽ không ảnh hưởng lắm nếu như chương trình của bạn tạo ra các Chuỗi để xài mà bạn không có nhu cầu chỉnh sửa Chuỗi hay chỉ chỉnh chút ít như các bài tập của bài học hôm nay. Nhưng nếu bạn có một Chuỗi nào đó mà liên tục liên tục bị thay đổi giá trị, thì thay vì khai báo một Chuỗi kiểu String, bạn có thể sử dụng kiểu StringBuffer hay StringBuilder thay thế. Bài hôm nay sẽ không đủ để nói về StringBuffer và StringBuilder, mình sẽ tập trung vào String và hẹn các bạn tiếp tục ở bài kế tiếp.
Mình xin trắc nghiệm các bạn một tí. Dựa vào tất cả những gì mình nói ở trên
đây, giả sử như như bây giờ mình kêu các bạn hãy thử khai báo một Chuỗi và khởi tạo Chuỗi đó với nội dung là "Hello World!", bạn sẽ nghĩ ngay đến khai báo sau đúng không nào.
char[] chuoi = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
Chưa đúng đâu nhé, code trên đây chỉ giúp bạn khởi tạo một Mảng các ký tự thôi, với Chuỗi thì dễ hơn nhiều đấy bạn, bạn có thể chọn một trong hai cách khai báo và khởi tạo như sau.
Khởi tạo theo kiểu "nguyên thủy", tức là bạn xem Chuỗi như là một biến với kiểu dữ liệu nguyên thủy. Mình đưa ra ví dụ khai báo cho chuỗi "Hello World!" luôn chứ không đưa cú pháp như sau.
String myLiteralStr = "Hello World!";
Hoặc khởi tạo theo kiểu "đối tượng", tức là bạn dùng từ khóa new để khởi tạo như những gì bạn thao tác với Scanner hay Mảng ở bài trước, bạn xem.
String myObjectStr = new String("Hello World!");
Bạn chú ý ở khai báo và thao tác với Chuỗi chúng ta bao chuỗi trong dấu nháy kép (" ") chứ không phải nháy đơn (' ') như với Ký tự nhé.
Nói về kiến thức sâu một tí thì hai cách khởi tạo hai Chuỗi trên đây thực ra là khác nhau, là bởi vì liên quan đến tính chất không thể thay đổi được mà mình đã nhắc ở trên. Nhưng mình sẽ không nói sâu về sự khác nhau đó ở đây. Còn nhớ khi mới tiếp cận Java mình cũng chỉ ghi nhớ hai cách khởi tạo trên, và mình thích cách đầu tiên nhất, cách đó trực quan và rất nhanh. Mình sẽ lập một mục riêng để bàn về hai cách khởi tạo này ở một bài viết khác nhé.
Đến đây mình không biết trình bày theo kiểu nào là tốt nhất. Vì về nguyên tắc Chuỗi vẫn là một khái niệm Hướng đối tượng (OOP), nó sẽ xa vời với đa số các
bạn mới làm quen với lập trình. Nhưng mình nghĩ là không sao, việc sử dụng Chuỗi khá là dễ, mình sẽ cố gắng liệt kê tất cả các phương thức hữu ích của
Chuỗi, bạn hãy làm quen với cách sử dụng các phương thức này, sau đó nếu có gặp lại ở đâu đó thì bạn hãy nhớ đến bài học hôm nay vậy.
Khi bạn có hai chuỗi, và muốn biết chúng giống hay khác nhau, hay khác nhau là khác nhau như thế nào, thì có thể dùng đến các hàm so sánh như sau.
equals()
Nếu có hai chuỗi, mình ví dụ chuoi1 và chuoi2, bạn có thể gọi chuoi1.equals(chuoi2), kết quả trả về là một kiểu boolean với true là giống nhau và false là không giống nhau.
Với chuỗi gốc là "Hello World!", bạn hãy in ra kết quả của so sánh chuỗi gốc với hai chuỗi "HelloWorld!" và "hello world!" nhé.
String rootStr = "Hello World!"; System.out.println("Compare 1: " + rootStr.equals("HelloWorld!")); System.out.println("Compare 2: " + rootStr.equals("hello world!"));
Kết quả của hai câu lệnh in ra console trên hiển nhiên là false và false rồi.
equalsIgnoreCase()
Cách sử dụng của phương thức này cũng giống với equals(), nhưng
Bạn hãy in ra kết quả so sánh chuỗi "Hello World!" và "hello world!" nhé.
String rootStr = "Hello World!"; System.out.println("Compare: " + rootStr.equalsIgnoreCase("hello world!"));
Kết quả của câu lệnh so sánh chắc chắn là true.
So Sánh Với Toán Tử ==
Ở mức độ của bài học hôm nay, chúng ta không bàn về việc dùng toán tử == để so sánh hai Chuỗi trong Java. Về mặt ngữ pháp lập trình thì không sai, bạn có thể thử dùng toán tử này để thực hành so sánh chuoi1 == chuoi2. Nhưng một khi bạn chưa hiểu rõ về Chuỗi và về Hướng đối tượng thì mình khuyên bạn nên bỏ khỏi đầu ý định so sánh hai Chuỗi trong Java bằng toán tử == nhé, mình sẽ nói tại sao ở một bài khác.
compareTo()
Phương thức so sánh này khá đặc biệt, nó sẽ lấy từng ký tự ra so sánh, với mỗi ký tự nó sẽ lấy giá trị Unicode ra rồi tiến hành phép trừ các ký tự này, một khi đã phát hiện ta ký tự khác biệt (phép trừ cho ra kết quả âm hay dương) thì sẽ ngừng việc trừ lại.
Cụ thể như ví dụ chuoi1.compareTo(chuoi2) thì.
- Kết quả trả về là một số âm khi chuoi1 có thứ tự ký tự trong Unicode nhỏ hơn chuoi2.
- Kết quả trả về là số 0 khi chuoi1 giống hệt chuoi2.
- Kết quả trả về là một số dương khi chuoi1 có thứ tự ký tự trong Unicode lớn hơn chuoi2.
Giả sử bạn lấy đâu đó ra hai chuỗi "1.5.5.3" và "1.5.6.1", đây chính là hai version của ứng dụng. Bằng cách nào bạn có thể in ra console version nào là version mới nhất?
String version1 = "1.5.5.3"; String version2 = "1.5.6.1"; int compare = version1.compareTo(version2); if (compare < 0) { System.out.println(version2 + " mới hơn " + version1); } else if (compare > 0) { System.out.println(version1 + " mới hơn " + version2);
} else { System.out.println(version1 + " giống " + version2); }
Khi bạn có hai chuỗi, và muốn nối chúng lại với nhau, bạn có thể dùng đến các hàm sau.
Nối Chuỗi Với Toán Tử +
Thật dễ dàng như 1 + 1 = 2 vậy, bạn hãy xem qua ví dụ sau.
String str1 = "Hello World!"; String str2 = str1 + " Hello Yellow Code Books!"; System.out.println(str2);
Với cách nối Chuỗi bằng toán tử + này thì bạn có thể nối thoải mái bao nhiêu Chuỗi lại với nhau cũng được. Và nên nhớ là không hề có các toán tử -,* hay / với Chuỗi nhé bạn.
Với ví dụ nối Chuỗi bằng toán tử + trên đây thì bạn hãy nhớ lại đi, bạn đã và đang làm quen với cách nối Chuỗi này từ các bài học trước và kể cả bài học
concat()
Với việc gọi chuoi1.concat(chuoi2) thì kết quả cho ra sẽ là một Chuỗi mới tương tự như gọi chuoi1 + chuoi2 trên đây vậy.
Bạn hãy viết lại câu lệnh nối chuỗi với toán tử + trên đây bằng hàm concat() nhé.
String str1 = "Hello World!"; System.out.println(str1.concat(" Hello Yellow Code Books!"));
Nếu bạn có một Chuỗi, và muốn lấy ra Chuỗi con trong Chuỗi gốc này, mời bạn làm quen với hai hàm sau.
subString(int startIndex)
Nếu bạn gọi chuoi.subString(startIndex) thì kết quả sẽ trả về một Chuỗi con với nội dung bắt đầu từ chỉ số startIndex của chuoi, lưu ý là giống như với Mảng, chỉ số của các ký tự trong chuỗi được bắt đầu từ 0.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy trích ra chuỗi con là "Hello Yellow Code Books!" nhé.
String rootStr= "Hello World! Hello Yellow Code Books!"; System.out.println(rootStr.substring(13));
subString(int startIndex, int endIndex)
Tương tự như trên nhưng nếu bạn gọi chuoi.subString(startIndex, endIndex)
thì kết quả sẽ trả về một Chuỗi con với nội dung bắt đầu từ chỉ số startIndex đến chỉ số endIndex của chuoi.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy thử trích ra chuỗi con là "Yellow Code Books" xem sao.
String rootStr= "Hello World! Hello Yellow Code Books!"; System.out.println(rootStr.substring(13, rootStr.length() - 2));
Lưu ý ở bài thực hành này mình có gọi đến hàm length() của Chuỗi, hàm này sẽ trả về độ lớn của Chuỗi, là tổng số ký tự trong Chuỗi đó.
Ở Chuỗi có các hàm sau để bạn có thể chuyển đổi về in hoa hay in thường cho tất cả các ký tự trong Chuỗi.
toUpperCase()
Nếu bạn gọi chuoi.toUpperCase() thì nó sẽ trả về một Chuỗi mới với tất cả các ký tự được in hoa từ chuoi.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy in hoa hết tất cả các ký tự của chuỗi và in ra console.
String rootStr= "Hello World! Hello Yellow Code Books!"; rootStr = rootStr.toUpperCase(); System.out.println(rootStr);
Phương thức này ngược lại hoàn toàn với toUpperCase(), nó sẽ giúp trả về một Chuỗi với tất cả các ký tự trong chuoi về thành ký tự thường.
Dưới đây là một số phương thức thông dụng khác của Chuỗi mà bạn cần phải biết.
trim()
Phương thức này loại bỏ các khoảng trắng ở trước và sau Chuỗi.
Với chuỗi gốc là " Hello World! ". Bạn hãy cắt bỏ các khoảng trắng ở đầu và
cuối chuỗi rồi in ra console.
String rootStr= " Hello World! "; rootStr = rootStr.trim(); System.out.println(rootStr);
startsWith() Và endsWith()
Hai phương thức này đều trả về một kiểu boolean, cho biết Chuỗi có bắt đầu hay kết thúc với một Chuỗi được so sánh hay không.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", hãy in ra console cho biết chuỗi này có bắt đầu là "Hello" và có kết thúc là "Hello" hay không.
String rootStr= "Hello World! Hello Yellow Code Books!"; System.out.println("String start with Hello? " + rootStr.startsWith("Hello")); System.out.println("String end with Hello? " + rootStr.endsWith("Hello"));
Kết quả của hai câu in ra console trên lần lượt là true và false.
charAt()
Phương thức này trả về ký tự của Chuỗi tại chỉ mục được truyền vào.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy in ra console ký tự thứ 11 trong chuỗi.
String rootStr= "Hello World! Hello Yellow Code Books!"; System.out.println("The 11th char is " +
rootStr.charAt(11));
replace()
Phương thức này có hai tham số truyền vào chính là hai Chuỗi, như này chuoi.replace(chuoi1, chuoi2). Nếu chuoi1 có tồn tại trong chuoi thì tất cả những nơi nào xuất hiện chuoi1 trong chuoi sẽ bị thay bằng chuoi2.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy thay thế tất cả các "Hello" trong chuỗi gốc này thành "Hi" nhé.
String rootStr= "Hello World! Hello Yellow Code Books!"; rootStr = rootStr.replace("Hello", "Hi"); System.out.println(rootStr);
indexOf()
Hàm này sẽ tìm trong Chuỗi vị trí xuất hiện đầu tiên của ký tự được truyền vào trong hàm.
Với chuỗi gốc là "Hello World! Hello Yellow Code Books!", bạn hãy in ra console vị trí của ký tự "!".
String rootStr= "Hello World! Hello Yellow Code Books!"; System.out.println("The ! char at " + rootStr.indexOf("!")) ;
Kết quả câu lệnh này sẽ in ra console số 11.
Ngoài các phương thức được liệt kê trên đây thì Chuỗi còn có khá nhiều các phương thức hữu ích khác, nhưng trong khuôn khổ bài viết này mình tạm không nói hết. Hoặc có những phương thức mang đậm tính Hướng đối tượng hơn, có thể khiến bạn phải hoang mang, nên mình sẽ dành khi nào dùng đến các phương thức đặc biệt này sẽ nói rõ hơn.
Vậy là các bạn đã xem sơ qua về Chuỗi trong Java. Về cá nhân mình thấy nhiêu đây vẫn chưa đủ kiến thức về Chuỗi, vẫn còn nhiều điều hay ho nữa, có lẽ mình
sẽ tạo một bài mới để nói được rõ ràng hơn.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Bài này là bài học mở rộng so với bài về Chuỗi của bữa trước. Có thể nói là bài học bổ sung, nó không quan trọng lắm, nhưng không nói thì lại áy náy, ăn ngủ không yên. Nếu bạn nào quan tâm đến hiệu năng (performance) của ứng dụng, thì chú ý kỹ các bài học dạng này.
Để làm quen với các kiến thức và ví dụ của bài học hôm nay, bạn nhất thiết phải thực hành một chút với các hàm của Chuỗi của bài trước để hiểu được cơ bản đối tượng này, thì hôm nay bạn mới có thể làm quen dễ hơn với các khái niệm và phương thức mở rộng.
Tất nhiên đây sẽ là thắc mắc đầu tiên của các bạn khi mình giới thiệu hai đối tượng này trong ngày hôm nay.
Chà... thực ra bài học hôm nay mình cũng cân nhắc nhiều. Bạn cũng biết là khi tìm hiểu về Chuỗi thì coi như bạn đã "đặt một chân" vào cánh cửa OOP rồi, mà chúng ta lại chưa có bài viết nào về OOP cả. Vậy mà StringBuffer và StringBuilder của bài hôm nay lại tiếp tục nói nữa về OOP. Nhưng mình nghĩ nếu biết về chúng trễ quá sau khi học hết OOP thì lại không hay chút nào. Thế là mình quyết định thôi thì nói trước, sau này làm quen với OOP chắc chắn các bạn sẽ hiểu rõ hơn bài học hôm nay.
Vậy quay lại câu hỏi tại sao phải biết hai đối tượng này? Như bài hôm trước mình có nói, nếu bạn đã làm quen với Chuỗi thì nên biết rằng Chuỗi là không thể thay đổi được, tiếng Anh gọi là Immutable. Điều này có nghĩa là khi bạn tạo ra một Chuỗi, thì Chuỗi đó là cố định và bạn không thể thay đổi được. Nhưng mà khoan!!! Bài hôm trước chúng ta có thực hành việc nối Chuỗi, cắt Chuỗi, chuyển
in hoa/thường cho Chuỗi... thì đó không phải là thay đổi Chuỗi hay sao??? Hoang mang quá. Thực ra là, nếu bạn tác động để làm một Chuỗi thay đổi, hệ thống sẽ tạo ra Chuỗi mới cho bạn, Chuỗi cũ (trước khi bị bạn thay đổi) sẽ còn lại trong hệ thống và sẽ có nhiều nguy cơ trở thành rác, làm ảnh hưởng đến hiệu năng của ứng dụng.
Khi đó StringBuffer và StringBuilder ra đời nhằm đáp ứng nhu cầu của bạn trong các trường hợp muốn sử dụng đến Chuỗi có khả năng thay đổi được (Mutable). Chúng đã làm điều đó như thế nào? Mời các bạn xem tiếp phần dưới sẽ rõ.
Lại một câu hỏi nữa khiến mình càng thêm cân nhắc khi nói đến hai đối tượng này ở bài hôm nay. Vì câu trả lời cho câu hỏi này là: StringBuffer và StringBuilder có công năng và cách sử dụng hoàn toàn giống nhau, tuy nhiên về mặt cấu trúc thì có khác nhau đôi chút, đó là StringBuffer được cấu tạo để ứng dụng vào các xử lý Đa Luồng (Multithreading) giúp tránh các tranh chấp giữa các Luồng (Thread), còn StringBuilder được cấu tạo để ứng dụng trong một Luồng mà thôi. Khái niệm Luồng hay Đa Luồng sẽ được nói đến ở các bài học sau nữa, do đó một lần nữa mình chỉ muốn nêu ra cho các bạn biết, còn để hiểu rõ hơn thì bạn lại phải tiếp tục theo dõi dông dài các bài viết của Yellow Code Books rồi ;) .
Có một số so sánh về tốc độ trên mạng có chỉ ra rằng, sử dụng StringBuilder là nhanh nhất, sau đó đến StringBuffer rồi cuối cùng là String. Mình tin là điều này đúng, nhưng mình vẫn chưa có cơ hội kiểm chứng.
Nếu bạn vẫn chưa nắm rõ lắm về sự khác nhau giữa StringBuffer và StringBuilder, mà vẫn muốn áp dụng chúng vào project hiện tại của bạn thay vì phải đợi đến khi học đến Luồng (Thread), thì bạn có thể dùng tạm một trong hai, StringBuffer chẳng hạn.
Do mình đã nói rằng, cách sử dụng StringBuffer và String Builder là hoàn toàn giống nhau, do đó ở mục cách sử dụng này, mình chỉ đưa ra ví dụ về
StringBuffer thôi nhé, bạn hoàn toàn có thể áp dụng các kiến thức của phần này dành cho StringBuffer vào StringBuilder mà không gặp chút rắc rối nào.
Bạn có nhớ hai cách khai báo và khởi tạo một Chuỗi không? Nếu quên thì xem lại bài trước cho nhớ nhé. Cơ bản với Chuỗi bạn sẽ có hai cách khởi tạo, hoặc là khởi tạo kiểu "nguyên thủy", hoặc là khởi tạo kiểu "đối tượng".
Còn với StringBuffer (và cả StringBuilder) của bài hôm nay thì hơi khác Chuỗi thông thường một chút, đó là chỉ có một cách duy nhất để khởi tạo chúng mà thôi, chính là khởi tạo kiểu "đối tượng".
Như đã trình bày ở bài Chuỗi, bài này mình cũng xin đưa ra ví dụ về khai báo StringBuffer mà không đưa ra cú pháp.
StringBuffer strBuffer_1 = new StringBuffer(); // Khởi tạo một StringBuffer rỗng, // với khả năng chứa đựng ban đầu // là 16 ký tự StringBuffer strBuffer_2 = new StringBuffer(50); // Khởi tạo một StringBuffer // rỗng, với khả năng chứa đựng // ban đầu do bạn định nghĩa StringBuffer strBuffer_3 = new StringBuffer("Hello World!"); // Giống với Chuỗi, // cách này khởi // tạo một StringBuffer // với chuỗi xác định
Và vì StringBuffer và StringBuilder là các cấu trúc giúp thay đổi chuỗi thoải mái, nên bạn cứ yên tâm là sẽ không ảnh hưởng đến hiệu suất của hệ thống nếu bạn thay đổi nhiều và liên tục trên chuỗi. Dưới đây là các phương thức
Nếu như với Chuỗi, bạn có thể dùng hàm concat() hay toán tử + để nối hai chuỗi
append() sẽ có tác dụng gần như tương tự, tức là sẽ thêm chuỗi mới vào chuỗi cũ mà hệ thống vẫn không tạo ra chuỗi khác. Bạn xem ví dụ cách sử dụng hàm này như sau.
StringBuffer str1 = new StringBuffer("Hello World!"); str1.append(" Hello Yellow Code Books!"); System.out.println(str1); // Kết quả in ra là // "Hello World! Hello Yellow Code Books!"
Phương thức này giúp chèn một chuỗi vào một vị trí nào đó ở chuỗi gốc. Ví dụ dưới đây giúp chèn thêm chuỗi "Java" vào sau chuỗi "Hello" ở ví dụ trên, để tạo thành chuỗi "Hello Java World! Hello Yellow Code Books!".
StringBuffer str2 = new StringBuffer("Hello World!"); str2.append(" Hello Yellow Code Books!"); str2.insert(5, " Java"); System.out.println(str2); // Kết quả in ra là // "Hello Java World! Hello Yellow Code Books!"
Nếu bạn còn nhớ thì ở Chuỗi chúng ta cũng đã làm quen với phương thức có tên replace() này rồi. Nhưng khi đó tham số truyền vào của replace() là hai chuỗi, giúp bạn thay thế tất cả chuỗi nếu có tồn tại trong chuỗi gốc bằng một chuỗi mới. Qua đến replace() của bài hôm nay các bạn phải chỉ định vị trí bắt đầu và kết thúc của chuỗi gốc cần được thay thế bằng chuỗi mới.
Như ví dụ sau (mình lấy lại từ yêu cầu thực hành thay thế chuỗi "Hello" thành "Hi" ở bài trước).
StringBuffer str3 = new StringBuffer("Hello World! Hello Yellow Code Books!"); str3.replace(0, 5, "Hi");
System.out.println(str3); // Kết quả in ra là // "Hi World! Hello Yellow Code Books!"
Bạn lưu ý là chuỗi mới "Hi" chỉ thay thế cho chuỗi "Hello" có vị trí từ 0 đến 5 trong chuỗi gốc. Chuỗi "Hello" đứng sau đó vẫn còn trong chuỗi gốc, điều này khác với replace() ở bài Chuỗi là thay thế hết tất cả "Hello" thành "Hi" luôn đấy nhé.
Phương thức này không có trong bài trước, dùng để xóa chuỗi từ vị trí bắt đầu đến vị trí kết thúc trong chuỗi gốc. Ví dụ sau xóa chuỗi "Java" ra khỏi chuỗi gốc ban đầu.
StringBuffer str4 = new StringBuffer("Hello Java World! Hello Yellow Code Books!"); str4.delete(6, 11); System.out.println(str4); // Kết quả in ra là // "Hello World! Hello Yellow Code Books!"
Một phương thức khá thú vị ở bài hôm nay, đó là đảo chuỗi. Bạn hãy xem kết quả đảo ngược từ ví dụ bên dưới.
StringBuffer str5 = new StringBuffer("Hello World! Hello Yellow Code Books!"); str5.reverse(); System.out.println(str5); // Kết quả in ra là // "!skooB edoC wolleY olleH !dlroW olleH"
Ở bước khởi tạo trên đây bạn đã biết đến khả năng chứa đựng số lượng ký tự của StringBuffer và StringBuilder. Nếu bạn khởi tạo các đối tượng này rỗng, dung
lượng mặc định ban đầu là 16. Hãy xem ví dụ chứng minh sau đây.
StringBuffer str6 = new StringBuffer(); System.out.println(str6.capacity()); // Kết quả in ra là 16
Điều này cũng giống khi bạn làm việc với Mảng, khi đó bạn Cấp phát một bộ nhớ cho Mảng có khả năng chứa 16 phần tử chẳng hạn mà chưa Khởi tạo giá trị cho Mảng đó. Thì với cách khai báo trên đây của StringBuffer cũng vậy. Hay với cách khai báo trên đây có truyền vào số lượng ký tự có thể chứa ban đầu.
StringBuffer str7 = new StringBuffer(30); System.out.println(str7.capacity()); // Kết quả in ra là 30
Mình tiếp tục thử thêm chuỗi vào rồi kiểm tra lại capicity() nhé.
StringBuffer str8 = new StringBuffer(); str8.append("Hello World!"); System.out.println(str8.capacity()); // Kết quả in ra là 16
Vậy khi bạn khai báo và thêm một chuỗi vào thì dung lượng mặc định ban đầu vẫn dựa vào khả năng chứa 16 ký tự, nếu số lượng ký tự của chuỗi vượt qua 16 thì khả năng chứa sẽ tự tăng lên. Điều này làm cho StringBuffer và StringBuilder khá linh động và hiệu năng sử dụng bộ nhớ cũng được tối ưu nữa.
Ngoài các phương thức cho StringBuffer và StringBuilder trên đây, đó là các phương thức đặc biệt linh động mà với Chuỗi ở bài trước không có. Thì hai anh em mới này của Chuỗi cũng vẫn có các phương thức tương tự như bên Chuỗi mà bạn có thể mang ra dùng, mình chỉ điểm mặt thôi, đó là.
- subString(startIndex);
- subString(startIndex, endIndex);
- charAt(index);
- indexOf(Chuỗi);
Chúng ta vừa đến với kiến thức có thể nói là cuối cùng của Java Cơ Bản trong chương trình học của Yellow Code Books, để từ bài học sau sẽ cùng nhau bước sang một lĩnh vực mới trong lập trình, tất nhiên vẫn sẽ là ngôn ngữ Java, nhưng sẽ là các bài học về Hướng Đối Tượng (OOP) của Java.
Bạn có thể theo dõi trực tiếp bài học ở link gốc này.
Các bạn vừa đọc qua các bài học trong phần thứ nhất của toàn bộ kiến thức về ngôn ngữ Java của Yellow Code Books.
Chắc chắn đây chưa phải là một bộ bách khoa đầy đủ nhất về Java. Nhưng mình tin chắc các kiến thức mà mình chia sẻ ra đây phần nào cũng giúp các bạn tham khảo, học tập và trau dồi thêm kinh nghiệm lập trình. Đặc biệt là các bạn mới làm quen với các dòng code.
Cảm ơn các bạn đã đồng hành với blog trong thời gian qua. Sự chia sẻ của các bạn về quyển ebook này sẽ là lời cảm ơn chân thành nhất và động lực tốt nhất để tác giả cho ra các bài học còn lại.