Giáo trình Công nghệ Java

pdf 226 trang Gia Huy 17/05/2022 3190
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Công nghệ Java", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_cong_nghe_java.pdf

Nội dung text: Giáo trình Công nghệ Java

  1. MỤC LỤC DANH MỤC THUẬT NGỮ TIẾNG ANH 7 LỜI NÓI ĐẦU 8 Chương 1. TỔNG QUAN VỀ LẬP TRÌNH JAVA 10 1.1. Lịch sử ra đời và phát triển của Java 10 1.2. Đặc trưng ngôn ngữ Java 10 1.3. Các ứng dụng của Java 12 1.4. Dịch và thực thi một chương trình viết bằng Java 13 1.5. Kiến trúc chương trình xây dựng trên Java 13 1.5.1. Kiến trúc chương trình Java 13 1.5.2. Chương trình Java đầu tiên. 16 Chương 2. CẤU TRÚC LẬP TRÌNH CƠ BẢN TRONG JAVA .20 2.1. Các kiểu dữ liệu 20 2.2. Biến 21 2.3. Toán tử. 22 2.4. Strings và StringBuider 23 2.4.1. Lớp String 23 2.4.2. StringBuffer và StringBuilder 27 2.5. Nhập/Xuất dữ liệu 27 2.6. Câu lệnh và các cấu trúc lệnh trong Java 31 2.6.1. Lệnh, khối lệnh trong Java. 32 2.6.2. Câu lệnh if else 32 2.6.3. Câu lệnh switch-case 33 2.6.4. Vòng lặp While 35 2.6.5. Vòng lặp do-while 35 2.6.6. Vòng lặp for 36 2.6.7. Lệnh break và continue 37 2.7. Số lớn trong java 38 2.8. Mảng 38 2.8.1. Vòng lặp “for each” 39 2.8.2. Khởi tạo mảng và mảng nặc danh 40 2.8.3. Copy mảng 41 2.8.4. Các tham số dòng lệnh 41 2.8.5. Sắp xếp mảng 42 1
  2. 2.8.6. Mảng nhiểu chiều 42 Chương 3. ĐỐI TƯỢNG, LỚP, KẾ THỪA, GIAO DIỆN 49 3.1. Đối tượng, lớp, lớp trừu tượng 49 3.1.1. Khái niệm 49 3.1.2. Khai báo lớp 49 3.1.3. Thuộc tính của lớp 50 3.1.4. Phương thức của lớp 52 3.1.5. Chỉ định truy xuất lớp 55 3.2. Tạo đối tượng 55 3.3. Kế thừa và đa hình 57 3.3.1. Mô tả kế thừa 57 3.3.2. Kế thừa đơn 57 3.3.3. Kế thừa kép 61 3.3.4. Lớp cha, lớp con 63 3.3.5. Cách sử dụng từ khóa super 64 3.3.6. Cách nạp chồng phương thức 64 3.3.7. Đa hình 66 3.4. Lớp trừu tượng 68 3.5. Lớp Object 71 3.5.1. Phương thức equals 71 3.5.2. Phương thức toString ( ) 72 3.6. Giao diện 72 3.6.1. Mô tả giao diện 72 3.6.2. Mục đích sử dụng giao diện 73 3.6.3. So sánh giao diện và lớp trừu tượng 73 3.7. Lớp nội 74 3.7.1. Các kiểu lớp nội 75 3.8. Xử lý ngoại lệ 77 3.8.1. Giới thiệu 77 3.8.2. Mục đích của việc xử lý ngoại lệ 77 3.8.3. Lớp Exception 77 3.8.4. Xử lý ngoại lệ 77 3.8.5. Mô hình xử lý ngoại lệ 78 3.8.6. Các khối chứa nhiều Catch 79 2
  3. 3.8.7. Khối ‘finally’ 81 3.8.8. Các ngoại lệ được định nghĩa với lệnh ‘throw’ và ‘throws’ 83 3.8.9. Danh sách các ngoại lệ 84 3.9. Giao diện Collection 85 3.9.1. Giới thiệu 85 3.9.2. Các kiểu collection 85 3.9.3. Collection frame work 91 3.10. Thread 94 3.10.1. Thread là gì, Tạo Thread 94 3.10.2. Các trạng thái của Thread 94 3.10.3. Các phương thức của lớp Thread 95 3.10.4. Quản lý Thread 96 3.10.5. Luồng chạy ngầm (deamon) 96 3.11. Đa luồng và tương tranh 97 3.11.1. Định nghĩa đa luồng 97 3.11.2. Sử dụng phương thức isAlive() và join() 100 3.11.3. Điều kiện tương tranh và cách khắc phục 101 3.11.4. Cách sử dụng phương thức wait() và notify() 102 3.11.5. Định nghĩa deadlock và khắc phục 103 CHƯƠNG 4. LẬP TRÌNH GIAO DIỆN VỚI JAVA SWING 111 4.1. Giới thiệu Swing và mô hình MVC 111 4.1.1. Lợi ích của sử dụng Swing so với AWT 112 4.1.2. Mô tả container trong Swing 113 4.2. Quản lý Layout 113 4.2.1. BorderLayout 113 4.2.2. GridLayout 115 4.2.3. GirdBagLayout 116 4.2.4. GroupLayout 116 4.3. Text Input 116 4.3.1. TextFields 116 4.3.2. Label 117 4.3.3. Password Fields 118 4.3.4. TextAreas 118 4.3.5. Scroll Panes 119 3
  4. 4.4. Các thành phần lựa chọn 120 4.4.1. Checkboxes 120 4.4.2. Radio Button 120 4.4.3. ComboBoxes 121 4.4.4. Slider 123 4.5. Menu 124 4.5.1. Xây dựng menu 124 4.5.2. Icons và Menu Items 124 4.5.3. Pop-Up Menu 125 4.5.4. Hiển thị và ẩn các mục trong menu 125 4.5.5. Toolbars 125 4.5.6. Tooltips 127 4.6. Dialog Boxes 127 4.6.1. Tạo Dialogs 127 4.6.2. Option Dialogs 128 4.6.3. Trao đổi dữ liệu 132 4.6.4. File Dialogs 132 4.6.5. Các lựa chọn màu 133 Chương 5. LUỒNG VÀ TẬP TIN 139 5.1. Mở đầu 139 5.2. Luồng 139 5.2.1. Khái niệm luồng 139 5.2.2. Luồng byte (Byte Streams) 139 5.2.3. Luồng ký tự (Character Streams) 140 5.2.4. Những luồng được định nghĩa trước (The Predefined Streams) 141 5.3. Sử dụng luồng Byte 141 5.3.1. Đọc dữ liệu từ Console 142 5.3.2. Xuất dữ liệu ra Console 143 5.3.3. Đọc và ghi file dùng luồng Byte 144 5.3.4. Đọc và ghi dữ liệu nhị phân 147 5.4. File truy cập ngẫu nhiên (Random Access Files) 150 5.5. Sử dụng luồng ký tự 152 5.5.1. Nhập Console dùng luồng ký tự 153 5.5.2. Xuất Console dùng luồng ký tự 155 4
  5. 5.5.3. Đọc/ghi File dùng luồng ký tự 156 5.6. Lớp File 158 Chương 6. ỨNG DỤNG GIAO TIẾP VỚI CƠ SỞ DỮ LIỆU 163 6.1. Giới thiệu 163 6.2. Kiến trúc JDBC, cách tạo ứng dụng JDBC 163 6.3. Các khái niệm cơ bản 163 6.3.1. JDBC Driver 164 6.3.2. JDBC URL 166 6.4. Kết nối cơ sở dữ liệu với JDBC 166 6.4.1. Đăng ký trình điều khiển 166 6.4.2. Thực hiện kết nối 167 6.4.3. Các ví dụ 168 6.5. Cách tạo truy vấn và các kiểu truy vấn 172 6.6. Mô tả Rowset, JDBCRowset và CatchedRowset 178 CHƯƠNG 7. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI SOCKET .183 7.1. Giới thiệu chung 183 7.2. Lập trình thao tác với địa chỉ máy trạm 183 7.2.1. Lập trình thao tác với địa chỉ IP, lớp URL Connection 183 7.2.2. Ví dụ sử dụng các phương thức lớp InetAddress 189 7.3. Lập trình ứng dụng mạng với TCP socket 190 7.3.1. Lớp Socket và lớp Server Socket 190 7.3.2. Kỹ thuật lập trình truyền thông với giao thức TCP 194 7.3.3. Một số ví dụ 197 7.4. Lập trình ứng dụng mạng với UDP Socket 200 7.4.1. Một số lớp Java hỗ trợ lập trình với UDP Socket 200 7.4.2. Kỹ thuật lập trình truyền thông với giao thức UDP 205 7.5. Lấy dữ liệu web 218 7.5.1. URL và URI 218 7.5.2. Sử dụng URLConnection để truy xuất dữ liệu 218 7.6. Gửi Email 218 7.6.2. Khái niệm Session 220 7.6.3. Cách tạo message 220 7.6.4. Các bước để tạo và gửi message 220 7.6.5. Các bước cần thiết để đọc một message 221 5
  6. 7.6.6. Các bước cần thiết để trả lời một email 222 TÀI LIỆU THAM KHẢO 226 6
  7. DANH MỤC THUẬT NGỮ TIẾNG ANH Từ Nghĩa của từ Abstract Trừu tượng Assert Được sử dụng để xác định vị trí lỗi chương trình nội bộ Break Dừng vòng lặp Catch Từ khóa đầu của một khối bắt ngoại lệ Continue Bỏ qua phần cuối vòng lặp, tiếp tục sang bước tiếp theo. Default Giá trị mặc định của hàm switch() Extends Kế thừa Final Một hằng số, phương thức hay một lớp không được ghi đè Finally Một phần của khối xử lý ngoại lệ try luôn được thực hiện Implements Định nghĩa giao diện mà lớp thực hiện Import Khai báo một gói thư viện Instanceof Kiểm tra một đối tượng là một thể hiện của lớp Interface Giao diện Native Một phương thức được thực hiện bởi hệ thống máy chủ new Tạo một đối tượng mới của lớp Null Tham chiếu rỗng Package Gói Private Tiền tố chỉ được truy cập bởi phương thức của lớp Tiền tố được truy cập bởi phương thức của lớp, lớp con Protected của và các lớp khác trong cùng một gói Public Tiền tố có thể được truy cập bởi phương thức của tất cả các lớp Return Trả về của một phương thức Super Gọi phương thức khởi tạo của lớp cha Synchronized Cho biết đây là một phương thức đồng bộ This Sử dụng như một tham chiếu đến đối tượng hiện tại 7
  8. LỜI NÓI ĐẦU Cùng với sự phát triển của khoa học, kỹ thuật, công nghệ thông tin ở nước ta trong những năm gần đây phát triển mạnh, đặc biệt là ngành công nghệ phần mềm. Sau khi ra đời, ngôn ngữ lập trình Java được sử dụng rộng rãi và phổ biến đối với các lập trình viên chuyên nghiệp cũng như các nhà phát triển phần mềm. Công nghệ Java đã được đưa vào giảng dạy sinh viên công nghệ thông tin ở một số trường Đại Học và các các cơ sở đào tạo lập trình viên chuyên nghiệp. Để đáp ứng với yêu cầu học tập của sinh viên chuyên ngành công nghệ thông tin, chúng tôi biên soạn giáo trình “Công nghệ Java”. Đây là học phần cơ sở của sinh viên chuyên ngành Đại học và Cao đẳng Công nghệ Thông tin. Học phần cung cấp cho sinh viên những kiến thức cơ bản và chuyên sâu về ngôn ngữ Java, trang bị cho sinh viên những kỹ năng viết phần mềm ứng dụng và phần mềm nhúng. Đây có thể xem là những kiến thức nền tảng cho các lập trình viên về công nghệ Java. Nội dung của giáo trình bao gồm 7 chương: Chương 1. Giới thiệu tổng quan về ngôn ngữ lập trình Java. Cách dịch và thực thi một chương trình, kiến trúc chương trình xây dựng trên Java và giới thiệu về lập trình hướng đối tượng. Chương 2. Trình bày những khái niệm căn bản về lập trình Java như các kiểu dữ liệu và toán tử cơ bản trong Java, các câu lệnh và cấu trúc lệnh trong Java. Chương 3. Trình bày chi tiết các khái niệm về lớp, lớp nội, đối tượng, kế thừa tính đa hình, xử lý ngoại lệ, xử lý đa luồng và tương tranh. Chương 4. Giới thiệu lập trình giao diện bằng SWING như: các thành phần, cách trình bày giao diện, xử lý các sự kiện Chương 5. Trình bày về luồng và tập tin như luồng byte, luồng ký tự, Chương 6. Trình bày thiết kế ứng dụng liên quan đến cơ sở dữ liệu: như kiến trúc JDBC, cách kết nối cơ sở dữ liệu với JDBC và các thao tác trên cơ sở dữ liệu. Kết thúc chương này sinh viên có thể viết phần mềm hoàn thiện giải quyết các bài toán quản lý. Chương 7. Giới thiệu lập trình mạng bằng socket, sử dụng các lớp được cung cấp trong JAVA. Đây là kiến thức nền hỗ trợ cho học phần Thực tập lập trình mạng học ở học kỳ tiếp theo. Trong mỗi chương đều có các câu hỏi ôn tập và câu hỏi thảo luận để tổng kết và kiểm tra lại các kiến thức trong mỗi chương. Chúng tôi hy vọng nội dung giáo trình sẽ giúp cho sinh viên những kiến thức cần thiết, làm cơ sở để có thể đi sâu vào thiết kế các phần mềm. Giáo trình được dùng để giảng dạy cho sinh viên ngành Công nghệ thông tin, song cũng có thể giúp sinh viên các ngành điện tử, viễn thông của các trường đại học cần tham khảo các vấn đề về lập trình Java. 8
  9. Chúng tôi xin chân thành cảm ơn Thầy Nguyễn Hoàng Chiến, phó chủ nhiệm, phụ trách khoa Công nghệ Thông tin trường Đại học Kinh tế Kỹ thuật Công nghiệp cùng với các đồng nghiệp đã đóng góp nhiều ý kiến quý báu cho sự thành công của cuốn tài liệu này. Vì tài liệu được biên soạn lần đầu, chúng tôi đã rất cố gắng hoàn chỉnh, song không tránh khỏi thiếu sót. Rất mong nhận được sự góp ý của bạn đọc để tài liệu học tập được hoàn thiện hơn. Xin trân trọng cảm ơn! Tác giả Vũ Văn Đốc Lương Thị Thảo Hiếu Lê Thanh Của 9
  10. Chương 1. TỔNG QUAN VỀ LẬP TRÌNH JAVA Mục đích Nội dung tập trung trình bày các vấn đề chính về ngôn ngữ lập trình Java: Lịch sử ra đời và phát triển của Java Kiến trúc tổng quát một chương trình xây dựng trên Java Các đặc điểm của Java, khái niệm máy ảo. Cấu trúc một chương trình Java đơn giản, cách xây dựng, dịch, thực thi một chương trình Java. 1.1. Lịch sử ra đời và phát triển của Java Năm 1991, một nhóm kỹ sư của Sun Microsystems muốn lập trình điều khiển các thiết bị điện tử như tivi, máy giặt Ban đầu, định dùng C và C++ nhưng trình biên dịch C/C++ phụ thuộc vào từng loại CPU. Do đó, họ đã bắt tay vào xây dựng một ngôn ngữ chạy nhanh, gọn, hiệu quả, độc lập thiết bị và ngôn ngữ “Oak” ra đời vào năm 1995, tương tự như C++ nhưng loại bỏ một số tính năng nguy hiểm của C++ và có khả năng chạy trên nhiều nền phần cứng khác nhau. Cùng lúc đó world wide web bắt đầu phát triển và Sun thấy được tiềm năng của ngôn ngữ Oak nên đã đầu tư cải tiến và phát triển. Ngôn ngữ lập trình Java được Sun Microsystems giới thiệu vào tháng 6 năm 1995 và nhanh chóng trở thành một ngôn ngữ lập trình của các lập trình viên chuyên nghiệp. Java được xây dựng dựa trên nền tảng của C và C++, Java sử dụng cú pháp của C và đặc trưng hướng đối tượng của C++. Java là ngôn ngữ vừa biên dịch vừa thông dịch. Đầu tiên mã nguồn được biên dịch thành dạng bytecode. Sau đó được thực thi trên từng loại máy nhờ trình thông dịch. Mục tiêu của các nhà thiết kế Java là cho phép người lập trình viết chương trình một lần nhưng có thể chạy trên các nền phần cứng khác nhau. Ngày nay, Java được sử dụng rộng rãi, không chỉ để viết ứng dụng trên máy cục bộ hay trên mạng mà còn để xây dựng các trình điều khiển thiết bị di động, PDA, 1.2. Đặc trưng ngôn ngữ Java Tính đơn giản Ngôn ngữ lập trình Java loại bỏ các đặc trưng phức tạp của C và C++ như: Loại bỏ thao tác con trỏ, thao tác định nghĩa chồng toán tử (operator overloading) , không cho phép đa kế thừa (Multi-inheritance) mà sử dụng giao diện (interface), không sử dụng lệnh “goto” cũng như file header (.h), loại bỏ cấu trúc “struct” và “union”. Tính hướng đối tượng Hướng đối tượng trong Java tương tự như C++ nhưng Java là một ngôn ngữ lập trình hướng đối tượng hoàn toàn. Tất cả mọi thứ đề cập đến trong Java đều liên quan đến các đối tượng được định nghĩa trước, thậm chí hàm chính của một chương trình viết 10
  11. bằng Java (hàm main) cũng phải đặt bên trong một lớp. Hướng đối tượng trong Java không có tính đa kế thừa (multi inheritance) như trong C++ thay vào đó Java đưa ra khái niệm interface hỗ trợ tính đa kế thừa. Tính độc lập với phần cứng và hệ điều hành Tính độc lập với phần cứng được hiểu theo nghĩa một chương trình Java, nếu chạy đúng trên phần cứng của một họ máy nào đó thì cũng chạy đúng trên tất cả các họ máy khác. Một chương trình chỉ chạy đúng trên một số họ máy cụ thể được gọi là phụ thuộc vào phần cứng. Tính độc lập với hệ điều hành được hiểu theo nghĩa một chương trình Java có thể chạy được trên tất cả các hệ điều hành. Một chương trình chỉ chạy được trên một số hệ điều hành được gọi là phụ thuộc hệ điều hành. Các chương trình viết bằng Java có thể chạy trên hầu hết các hệ nền mà không cần phải thay đổi gì, những người lập trình đặt cho nó một khẩu hiệu ‘viết một lần, chạy mọi nơi’, điều này là không thể có với các ngôn ngữ lập trình khác. Tính mạnh mẽ Java là ngôn ngữ yêu cầu chặt chẽ về kiểu dữ liệu, việc ép kiểu tự động bừa bãi của C, C++ được hạn chế trong Java, điều này làm chương trình rõ ràng, sáng sủa, ít lỗi hơn. Java kiểm tra lúc biên dịch và cả trong thời gian thông dịch vì vậy loại bỏ một số loại lỗi lập trình nhất định. Java không sử dụng con trỏ và các phép toán con trỏ. Java kiểm tra tất cả các truy nhập đến mảng, chuỗi khi thực thi để đảm bảo rằng các truy nhập đó không ra ngoài giới hạn kích thước. Trong các môi trường lập trình truyền thống, lập trình viên phải tự mình cấp phát bộ nhớ. Trước khi chương trình kết thúc thì phải tự giải phóng bộ nhớ đã cấp. Trong chương trình Java, lập trình viên không phải bận tâm đến việc cấp phát. Quá trình cấp phát, giải phóng được thực hiện tự động, nhờ dịch vụ thu gom rác (garbage collection). Đồng thời cơ chế bẫy lỗi của Java giúp đơn giản hóa quá trình xử lý lỗi và hồi phục sau lỗi. Tính an toàn Java cung cấp một môi trường quản lý thực thi chương trình với nhiều mức để kiểm soát tính an toàn: Ở mức thứ nhất, mức ngôn ngữ: Dữ liệu và các phương thức được đóng gói bên trong lớp. Chúng chỉ được truy xuất thông qua các giao diện mà lớp cung cấp. Ở mức thứ hai, mức Compiler: Trình biên dịch kiểm tra an toàn cho code trước khi biên dịch. Mức thứ ba được đảm bảo bởi trình thông dịch: Chúng kiểm tra xem bytecode có đảm bảo các qui tắc an toàn trước khi thực thi. Mức thứ tư, mức class: Kiểm soát việc nạp các lớp vào bộ nhớ để giám sát việc vi phạm giới hạn truy xuất trước khi nạp vào hệ thống. 11
  12. Tính phân tán Java được thiết kế để hỗ trợ các ứng dụng chạy trên mạng bằng các lớp mạng nằm trong gói (Java.net). Cụ thể là Java có hỗ trợ công nghệ lập trình RMI, CORBA, JavaBean. Các công nghệ này cho phép sử dụng lại các lớp đã tạo ra, triệu gọi các phương thức (method) hoặc các đối tượng từ một máy ở xa. Tính đa luồng Tính năng này cho phép viết một chương trình có nhiều đoạn mã lệnh được chạy song song với nhau. Với Java có thể viết các chương trình có khả năng chạy song song một cách dễ dàng, hơn thế nữa việc đồng bộ tài nguyên dùng chung trong Java cũng rất đơn giản. Điều này không thể có đối với một số ngôn ngữ lập trình khác như C/C++, pascal Tính động Java được thiết kế như một ngôn ngữ động để đáp ứng cho những môi trường mở. Các chương trình Java bổ sung các thông tin cho các đối tượng tại thời gian thực thi. Điều này cho phép khả năng liên kết động các mã. 1.3. Các ứng dụng của Java Ứng dụng thực thi qua dòng lệnh (Console) Ứng dụng Console là ứng dụng nhập xuất ở chế độ văn bản tương tự như màn hình Console của hệ điều hành MS-DOS. Các ứng dụng kiểu Console thường được dùng để minh họa các ví dụ cơ bản liên quan đến cú pháp ngôn ngữ, các thuật toán, và các chương trình ứng dụng không cần thiết đến giao diện người dùng đồ họa. Ứng dụng Java Applet Applet là chương trình Java được tạo ra để sử dụng trên Internet thông qua các trình duyệt hỗ trợ Java như IE hay Netscape. Applet được nhúng bên trong trang Web. Khi Web hiển thị trong trình duyệt, Applet sẽ được tải về và thực thi tại trình duyệt. Ứng dụng giao diện (GUI Application) Việc phát triển các chương trình ứng dụng có giao diện đồ họa trực quan đã được Java giải quyết bằng thư viện AWT và JFC. JFC (Swing) là thư viện rất phong phú và hỗ trợ mạnh mẽ hơn nhiều so với AWT. JFC giúp cho người lập trình có thể tạo ra một giao diện trực quan của bất kỳ ứng dụng nào. Ứng dụng web Java hỗ trợ mạnh mẽ đối với việc phát triển các ứng dụng Web thông qua công nghệ J2EE (Java 2 Enterprise Edition). Công nghệ J2EE hoàn toàn có thể tạo ra các ứng dụng Web một cách hiệu quả không thua kém công nghệ .NET mà Microsft đang quảng cáo. Công nghệ web hiện có của Java là Servlet và JSP, ngoài ra còn có sự hỗ trợ của lập trình Socket, Java Bean, RMI và CORBA, EJB. Ứng dụng lập trình mạng 12
  13. Có thể nói Java hỗ trợ rất mạnh về lập trình ứng mạng với các lớp thư viện socket giúp đơn giản hóa quá trình kết nối và chuyển dữ liệu trên mạng. Java cho phép người lập trình có thể lập trình trên rất nhiều giao thức TCP/ IP, UDP, HTTP, FTP, Telnet Những ứng dụng điển hình: Các chương trình chat, chương trình chuyển dữ liệu, truyền tin thông báo tin tức, trao đổi trực tuyến, gửi nhận thư điện tử Ứng dụng cơ sở dữ liệu CSDL là yếu tố không thể thiếu trong trong hầu hết các ứng dụng, việc hỗ trợ truy xuất CSDL không phải lúc nào cũng đầy đủ trong các ngôn ngữ lập trình. Ví dụ VB cung cấp rất nhiều cơ chế truy xuất CSDL thuận tiện như ODBC, ADO, ADO.NET, OLEDB trong khi C/C++ không có. Java hỗ trợ các cơ chế truy xuất CSDL mở với công nghệ JDBC (Java Database Connectivity), cho phép truy xuất đến nhiều loại CSDL khác nhau như Oracle, DB2. MySQL, SQL Server . JDBC cho phép truy cập dữ liệu thông qua mô hình đa luồng rất thích hợp cho các ứng dụng phân tán. 1.4. Dịch và thực thi một chương trình viết bằng Java Việc xây dựng, dịch và thực thi một chương trình viết bằng Java có thể tóm tắt qua các bước sau: Viết mã nguồn: Dùng một chương trình soạn thảo nào đó (NotePad hay Jcreator chẳng hạn) để viết mã nguồn và lưu lại với tên có đuôi “. Java”. Biên dịch ra mã máy ảo: Dùng trình biên dịch Javac để biên dịch mã nguồn “.Java” thành mã của máy ảo (Java bytecode) có đuôi “.class” và lưu lên đĩa. Thông dịch và thực thi: Ứng dụng được load vào bộ nhớ, thông dịch và thực thi dùng trình thông dịch Java thông qua lệnh “Java”. Đưa mã Java bytecode vào bộ nhớ: đây là bước “loading”. Chương trình phải được đặt vào trong bộ nhớ trước khi thực thi. “Loader” sẽ lấy các files chứa mã Java bytecode có đuôi “.class” và nạp vào bộ nhớ. Kiểm tra mã Java bytecode: trước khi trình thông dịch chuyển bytecode thành mã máy tương ứng để thực thi thì mã bytecode phải được kiểm tra tính hợp lệ. Thông dịch & thực thi: cuối cùng dưới sự điều khiển của CPU và trình thông dịch tại mỗi thời điểm sẽ có mã bytecode được chuyển sang mã máy và thực thi. 1.5. Kiến trúc chương trình xây dựng trên Java 1.5.1. Kiến trúc chương trình Java Dạng cơ bản của một tập tin mã nguồn Java có cấu trúc như sau : package packageName; // Khai báo tên gói, nếu có import java.awt.*; // Khai báo tên thư viện sẵn có, nếu cần dùng class className // Khai báo tên lớp 13
  14. { /* Đây là dòng chú thích, nếu cần */ int var; // Khai báo biến public void methodName() // Khai báo tên phương thức { /* Phần thân của phương thức */ // Các lệnh thực hiện trong thân phương thức } } Một tệp mã nguồn Java có thể có ba phần chính: Phần khai báo tên gói (khối) bằng từ khoá package. Phần khai báo thư viện tham khảo bằng từ khoá import. Phần khai báo nội dung lớp bằng từ khoá class. Khai báo Package Package dùng để đóng gói các lớp trong chương trình lại với nhau thành một khối. Đây là một cách hữu hiệu để lưu trữ các lớp gần giống nhau hoặc có cùng một module thành một khối thống nhất. Cú pháp khai báo tên gói bằng từ khoá package: package ; Để đặt tên package trong chương trình, người ta có thể tiến hành như đặt tên thư mục trên ổ đĩa. Nghĩa là bắt dầu bằng tên có phạm vi lớn, cho đến các tên có phạm vi nhỏ, cuối cùng là tên các gói trực tiếp chứa các lớp. Phạm vi đặt tên gói, trên thực tế, thường được tiến hành theo thứ tự phạm vi lớn đến nhỏ như sau: Tên tổ chức Tên công ty Tên dự án Tên modul trong dự án Tên các chức năng trong modul Ưu điểm của package: Cho phép nhóm các lớp vào với nhau thành các đơn vị nhỏ hơn. Việc thao tác trên các đơn vị khối sẽ gọn hơn thao tác trên một tập các lớp. Tránh việc xung đột khi đặt tên lớp. Khi số lượng lớp của chương trình quá lớn ta có thể tránh phải đặt tên khác nhau cho các lớp bằng cách đặt chúng vào các package khác nhau. 14
  15. Cho phép bảo vệ các lớp. Khi chương trình lớn, việc chia nhỏ chương trình thành các package sẽ thuận lợi hơn cho việc quản lí và phát triển. Tên gói còn được dùng để định danh lớp trong ứng dụng. Lưu ý: Các tệp tin của các lớp nằm cùng gói ứng dụng phải được lưu trong cùng một thư mục (tên thư mục là tên khối) theo cấu trúc khối của dự án. Tên gói nên đặt theo chữ thường vì tên gói sẽ là tên thư mục tương ứng trong ổ đĩa, tránh nhầm lẫn với tên các tệp tin là tên các lớp của chương trình. Khai báo thư viện Khai báo thư viện để chỉ ra những thư viện đã được định nghĩa sẵn mà chương trình sẽ tham khảo tới. Cú pháp khai báo thư viện với từ khoá import như sau: import ; Java chuẩn cung cấp một số thư viện như sau: java.lang: cung cấp các hàm thao tác trên các kiểu dữ liệu cơ bản, xử lí lỗi và ngoại lệ, xử lí vào ra trên các thiết bị chuẩn như bàn phím và màn hình. java.applet: cung cấp các hàm cho xây dựng các applet java.awt: cung cấp các hàm cho xây dựng các ứng dụng đồ hoạ với các thành phần giao diện đa phương tiện multimedia. java.io: cung cấp các hàm xử lí vào/ra trên các thiêt bị chuẩn, các thiết bị ngoại vi. java.util: cung cấp các hàm tiện ích trong xử lí liên quan đến các kiểu dữ liệu có cấu trúc như Date, Stack, Vector. Lưu ý: Nếu muốn khai báo tham khảo nhiều thư viện, phải khai báo tham khảo mỗi thư viện với một từ khoá import. Nếu chỉ tham khảo một vài lớp trong một thư viện, nên chỉ rõ tham khảo lớp nào, thay vì phải khai báo tham khảo cả gói (bằng kí hiệu “*”) vì tham khảo cả gói sẽ tăng kích cỡ tệp tin class sau khi biên dịch. Nếu không tham khảo thư viện nào, không cần thiết phải khai báo các tham khảo với từ khoá import. Khai báo lớp Phần thứ ba là phần khai báo lớp và nội dung của lớp, phần này luôn bắt buộc phải có đối với một tệp mã nguồn Java: Khai báo tên lớp với từ khoá class. Khái báo các thuộc tính của lớp. Khai báo các phương thức của lớp 15
  16. 1.5.2. Chương trình Java đầu tiên. 1.5.2.1. Công cụ soạn thảo mã nguồn Java. Để viết mã nguồn Java chúng ta có thể sử dụng trình soạn thảo NotePad hoặc một số môi trường phát triển hỗ trợ ngôn ngữ Java như: Jbuilder của hãng Borland, JDeveloper của hãng Oracle, Visual J++ của Microsoft Trong khuôn khổ giáo trình này để hướng dẫn sinh viên thực hành chúng tôi dùng công cụ NetBeans IDE 7.0 được phát triển bởi Sun Microsystems. NetBean IDE: Là môi trường phát triển - một công cụ dành cho lập trình viên để viết, biên dịch, gỡ lỗi (debug) và triển khai (deploy) chương trình. Chương trình được viết bằng Java nhưng có thể hỗ trợ bất kỳ ngôn ngữ lập trình nào. Với giao diện thân thiện, được coi là phổ biến và hiệu quả nhất, một sản phẩm miễn phí và không có giới hạn trong việc sử dụng, NetBean IDE là lựa chon tối ưu nhất để viết Java. NetBean IDE dễ dàng cài đặt và chạy trên nhiều hệ điều hành, bao gồm Windows, Linux, hệ điều hành Mac OS X và Solaris có thể download Netbean IDE tại: netbeans.org/downloads/ Ví dụ. Để tạo và thực thi chương trình có tên HelloWorldApp như sau: Bước 1. Tạo Project: Chọn File/NewProject: Hình 1.1. Tạo project mới Tại mục Categories ta chọn Java. Tại mục Projects ta chọn Java Application rồi chọn Next. Nhập Project Name và chọn Finish. 16
  17. Hình 1. 2. Vị trí lưu project Bước 2. Soạn thảo mã nguồn tại cửa sổ bên dưới: Hình 1.3. Màn hình soạn thảo 1.5.2.2. Chương trình Java. Chương trình sau cho phép hiển thị thông điệp: "Hello World Application!" package helloworldapp; / *File: HelloWorldApp.Java * @author Admin */ public class HelloWorldApp { / * @param args the command line arguments */ 17
  18. public static void main(String[] args) { // TODO code application logic here System.out.println("Hello World Applications!"); } } Để thực thi chương trình ta vào Run/Run Main Project hoặc Bấm Run Main Project () trên thanh công cụ hoặc bấm phím F6. Kết quả hiển thị trên màn hình: 1.5.2.3. Phân tích chương trình đầu tiên. Tập tin HelloWorldApp.Java sẽ nằm trong gói helloworldapp /* *File: HelloWorldApp.Java * @author Admin*/ Ký hiệu “/* */ ” dùng để chú thích nhiều dòng lệnh. Trình biên dịch sẽ bỏ qua các dòng chú thích này. Java hỗ trợ hai loại chú thích: Loại chú thích trên một dòng, dùng “//”. Trình biên dịch sẽ bỏ qua nội dung bắt đầu từ kí hiệu “//” cho đến hết dòng lệnh chứa nó. Loại chú thích trên nhiều dòng có thể bắt đầu với “/*” và kết thúc với “*/”. Trình biên dịch sẽ bỏ qua nội dung nằm giữa hai kí hiệu này. Dòng kế tiếp khai báo lớp có tên HelloWorldApp: Bắt đầu với từ khoá public, kế đến là từ khóa class, cuối cùng là tên lớp: public class HelloWorldApp { } Một định nghĩa lớp nằm trọn vẹn giữa hai ngoặc móc mở “{“ và đóng “}”. Các ngoặc này đánh dấu bắt đầu và kết thúc một khối lệnh. public static void main(String args[ ]) Đây là phương thức chính, từ đây chương trình bắt đầu việc thực thi của mình. Tất cả các ứng dụng Java đều sử dụng một phương thức main này. Từ khoá public là một chỉ định truy xuất. Nó cho biết thành viên của lớp có thể được truy xuất từ bất cứ đâu trong chương trình. Từ khoá static cho phép main được gọi tới mà không cần tạo ra một thể hiện (instance) của lớp. Nó không phụ thuộc vào các thể hiện của lớp được tạo ra. 18
  19. Từ khoá void thông báo cho máy tính biết rằng phương thức sẽ không trả lại bất cứ giá trị nào khi thực thi chương trình. String args[] là tham số dùng trong phương thức main. Khi không có một thông tin nào được chuyển vào main, phương thức được thực hiện với các dữ liệu rỗng - không có gì trong dấu ngoặc đơn. System.out.println("Hello World Applications!"); Dòng lệnh này hiển thị chuỗi “Hello World Applications!” trên màn hình. Lệnh println() cho phép hiển thị chuỗi được truyền vào lên màn hình. 19
  20. Chương 2. CẤU TRÚC LẬP TRÌNH CƠ BẢN TRONG JAVA Mục đích Nội dung chương này trình bày khái niệm cơ bản về lập trình trong Java như: Các kiểu dữ liệu, cách khai báo biến và các toán tử. Cách nhập xuất dữ liệu Các cấu trúc lệnh trong Java như các cấu trúc lựa chọn và các vòng lặp Mảng và các thao tác cơ bản trên kiểu dữ liệu mảng trong Java 2.1. Các kiểu dữ liệu Kiểu dữ liệu cơ bản Kiểu số nguyên. Kiểu dữ liệu Kích Phạm vi biểu diễn Giá trị byte thước8 bits -128 ÷127 mặc định0 short 16 bits - 32768 ÷ 32767 0 int 32 bits -231 ÷ 231-1 0 long 64 bits -263 ÷ 263-1 0l Kiểu số thực Kiểu dữ liệu Kích Phạm vi biểu diễn Giá trị float thước32 bits -3.4e38÷3.4e38 0.0f double 64 bits -1.7976 E+308÷+ 0.00d 1.7976 E+308 1791.7e308÷1.7e308 Kiểu logic (boolean) Nhận giá trị true hoặc false. Giá trị mặc định là false Kiểu ký tự (char) Kiểu dữ liệu Kích Phạm vi biểu diễn Giá trị char thước16 bits \u0000÷\uffff 0 Kiểu dữ liệu đối tượng : Trong Java, có 3 kiểu dữ liệu đối tượng: array: Một mảng của các dữ liệu cùng kiểu class: Dữ liệu kiểu lớp đối tượng do người dùng định nghĩa. interface: Dữ liệu kiểu lớp giao diện do người dùng định nghĩa. Chứa các phương thức của giao diện. Ép kiểu Chuyển đổi giữa các kiểu dữ liệu cơ sở: 20
  21. float byte short int long float double Hình 2. 1. Chuyển đổi giữa các kiểu dữ liệu cơ sở Trong Java có hai loại ép kiểu dữ liệu: Mở rộng (widening): quá trình làm tròn số từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu có kích thước lớn hơn (theo chiều mũi tên nét liền). Kiểu biến đổi này không làm mất thông tin. Ví dụ chuyển từ int sang long. Chuyển kiểu loại này có thể được thực hiện ngầm định bởi trình biên dịch. Thu hẹp (narrowwing): quá trình làm tròn số từ kiểu dữ liệu có kích thước lớn hơn sang kiểu có kích thước nhỏ hơn (theo chiều mũi tên nét đứt). Kiểu biến đổi này có thể làm mất thông tin. Chuyển kiểu loại này không thể thực hiện ngầm định bởi trình biên dịch, người dùng phải thực hiện chuyển kiểu tường minh. Qui tắc ép kiểu có dạng: ( ) Trong đó: : kiểu dữ liệu cần ép sang : là biểu thức cần ép kiểu cho giá trị trả về của biểu thức Ví dụ 2.1: float fNum = 2.2; int iCount = (int) fNum 2.2. Biến Biến là vùng nhớ dùng để lưu trữ các giá trị của chương trình. Tên biến phải bắt đầu bằng một chữ cái, một dấu gạch dưới hay dấu dollar. Tên biến có phân biệt chữ hoa chữ thường Trong Java, biến có thể được khai báo ở bất kỳ đâu trong chương trình Cú pháp khai báo biến: dataType varName; hoặc dataType varName = value; Trong đó, dataType là kiểu dữ liệu của biến, varName là tên biến, value là giá trị khởi tạo ban đầu cho biến varName. Phạm vi hoạt động của biến Một biến có phạm vi hoạt động trong toàn bộ khối lệnh mà nó được khai báo. Một khối lệnh bắt đầu bằng dấu “{” và kết thúc bằng dấu “}”: 21
  22. Nếu biến được khai báo trong một phương thức (Không nằm trong khối lệnh nào), biến đó có phạm vi hoạt động trong phương thức tương ứng: có thể được sử dụng trong tất cả các khối lệnh của phương thức. Nếu biến được khai báo trong một lớp (Không nằm trong trong một phương thức nào), biến đó có phạm vi hoạt động trong toàn bộ lớp tương ứng: có thể được sử dụng trong tất cả các phương thức của lớp. 2.3. Toán tử Toán tử số học: Các toán hạng của các toán tử số học phải ở dạng số hoặc ký tự. Các toán hạng kiểu boolean không sử dụng được. Một vài kiểu toán tử được liệt kê trong bảng dưới đây: Toán tử Mô tả + Trả về giá trị tổng hai toán hạng - Trả về kết quả của phép trừ. * Nhân. Trả về giá trị là tích hai toán hạng. / Chia.Trả về giá trị là thương của phép chia % Phép lấy modul Giá trị trả về là phần dư của phép chia ++ Tăng giá trị của biến lên 1. Ví dụ a++ tương đương với a = a + 1 Giảm giá trị của biến 1 đơn vị. Ví dụ a tương đương với a = a - 1 += Ví dụ c += a tương đương c = c + a -= Giảm giá trị của biến 1 đơn vị. Ví dụ a tương đương với a = a - 1 *= Ví dụ c *= a tương đương với c = c*a /= Ví dụ c /= a tương đương với c = c/a %= Ví dụ c %= a tương đương với c = c%a Các toán tử logic: Toán tử Mô tả Và (AND) Trả về giá trị “Đúng” (True) chỉ khi cả hai toán tử có giá && trị “True” || Hoặc (OR) Trả về giá trị “True” nếu ít nhất một giá trị là True ! NOT. Chuyển giá trị từ True sang False và ngược lại. Các toán tử điều kiện: Toán tử điều kiện là một loại toán tử đặc biệt vì nó bao gồm ba thành phần cấu thành biểu thức điều kiện. 22
  23. Cú pháp: ? : ; Trong đó: biểu thức 1: Biểu thức logic. Trả trả về giá trị True hoặc False biểu thức 2: Là giá trị trả về nếu xác định là True biểu thức 3: Là giá trị trả về nếu xác định là False Toán tử gán Toán tử gán (=) dùng để gán một giá trị vào một biến và có thể gán nhiều giá trị cho nhiều biến cùng một lúc. Ví dụ đoạn lệnh sau gán một giá trị cho biến x và giá trị này lại được gán cho nhiều biến trên một dòng lệnh đơn. int x = 20; int p, q, r, s; p=q=r=s=x; Dòng lệnh cuối cùng được thực hiện từ phải qua trái. Đầu tiên giá trị ở biến x được gán cho ‘s’, sau đó giá trị của ‘s’ được gán cho ‘r’ và cứ tiếp như vậy. 2.4. Strings và StringBuider 2.4.1. Lớp String String là một class rất quan trọng trong Java. Class String là không thể thay đổi (immutable) và là final (không cho phép class nào thừa kế nó), tất cả các thay đổi trên String đều tạo ra một đối tượng String khác. Trong java, String là một class đặc biệt, nguyên nhân là nó được sử dụng một cách thường xuyên trong một chương trình, vì vậy đòi hỏi phải có hiệu suất và sự mềm dẻo. Đó là lý do tại sao String vừa có tính đối tượng và vừa có tính nguyên thủy (primitive). Tính nguyên thủy: Có thể tạo một string literal (chuỗi chữ), string literal được lưu trữ trong ngăn xếp (stack), đòi hỏi không gian lưu trữ ít String literal = "Hello World"; Có thể sử dụng toán tử + để nối 2 string, toán tử này vốn quen thuộc và sử dụng cho các kiểu dữ liệu nguyên thủy int, float, double. Các string literal được chứa trong một bể chung (common pool). Như vậy hai string literal có nội dung giống nhau sử dụng chung một vùng bộ nhớ trên stack, điều này giúp tiết kiệm bộ nhớ. Tính đối tượng Vì String là một class, vì vậy nó có thể được tạo ra thông qua toán tử new. String object = new String("Hello World"); 23
  24. Các đối tượng String được lưu trữ trên Heap, yêu cầu quản lý bộ nhớ phức tạp và tốn không gian lưu trữ. Hai đối tượng String có nội dung giống nhau lưu trữ trên 2 vùng bộ nhớ khác nhau của Heap. Các phương thức của String Phương thức Mô tả Trả về một ký tự tại vị trí có chỉ số được char charAt (int index) chỉ định. int compareTo (Object o) So sánh một String với một Object khác. So sánh hai chuỗi theo từ điển. Int compareTo (String anotherString) (Phân biệt chữ hoa chữ thường) So sánh hai chuỗi theo từ điển. int compareToIgnoreCase (String str) (Không phân biệt chữ hoa chữ thường) String concat (String str) Nối chuỗi Trả về true nếu và chỉ nếu chuỗi này đại boolean contentEquals (StringBuffer sb) diện cho cùng một chuỗi ký tự như là StringBuffer quy định. Trả về một chuỗi đại diện cho chuỗi ký tự static String copyValueOf (char[] data) trong mảng quy định. static String copyValueOf (char[] data, int Trả về một chuỗi đại diện cho chuỗi ký tự offset, int count) trong mảng quy định. Kiểm tra nếu chuỗi này kết thúc với hậu boolean endsWith (String suffix) tố quy định. boolean equals (Object anObject) So sánh với một đối tượng Boolean equalsIgnoreCase (String So sánh với một String khác, không phân anotherString) biệt chữ hoa chữ thường. Mã hóa chuỗi này thành một chuỗi các byte bằng cách sử dụng bảng mã mặc byte[] getBytes ( ) định của flatform (nền tảng), lưu trữ kết quả vào một mảng byte mới. 24
  25. Mã hóa chuỗi này thành một chuỗi các byte bằng cách sử dụng bảng mã cho byte[] getBytes (String charsetName) trước, lưu trữ kết quả vào một mảng byte mới. void getChars (int srcBegin, int srcEnd, Copy các ký tự từ chuỗi này vào mảng ký char[] dst, int dstBegin) tự đích. Trả về chỉ số trong chuỗi này xuất hiện int indexOf (int ch) đầu tiên của ký tự cụ thể. Trả về chỉ số trong chuỗi này xuất hiện int indexOf (int ch, int fromIndex) đầu tiên của ký tự được chỉ định, bắt đầu tìm kiếm từ chỉ số cụ thể đến cuối. Trả về chỉ số trong chuỗi này xuất hiện int indexOf (String str) đầu tiên của chuỗi quy định. Trả về chỉ số trong chuỗi này xuất hiện int indexOf(String str, int fromIndex) đầu tiên của chuỗi quy định, bắt đầu từ chỉ số xác định. Trả về chỉ số trong chuỗi này về sự xuất int lastIndexOf (int ch) hiện cuối cùng của ký tự cụ thể. Trả về chỉ số trong chuỗi này về sự xuất int lastIndexOf (int ch, int fromIndex) hiện cuối cùng của ký tự được chỉ định, tìm kiếm lùi lại bắt đầu từ chỉ số xác định. Trả về chỉ số trong chuỗi này xảy ra cuối int lastIndexOf (String str) cùng bên phải của chuỗi quy định. int length ( ) Trả về độ dài chuỗi. Kiểm tra chuỗi này khớp với biểu thức boolean matches (String regex) chính quy chỉ định hay không. boolean regionMatches (int offset, String Kiểm tra chuỗi có một phần giống nhau. other, int ooffset, int len) Trả về một chuỗi mới từ thay thế tất cả String replace (char oldChar, char các lần xuất hiện của ký tự oldChar trong newChar) chuỗi này với ký tự newChar. 25
  26. Thay thế tất cả các chuỗi con của chuỗi String replaceAll (String regex, String này khớp với biểu thức chính quy bởi replacement) String mới replacement Thay thế chuỗi con đầu tiên của chuỗi này String replaceFirst (String regex, String khớp với biểu thức chính quy bởi một replacement) String mới replacement Tách chuỗi này thành các chuỗi con, tại String[] split (String regex) các chỗ khớp với biểu thức chính quy cho trước. Kiểm tra nếu chuỗi này bắt đầu với tiền tố boolean startsWith (String prefix) quy định. CharSequence subSequence (int Trả về một chuỗi ký tự mới là một dãy beginIndex, int endIndex) con của dãy này. String substring (int beginIndex, int Trả về một chuỗi ký tự mới là một dãy endIndex) con của dãy này. Từ chỉ số bắt đầu cho tới chỉ số cuối. char[] toCharArray ( ) Chuyển chuỗi này thành mảng ký tự. String toLowerCase ( ) Chuyển tất cả các ký tự của chuỗi này sang chữ thường, sử dụng miền địa phương mặc định (default locale) String toLowerCase (Locale locale) Chuyển tất cả các ký tự của chuỗi này sang chữ thường, sử dụng miền địa phương (locale) cho trước. String toString ( ) Trả về String này. String toUpperCase ( ) Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương mặc định (default locale) String toUpperCase (Locale locale) Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương (locale) cho trước. 26
  27. String trim ( ) Trả về một String mới, sau khi loại bỏ các ký tự trắng bên trái và bên phải. 2.4.2. StringBuffer và StringBuilder StringBuilder và StringBuffer rất giống nhau, điều khác biệt là tất cả các phương thức của StringBuffer đã được đồng bộ, nó thích hợp khi làm việc với ứng dụng đa luồng, nhiều luồng có thể truy cập vào một đối tượng StringBuffer cùng lúc. Trong khi đó StringBuilder có các phương thức tương tự nhưng không được đồng bộ, vì vậy hiệu suất của nó cao hơn, nên sử dụng StringBuilder trong ứng dụng đơn luồng, hoặc sử dụng như một biến địa phương trong một phương thức. Khai báo: StringBuffer(): tạo một bộ đệm chuỗi trống với dung lượng ban đầu là 16. StringBuffer(String str): tạo một bộ đệm chuỗi với chuỗi đã xác định. StringBuffer(int capacity): tạo một bộ đệm chuỗi trống với dung lượng đã cho. Các phương thức quan trọng của lớp StringBuffer trong Java public synchronized StringBuffer append (String s): Sử dụng để thêm (append) chuỗi đã cho vào chuỗi hiện tại. Phương thức append() được nạp chồng giống dạng append(char), append(boolean), append(int), append(float), append(double). public synchronized StringBuffer insert (int offset, String s): Chèn chuỗi đã cho vào chuỗi hiện tại, tại vị trí đã cho. Phương thức insert() được nạp chồng giống dạng insert (int, char), insert (int, boolean), insert (int, int), insert (int, float), insert (int, double) public synchronized StringBuffer replace(int startIndex, int endIndex, String str): Thay thế chuỗi từ chỉ mục ban đầu startIndex đến chỉ mục kết thúc endIndex. public synchronized StringBuffer delete(int startIndex, int endIndex):xóa chuỗi từ chỉ mục startIndex và endIndex đã cho. public synchronized StringBuffer reverse(): Đảo ngược chuỗi. public char charAt(int index): Được sử dụng để trả về ký tự tại vị trí đã cho. public int length(): Được sử dụng để trả về độ dài của chuỗi public String substring(int beginIndex): Trả về chuỗi con từ chỉ mục bắt đầu beginIndex đã cho. public String substring(int beginIndex, int endIndex): Trả về chuỗi con từ beginIndex đến endIndex đã cho. 2.5. Nhập/Xuất dữ liệu Trong Java có 3 cách nhập liệu từ bàn phím: a. Sử dụng lớp BufferReader BufferReader là một lớp dùng để đọc dữ liệu từ bàn phím hay từ file. 27
  28. Có thể dùng lớp này để đọc một chuỗi một mảng hoặc một kí tự. Ví dụ 2.2: import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class GetInputFromKeyboard { public static void main(String[] args) { // Tạo một đối tượng BufferedReader BufferedReader dataIn = new BufferedReader(new InputStreamReader( System.in) ); // Biến name String name = ""; System.out.println("Please Enter Your Name:"); try { name = dataIn.readLine(); } catch( IOException e ) { System.out.println("Error!"); } // hiển thị tên System.out.println("Hello " + name +"!"); } } Kết quả: Tham số đầu vào của BufferReader có thể là InputStreamReader hoặc FileReader (Dùng để đọc file). 28
  29. Một số phương thức của lớp BufferReader: - read() : đọc một kí tự. - readLine() : đọc một dòng text. b. Sử dụng JOptionPane JOptionPane là một lớp thừa kế từ lớp JComponent. Khi biên dịch chương trình sẽ hiện lên một dialog box cho phép nhập dữ liệu. Ví dụ 2.3: import javax.swing.JOptionPane; public class InputFromKeyboardJOptionPane { public static void main(String[] args) { String name = ""; name=JOptionPane.showInputDialog("Please enter your name"); String msg = "Hello " + name + "!"; JOptionPane.showMessageDialog(null, msg); System.out.println("Name is:"+msg); } } Kết quả: Một số phương thức của JOptionPane: - showConfirmDialog() : Hiển thị một câu hỏi lựa chọn giống như yes no cancel - showInputDialog() : Hiển thị box nhập - showMessageDialog() : Báo cho người dùng một sự kiện vừa xảy ra. c) Sử dụng lớp Scanner - Lớp Scanner trong Java cho phép nhập dữ liệu từ bàn phím. - Lớp Scanner có thể đọc được nhiều kiểu dữ liệu khác nhau chẳng hạn Int, Long, Double, String, Float. Một số phương thức thường được sử dụng trong lớp Scanner: 29
  30. Phương thức Mô tả public String next() Trả về kết quả nội dung trước khoảng trắng public String nextLine() Trả về kết quả nội dung của một hàng public byte nextByte() Trả về kiểu dữ liệu byte public short nextShort() Trả về kiểu dữ liệu short public int nextInt() Trả về kiểu dữ liệu int public long nextLong() Trả về kiểu dữ liệu long public float nextFloat() Trả về kiểu dữ liệu float public double nextDouble() Trả về kiểu dữ liệu double Sự khác nhau khi sử dụng next() và nextLine() lớp Scanner trong Java: – Phương thức next() sẽ trả về kết quả cách nhau bởi khoảng trắng. Ví dụ 2.4: import java.util.Scanner; public class Main { public static void main(String[] args) { String str = "Chào mừng bạn đến với \nKênh Lập Trình"; Scanner scanner = new Scanner(str); while(scanner.hasNext()){ System.out.println(scanner.next()); } scanner.close(); } Kết quả: Phương thức nextLine() sẽ trả về kết quả nội dung của một hàng, ví dụ: 30
  31. import java.util.Scanner; public class Main { public static void main(String[] args) { String str = "Chào mừng bạn đến với \nKênh Lập Trình"; Scanner scanner = new Scanner(str); while(scanner.hasNext()){ System.out.println(scanner.nextLine()); } scanner.close(); } } Sử dụng lớp Scanner nhập dữ liệu từ bàn phím 2 số hạng, và tính tổng. package exam; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Vui lòng nhập số hạng thứ nhất: "); int sothu1 = scanner.nextInt(); System.out.print("Vui lòng nhập số hạng thứ hai: "); int sothu2 = scanner.nextInt(); System.out.println("Tính tổng: " + (sothu1 + sothu2)); scanner.close(); }} Kết quả: 2.6. Câu lệnh và các cấu trúc lệnh trong Java Java cung cấp hai loại cấu trúc điều khiển: Điều khiển rẽ nhánh 31
  32. Mệnh đề if-else Mệnh đề swich-case Vòng lặp (Loops) Vòng lặp while Vòng lặp do-while Vòng lặp for 2.6.1. Lệnh, khối lệnh trong Java. Giống ngôn ngữ C, các câu lệnh trong Java kết thúc bằng dấu chấm phẩy (;). Một khối lệnh là đoạn chương trình gồm hai lệnh trở lên và được bắt đầu bằng dấu mở ngoặc nhọn ({) và kết thúc bằng dấu đóng ngoặc nhọc (}). Bên trong một khối lệnh có thể chứa một hay nhiều lệnh hoặc chứa các khối lệnh khác. { // khối 1 { // khối 2 lệnh 2.1 lệnh 2.2 } // kết thúc khối lệnh 2 lệnh 1.1 lệnh 1.2 } // kết thúc khối lệnh 1 { // bắt đầu khối lệnh 3 // Các lệnh thuộc khối lệnh 3 } // kết thúc khối lệnh 3 2.6.2. Câu lệnh if else Java đưa ra hai cấu trúc lệnh if else như sau: Dạng 1: if ( ) { ; } Dạng 2: if ( ) { 32
  33. ; } else { ; } Trong đó: là Biểu thức boolean như toán tử so sánh. , là câu lệnh đơn hoặc khối lệnh trong Java; Ví dụ 2.5: Chương trình kiểm tra xem ngày hiện tại có phải chủ nhật hay không: import java.util.Date; public class TestIf { public static void main( String args[ ] ){ Date today = new Date(); if( today.getDay() == 0 ) System.out.println(“Hom nay la chu nhat\n”); else System.out.println(“Hom nay khong la chu nhat\n" ); } } 2.6.3. Câu lệnh switch-case Khối lệnh switch-case có thể được sử dụng thay thế câu lệnh if-else trong trường hợp một biểu thức cho ra nhiều kết quả. Cú pháp: swich (expression) { case ‘value1’: action 1 statement; break; case ‘value2’: action 2 statement; break; case ‘valueN’: actionN statement; break; default: default_action statement; } 33
  34. Trong đó: - expression – Biểu thức chứa một giá trị xác định. Biểu thức expression phải có kiểu char, byte, short, int nếu biểu thức expression có kiểu khác với các kiểu liệt kê ở trên thì Java sẽ đưa ra một thông báo lỗi. - value1, value 2, ., valueN: Các giá trị hằng số cùng kiểu dữ liệu với giá trị trên biến expression. - action1, action2 , actionN: Khối lệnh được thực thi khi trường hợp tương ứng có giá trị True. - break: Câu lệnh được sử dụng để bỏ qua tất cả các câu lệnh sau đó và giành quyền điều khiển cho cấu trúc bên ngoài switch - default: Từ khóa tuỳ chọn được sử dụng để chỉ rõ các câu lệnh nào được thực hiện chỉ khi tất cả các trường hợp nhận giá trị False - default - action: Khối lệnh được thực hiện chỉ khi tất cả các trường hợp nhận giá trị False Ví dụ 2.6: Xác định giá trị trong một biến nguyên và hiển thị ngày trong tuần được thể hiện dưới dạng chuỗi. class SwitchDemo { public static void main(String agrs[]) { int day = 2; switch(day) { case 0 : System.out.println(“Sunday”); break; 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(“Satuday”); 34
  35. break; default: System.out.println(“Invalid day of week”); } } } Nếu giá trị của biến day = 4, chương trình sẽ hiển thị “Thursday” 2.6.4. Vòng lặp While Cú pháp: while(condition) { action statements; } Vòng lặp while thực thi khối lệnh khi điều kiện thực thi condition vẫn là True và dừng lại khi điều kiện thực thi condition nhận giá trị False. Ví dụ 2.7: Tính tổng các số lẻ từ 1 đến 20 int tong = 0, i = 1; while (i<=20) { tong+=i; i+=2; } System.out.println(“Tong bang ”+ tong); Ở ví dụ trên, vòng lặp được thực thi cho đến khi điều kiện i<=20 là False. Kết quả in ra: Tong bang 100 2.6.5. Vòng lặp do-while Cú pháp: do{ action statements; }while(condition); Vòng lặp do-while thực thi khối lệnh khi mà điều kiện condition là True, tương tự như vòng lặp while, ngoại trừ do-while thực hiện lệnh ít nhất một lần ngay cả khi điều kiện là False. Ví dụ 2.8: Tính tổng các số lẻ từ 1 đến 20. int tong = 0, i=1; 35
  36. do { tong+=i; i+=2; } while (i<=20); System.out.println(“Tong bang ”+ tong); Ở ví dụ trên, vòng lặp được thực thi cho đến khi điều kiện i<=20 là False. 2.6.6. Vòng lặp for Cú pháp: for(initialization statements; condition; increment statements) { action statements; } Ví dụ 2.9: Tính tổng các số lẻ từ 1 đến 20 public class TestFor { public static void main(String[] args) { int tong = 0; for(int i=1; i<=20; i+=2) tong+=i; System.out.println(“Tong bang ” + tong); } } Vòng lặp được thực thi cho đến khi điều kiện i<=20 là False. 36
  37. 2.6.7. Lệnh break và continue Bên trong thân của cấu trúc lặp có thể điều khiển luồng thực hiện bằng cách sử dụng lệnh break và continue, lệnh break sẽ chấm dứt quá trình lặp mà không thực hiện nốt phần còn lại của cấu trúc lặp, continue sẽ ngưng thực thi phần còn lại của thân vòng lặp và chuyển điều khiển về điểm bắt đầu của vòng lặp, để thực hiện lần lặp tiếp theo. Ví dụ 2.10: public class BreakAndContinue { public static void main(String[] args) { for(int i = 0; i < 100; i++) { if(i == 74) break;// Out of for loop if(i % 9! = 0) continue;// Next iteration System.out.print(i+ “ ”); } int i = 0; // An "infinite loop": while(true) { i++; int j = i * 27; if(j == 945) break;// Out of loop if(i % 10! = 0) continue;// Top of loop System.out.print(i + “ ”); } } } 37
  38. 2.7. Số lớn trong java Lớp BigInteger sử dụng để xử lý các số nguyên rất lớn nằm ngoài giới hạn của tất cả các kiểu dữ liệu nguyên thủy có sẵn. Khai báo: BigInteger A, B; Sử dụng phương thức tĩnh valueOf để chuyển một số bình thường thành số nguyên lớn BigInteger A = BigInterger(valueOf(100)); Một số hàm tạo: BigInteger (byte [] val) Hàm tạo này được sử dụng để dịch một mảng byte chứa biểu diễn nhị phân bổ sung của hai BigInteger thành một BigInteger. BigInteger (int numBits, rnd ngẫu nhiên) Hàm tạo này được sử dụng để tạo ngẫu nhiên một BigInteger nằm trong phạm vi từ 0 đến (2 numBits - 1) BigInteger (Chuỗi val) Hàm tạo này được sử dụng để chuyển biểu diễn Chuỗi thập phân của BigInteger thành BigInteger. BigInteger (Chuỗi val, int radix) Hàm tạo này được sử dụng để chuyển biểu diễn Chuỗi của BigInteger trong cơ số được chỉ định thành BigInteger. Một số phương thức thường dùng: BigInteger add (BigInteger val)- Phương thức này trả về một BigInteger có giá trị là (this + val). Int compareTo(BigInteger val)- so sánhBigInteger với BigInteger xác định. float floatValue() Chuyển BigInteger thành kiểu float. BigInteger max(BigInteger val) Trả về giá trị lớn nhất của BigInteger và val. BigInteger min(BigInteger val) Trả về giá trị nhỏ nhất của BigInteger và val 2.8. Mảng Mảng là tập hợp nhiều phần tử có cùng tên, cùng kiểu dữ liệu và mỗi phần tử trong mảng được truy xuất thông qua chỉ số trong mảng. Khai báo []; hoặc [] ; Ví dụ 2.11: int arrInt[]; // đây là mảng các số nguyên hoặc int[ ] arrInt; Cấp phát bộ nhớ cho mảng Để cấp phát bộ nhớ cho mảng trong Java dùng từ khóa new (tất cả trong Java đều thông qua các đối tượng). Chẳng hạn để cấp phát vùng nhớ cho mảng số nguyên gồm tối đa 100 phần tử trong Java ta làm như sau: 38
  39. int arrInt = new int[100]; Truy nhập các phần tử trong mảng Chỉ số mảng trong Java bắt đầu từ 0. Vì vậy phần tử đầu tiên có chỉ số là 0, và phần tử thứ n có chỉ số là n-1. Các phần tử của mảng được truy xuất thông qua chỉ số của nó đặt giữa cặp dấu ngoặc vuông []. Ví dụ 2.12: int arrInt[] = {1, 2, 3}; int x = arrInt[0]; // x sẽ có giá trị là 1. int y = arrInt[1]; // y sẽ có giá trị là 2. int z = arrInt[2]; // z sẽ có giá trị là 3. Lưu ý: Trong những ngôn ngữ lập trình khác (như C ), một chuỗi được xem như một mảng các ký tự. Trong Java thì khác, Java cung cấp một lớp String để làm việc với đối tượng dữ liệu chuỗi cùng các thao tác trên đối tượng dữ liệu này. 2.8.1. Vòng lặp “for each” Vòng lặp foreach được sử dụng chủ yếu với mảng và ArrayList Cú pháp: for(Khai_bao : Bieu_thuc) { //phan than vong lap } Ví dụ 2.13: public class Test { public static void main(String args[]){ int [] numbers = {10, 20, 30, 40, 50}; for(int x : numbers ){ System.out.print( x ); System.out.print(","); } System.out.print("\n"); String [] names ={"James", "Larry", "Tom", "Lacy"}; for( String name : names ) { System.out.print( name ); System.out.print(","); }}} 39
  40. Kết quả: 2.8.2. Khởi tạo mảng và mảng nặc danh Chúng ta có thể khởi tạo giá trị ban đầu cho các phần tử của mảng khi khai báo. Ví dụ 2.14: int arrInt[] = {1, 2, 3, 4}; //mảng số nguyên gồm 4 phẩn tử char arrChar[] = {‘a’, ‘b’, ‘c’}; // mảng ký tự gồm 3 phần tử là ký tự String arrStrng[] = {“ABC”, “DEF”, ‘GHI’}; //mảng gồm3 phần tử kiểu String Cũng có thể khởi tạo một mảng nặc danh new int[] { 17, 19, 23, 29, 31, 37 }; Sử dụng cú pháp khai báo mảng nặc danh này ta có thể khởi tạo một mảng mà không cần khai báo thêm biến mới. smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 }; Là rút gọn của khai báo: int[] anonymous = { 17, 19, 23, 29, 31, 37 }; smallPrimes = anonymous Ví dụ 2.15: Nhập và xuất giá trị các phần tử của một mảng các số nguyên. class ArrayDemo { public static void main(String args[]) { int arrInt[] = new int[10]; int i; for(i = 0; i < 10; i = i+1) arrInt[i] = i; for(i = 0; i < 10; i = i+1) System.out.println("This is arrInt[" + i + "]: " + arrInt[i]); } } Kết quả: 40
  41. 2.8.3. Copy mảng Có thể copy một biến mảng vào một biến mảng khác, tuy nhiên cả hai biến đó phải cùng tham chiếu tới cùng một mảng. int[] luckyNumbers = smallPrimes; luckyNumbers[5] = 12; // smallPrimes[5] cũng nhận giá trị 12 2.8.4. Các tham số dòng lệnh Trong hàm main của java có tham số string[ ] args, tham số này chỉ ra hàm main nhận gía trị đầu vào là một mảng các xâu ký tự, các tham số này được truyền qua dòng lệnh. Ví dụ 2.16: public class Message { public static void main(String[] args) { if (args[0].equals("-h")) System.out.print("Hello,"); else if (args[0].equals("-g")) System.out.print("Goodbye,"); // print the other command-line arguments for (int i = 1; i < args.length; i++) System.out.print(" " + args[i]); System.out.println("!"); } } Biên dịch chương trình và chạy java Message -g cruel world Mảng args có nội dung sau args[0]: "-g" args[1]: "cruel" 41
  42. args[2]: "world" Chương trình cho kết quả Goodbye, cruel world! 2.8.5. Sắp xếp mảng Để sắp xếp một mảng số ta dùng phương thức sort trong class Array. int[ ] a= new int [10000]; Array.sort(a);// sẽ sắp xếp mảng a. 2.8.6. Mảng nhiều chiều Cú pháp khai báo mảng hai chiều: [Kiểu_dữ_liệu] Tên_mảng[][]; Hoặc [Kiểu_dữ_liệu][][] Tên_mảng; Cấp phát bộ nhớ cho mảng Trong Java có 2 cách cấp phát bộ nhớ như sau: Cách 1: [Kiểu_dữ_liệu] Tên_mảng[][] = new [Kiểu_dữ_liệu] [Số_dòng][Số_cột]; Ví dụ 2.18: Khai báo và cấp phát bộ nhớ cho mảng number có 2 dòng, 3 cột: int number[][] = new int[2][3]; Cách 2: [Kiểu_dữ_liệu][][] Tên_mảng = new [Kiểu_dữ_liệu] [Số_dòng][Số_cột]; Truy xuất các phần tử trong mảng hai chiều Mỗi phần tử của mảng 2 chiều được truy xuất thông qua tên mảng cùng với chỉ số dòng và chỉ số cột của phần tử đó. Tương tự mảng một chiều, nếu một mảng hai chiều có m dòng và n cột thì chỉ số của dòng sẽ chạy từ 0, 1, 2, , m - 1 và chỉ số của cột sẽ chạy từ 0, 1, 2, , n - 1. Cú pháp: Tên_mảng[Chỉ_số_dòng][Chỉ_số_cột]; Ví dụ 2.19: Nhập xuất mảng hai chiều public static void main(String[] args) { // khai báo số dòng và số cột cho mảng int soDong, soCot; Scanner scanner = new Scanner(System.in); System.out.println("Nhập vào số dòng của mảng:"); soDong = scanner.nextInt(); System.out.println("Nhập vào số cột của mảng:"); soCot = scanner.nextInt(); // khai báo và cấp phát bộ nhớ cho mảng 42
  43. int[][] A = new int[soDong][soCot]; for (int i = 0; i < soDong; i++) { for (int j = 0; j < soCot; j++) { System.out.print("Nhập phần tử thứ [" + i + ", " + j + "]: "); A[i][j] = scanner.nextInt(); } } System.out.println("Mảng vừa nhập: "); for (int i = 0; i < soDong; i++) { for (int j = 0; j < soCot; j++) { System.out.print(A[i][j] + "\t"); } System.out.println("\n"); }} Kết quả: 43
  44. CÂU HỎI ÔN TẬP Câu 1. Những từ sau có phải là định danh hay không? a/ aZ4, b/ ân2, c/_class, d/ My$100 Câu 2. Dãy //* // */ có phải là chú thích hay không ? tại sao ? Những lệnh nào trong số các lệnh sau là hợp lệ a. Char a =’\u0061’; b. Char ‘\u0061’=’a’; c. Long phone=8235469L; d. Fload pi=3.14159; e. Double p=314.159e-2; Câu 3: Những khai báo nào của hàm main () sau đây là đúng ? a) static void main(String arg[]){/* */} b) public static int main(String arg[]){/* */} c) public stactic void main (String arg[]){/* */} d) public stactic void main (String [] arg){/* */} e) final static public void main (String [] arg){/* */} Câu 4.Tìm câu trả lời đúng? a) if-else đơn (1 if và 1 else) thì có ít nhất một khối lệnh (của if hoặc của else) được thực hiện. Đúng hay sai? b) Trong cấu trúc lệnh switch-case, khi không dùng “default” thì có ít nhất một khối lệnh được thực hiện. Đúng hay sai? c) Trong cấu trúc lệnh switch-case, khi dùng “default” thì có ít nhất một khối lệnh được thực hiện. Đúng hay sai? d) Trong cấu trúc lệnh while, khối lệnh được thực hiện ít nhất một lần ngay cả khi điều kiện có giá trị False. Đúng hay sai? e) Trong cấu trúc lệnh do-while, khối lệnh được thực hiện ít nhất một lần ngay cả khi điều kiện có giá trị False. Đúng hay sai? f) Trong cấu trúc lệnh for, khối lệnh được thực hiện ít nhất một lần ngay cả khi điều kiện có giá trị False. Đúng hay sai? Câu 5. Chương trình sau có lỗi hay không, nếu có lỗi thì sửa cho đúng. //NhietDo.Java PUBLIC CLASS nhietDo{ Public VOID main(String arg){ double fahre=62.5; double celsius=f2c(fahre); System.uot.println(fahre +’F=’+celsius+’c’); } 44
  45. Double f2c(float f) { Return (f-32)*5/9; } } Câu 6. Cho biết đoạn chương trình sau thực hiện vòng lặp bao nhiêu lần và kết quả in ra là gì? class me{ public static void main(String args[]){ int i = 0; int sum = 0; do{ sum += i; i++; } while(i <= 10); System.out.println(sum); } } Câu 7. Cho biết đoạn chương trình sau thực hiện vòng lặp bao nhiêu lần và kết quả in ra là gì? class me{ public static void main(String args[ ]){ int i = 5; int sum = 0; do { sum += i; i++; } while(i < 5); } Câu 8. Cho biết kết quả thực hiện chương trình sau : public class MyClas{ public static coid main (String [] arg){ String a,b,c; c=new String(“chuột”); b=new String(“Mèo”); b=a; a=”Chó”; c=b; System.uot.println(c); } } 45
  46. Câu 9. Điều gì sẽ xảy ra khi chạy và dịch chương trình sau : public class Prog1{ public static void main (String [] arg ) { int k=1; int i=++k + k++ + +k; System.uot.println(i); } Câu 10. Điều gì sẽ xảy ra khi dịch và chạy chương trình sau: public class Prog2{ public static void main(String [] arg){ int k=0; int[] a={3,6}; a[k]=k=9; //lưu ý mức ưu tiên của [] cao hơn phép gán= System.out.println(i+” ” +a [0]+ “ ” + a[1]); } } Câu 11. Cho biết kết quả thực hiện chương trình sau : public class Prog3{ public static void main (String [] arg){ int k =0 , i=0; boolean r,t=true; r=(t & 0< (i +=1)) ; r = (t && 0 < (i+=2)); r= (t | 0 < (k+=1)); r = (t || 0 < (k+=2)); System.uot.println(i+” ” + k); } } Câu 12. Kết quả dich và chạy chương trình sau là gì ? public class Prog4{ public static void main (String [] arg){ int a=0,b=0; int [] bArr new int [1]; bArr[0] = b; inC1(a) ; inC2(bArr); 46
  47. System.out.println(“a= ” + a + “b =” + b +”bArr = “+ bArr[0]”); } public static void inC1(int x){x++;}; public static void inC2(int [] x) {x[0]++;}; } CÂU HỎI THẢO LUẬN Bài 1. Viết chương trình tìm ước số chung lớn nhất, bội số chung nhỏ nhất của hai số tự nhiên a và b. Bài 2. Viết chương trình chuyển đổi một số tự nhiên ở hệ cơ số 10 thành số ở hệ cơ số b bất kì (1 =2. Hãy viết chương trình tìm số Fibonacci thứ n. Bài 8. Nhập số n và dãy các số thực a0 , a1 , , an-1. Không đổi chỗ các phần tử và không dùng thêm mảng số thực nào khác (có thể dùng mảng số nguyên nếu cần) hãy cho hiện trên màn hình dãy trên theo thứ tự tăng dần. Bài 9. Viết chương trình nhập vào vào ma trận A có n dòng, m cột, các phần tử là những số nguyên lớn hơn 0 và nhỏ hơn 100 được nhập vào từ bàn phím. Thực hiện các chức năng sau: a)Tìm phần tử lớn nhất của ma trận cùng chỉ số của số đó. b)Tìm và in ra các phần tử là số nguyên tố của ma trận (các phần tử không nguyên tố thì thay bằng số 0). c)Sắp xếp tất cả các cột của ma trận theo thứ tự tăng dần và in kết quả ra màn hình. Bài 10. Viết chương trình java cho phép tạo và thực thi theo menu sau: 1. Nhập vào một số nguyên dương n. 2. Tính tổng các số từ 1 đến n 3. Kiểm tra n có là số nguyên tố 4. Kiểm tra n có là số hoàn hảo. 5. In ra các số nguyên tố từ 1 đến n 6. In ra các số hoàn hảo từ 1 đến n. 47
  48. 7. Hiển thị số n thành tích các thừa số nguyên tố. 8. Thoát Bài 11. Viết chương trình nhập vào vào mảng A có n phần tử, các phần tử là những số nguyên lớn hơn 0 và nhỏ hơn 100 được nhập vào từ bàn phím. Thực hiện các chức năng sau: a) Tìm phần tử lớn nhất và lớn thứ 2 trong mảng cùng chỉ số của các số đó. b) Sắp xếp mảng theo thứ tự giảm dần . c) Nhập một số nguyên x và chèn x vào mảng A sao cho vẫn đảm bảo tính sắp xếp giảm dần và số phần tử của mảng vẫn không đổi. Bài 12. Nhập một dãy n số nguyên, tìm khoảng cách ngắn nhất giữa hai số liền kề nhau. Ví dụ: input 4 8 6 1 2 9 4, out put: Khoảng cách ngắn nhất là 1, giữa hai số 1 và 2. Bài 13.Viết chương trình thực hiện các yêu cầu sau: a) Tạo một mảng một chiều với số phần tử được nhập vào từ bàn phím; b) Nhập giá trị của các phân tử của mảng là các số nguyên từ bàn phím; c) Hiển thị mảng vừa nhập; d) Cho biết tổng số phần tử có giá trị là số chẵn. Ví dụ: e) Mảng được nhập vào là: 1 4 6 7 // số phần tử chẵn = 2 f) Mảng được nhập vào là: 1 3 5 7 // số phần tử chẵn = 0. g) Đưa ra phần tử trong mảng có giá trị là x (ví dụ như x = 3); h) Đưa ra: giá trị lớn nhất, nhỏ nhất có trong mảng. Bài 14. Viết chương trình thực hiện chuẩn hoá một xâu ký tự nhập từ bàn phím (loại bỏ các dấu cách thừa, chuyển ký tự đầu mỗi từ thành chữ hoa, các ký tự khác thành chữ thường) Gợi ý: Hãy sử dụng phương thức của StringTokenizer Bài 15. Viết chương trình nhập một xâu và in ra xâu đảo ngược. 48
  49. Chương 3. ĐỐI TƯỢNG, LỚP, KẾ THỪA, GIAO DIỆN Mục đích Trình bày chi tiết các khái niệm về hướng đối tượng trong Java như: Đối tượng, lớp và lớp trừu tượng Kế thừa và đa hình trong Java Các kỹ thuật xử lý ngoại lệ Luồng và sự tương tranh trong lập trình đa luồng 3.1. Đối tượng, lớp, lớp trừu tượng 3.1.1. Khái niệm Lớp được xem như một khuôn mẫu (template) của đối tượng, bao gồm các thuộc tính (properties) của đối tượng và các phương thức (method) tác động lên các thuộc tính. Ví dụ 3.1: Lớp SinhVien có các thuộc tính MãSV, điểm, hạnh kiểm, có các phương thức học tập, thực hành, giải trí .v.v. Đối tượng là một thể hiện (class instance) của lớp. Mỗi đối tượng có một lớp định nghĩa các dữ liệu và hành vi của nó. Mỗi sinh viên cụ thể là một thể hiện (hay đối tượng) của lớp SinhVien. 3.1.2. Khai báo lớp class { ; ; Constructor method_1 method_2 } Trong đó: class: là từ khóa của Java ClassName: là tên chúng ta đặt cho lớp field_1, field_2: các thuộc tính (các biến, hay các thành phần dữ liệu của lớp) constructor: là phương thức xây dựng, khởi tạo đối tượng của lớp. method_1, method_2: là các phương thức (có thể gọi là hàm) thể hiện các thao tác xử lý, tác động lên các thuộc tính của lớp. Ví dụ 3.2: Khai báo một lớp rỗng không chứa thuộc tính, phương thức. Class Student 49
  50. { // không chứa thuộc tính, phương thức } Ví dụ 3.3: Xây dựng lớp Pencil với các thuộc tính màu, độ dài, số hiệu và phương thức thiết lập màu: class Pencil { public String color = “red”; public int length; public float diameter; public static long nextID = 0; public void setColor (String newColor) { color = newColor; } } 3.1.3. Thuộc tính của lớp class { // khai báo những thuộc tính của lớp field1; // } Trong đó - Kiểu dữ liệu có thể là kiểu dữ liệu cơ sở hoặc kiểu dữ liệu đối tượng tham chiếu: boolean, char, byte, short, int, long, float, double - Tiền tố bao gồm: Chỉ định truy xuất thuộc tính lớp + các bổ nghĩa loại thuộc tính nếu có (Các bổ nghĩa loại thuộc tính có thể là static hoặc final hoặc cả 2) - Sau tên trường có thể có kèm theo giá trị khởi tạo Chỉ định truy xuất thuộc tính lớp: - private: Có thể truy cập thuộc tính này chỉ bên trong lớp khai báo - package (~ không có chỉ định truy xuất): Có thể truy cập từ các lớp trong cùng gói (package) và trong bản thân lớp khai báo - protected: Có thể truy cập từ các lớp trong cùng gói,các lớp thừa kế và bản thân lớp khai báo 50
  51. - public: Có thể được truy cập từ bất kỳ một lớp nào Ví dụ 3.4: public class Pencil { public String color = “red”; public int length; public float diameter; private float price; public static long nextID = 0; public void setPrice (float newPrice) { price = newPrice; } } public class CreatePencil { public static void main (String args[]){ Pencil p1 = new Pencil(); p1.price = 0.5f; // price has private access in Pencil } } Các bổ nghĩa loại thuộc tính static  Chỉ một bản duy nhất của thuộc tính static được tồn tại, chia sẻ giữa các đối tượng của cùng lớp chứa thuộc tính này  Có thể được truy cập trực tiếp trong bản thân lớp khai báo (truy cập trong hàm main)  Nếu truy cập từ bên ngoài lớp khai báo, phải kèm theo tên lớp trước tên thuộc tính: System.out.println(Pencil.nextID); hay thông qua một đối tượng phụ thuộc vào lớp khai báo  Từ bên ngoài lớp, các thuộc tính non-static phải được truy cập thông qua tham chiếu đối tượng Ví dụ 3.5: public class CreatePencil { public static void main (String args[]){ Pencil p1 = new Pencil(); Pencil.nextID++; System.out.println(p1.nextID); //Result? 1 Pencil p2 = new Pencil(); Pencil.nextID++; 51
  52. System.out.println(p2.nextID); //Result? 2 System.out.println(p1.nextID); //Result? Still 2 } } final  Khi đã được khởi tạo, giá trị không thể thay đổi  Thường được sử dụng cho các hằng số  Thuộc tính static + final phải được khởi tạo ngay khi khai báo  Thuộc tính non-static + final phải được khởi tạo ngay khi một đối tượng của lớp được tạo ra Khởi tạo giá trị thuộc tính Không nhất thiết là hằng số, ta có thể khởi tạo giá trị cho mọi biến nếu có quyền. Nếu không khởi tạo, giá trị khởi tạo mặc định sẽ phụ thuộc vào loại thuộc tính 3.1.4. Phương thức của lớp Khai báo: ( ) { ; } Trong đó: : Để xác định quyền truy xuất của các đối tượng khác đối với các phương thức của lớp người ta thường dùng các tiền tố sau: - Các chỉ định truy xuất của phương thức (cùng ý nghĩa với thuộc tính): public, protected, private - Các bổ nghĩa loại phương thức: static, final, abstract, synchronized, native, volatile. : có thể là kiểu void, kiểu cơ sở hay một lớp. : đặt theo qui ước giống tên biến. 52
  53. : có thể rỗng Các loại bổ nghĩa của phương thức - static: Phương thức dùng chung cho tất cả các thể hiện của lớp, chỉ có thể truy cập đến các thuộc tính hoặc phương thức static trong cùng lớp - final: Phương thức có thuộc tính này không được ghi đè (overridden) trong các lớp thừa kế - abstract: Phương thức không cần cài đặt, sẽ được cài đặt trong các lớp thừa kế Ví dụ 3.6: abstract void sampleMethod( ); Lời gọi phương thức: - Sử dụng toán tử (.): reference.method(arguments) - Với phương thức static  Bên ngoài class, tham chiếu reference có thể là tên class hoặc tham chiếu đối tượng của class  Bên trong class, không cần tham chiếu reference - Với phương thức non-static: “reference” phải là tham chiếu đối tượng. Giá trị của các đối số truyền vào trong lời gọi phương thức Khi đối số không phải là một tham chiếu đối tượng, nó truyền vào một bản copy giá trị của đối số. Ví dụ 3.7: public void method1 (int a) { a = 6; } public void method2 ( ) { int b = 3; method1(b); // now b = ? b = 3 } Khi đối số là một tham chiếu đối tượng, nó truyền vào một bản copy của tham chiếu tới đối tượng đó Ví dụ 3.8: 53
  54. class PassRef{ public static void main(String[] args) { Pencil plainPencil = new Pencil(); plainPencil.color = “PLAIN”; System.out.println("original color:" + plainPencil.color); paintRed(plainPencil); // truyen vao tham chieu doi tuong System.out.println("new color: " + plainPencil.color); } public static void paintRed(Pencil p) { p.color = "RED"; // doi mau thanh do p = null; // sau do tro toi null } } Kết quả chương trình: Nạp chồng phương thức (method overloading): Một class có thể có nhiều cách thức có cùng tên nhưng khác nhau về danh sách đối số public class Pencil { . . . public void setPrice (float newPrice) { price = newPrice; } public void setPrice (Pencil p) { price = p.getPrice(); } } Trình dịch phân biệt sự khác nhau của 2 danh sách đối số bằng cách so sánh số đối số, kiểu của các đối số trong 2 danh sách (có phân biệt thứ tự). 54
  55. 3.1.5. Chỉ định truy xuất lớp Một lớp cũng có thể có các chỉ định truy xuất đi trước tên lớp: - public Có thể truy xuất từ bất kỳ đâu Không có từ khóa này, lớp chỉ có thể truy cập trong phạm vi cùng gói - abstract Lớp này được thiết kế nhằm tạo ra một lớp có các đặc tính tổng quát, nó định nghĩa các thuộc tính chung cho các lớp con của nó Không thể tạo đối tượng (thể hiện) cho những lớp abstract - final Lớp không thể được thừa kế 3.2. Tạo đối tượng Ví dụ 3.9: Tạo lớp Student, sau đó tạo đối tượng Student có kiểu là kiểu Student vừa được tạo ra: class Student { private long idNum; private String name = “empty”; private String address; private static long nextID = 0; } Trong Java, một đối tượng được tạo ra bằng cách sử dụng phương thức new Tạo đối tượng mới: Student std = new Student (); Constructor Là một phương thức đặc biệt của lớp. Dùng gọi tự động khi khởi tạo một thể hiện (đối tượng) của lớp, có thể dùng để khởi gán những giá trị mặc định - Không có giá trị trả về, có thể có hoặc không có tham số - Phải có cùng tên với lớp và được gọi đến khi dùng từ khóa new - Nếu một lớp không có constructor, Java cung cấp một constructor mặc định, những thuộc tính của lớp sẽ được khởi tạo giá trị mặc định (kiểu số = 0, logic = false, đối tượng = null) Chú ý: Thông thường để an toàn, dễ kiểm soát và làm chủ mã nguồn mỗi lớp nên khai báo một constructor Ví dụ 3.10: class Student { private long idNum; 55
  56. private String name= “empty”; private String address; // bien static chia se giua cac doi tuong cua lop Student private static long nextID = 0; Student( ) { // gan gia tri nextID cho idNum, sau do tang nextID len 1 idNum = nextID++; } Student(String studentName, String addr) { this( ); name = studentName; address = addr; } } Xem xét trước đó chưa có bất kỳ đối tượng Student nào được tạo ra. Xem xét hai trường hợp sau: Trường hợp 1. Student std = new Student() Khi đó constructor không có tham số Student() được gọi khởi tạo các giá trị cho đối tượng Student: idNum = 0, name = “empty”, address = null, nextID = 1 Trường hợp 2. Student std = new Student(“Hai”, null); Student std1 = new Student(“Hau”, “Hung Yen”); Trong trường hợp này ta tạo ra hai đối tượng Student sử dụng constructor có tham số đầu vào. Các giá trị được khởi tạo như sau: Đối tượng được tham chiếu bởi std: idNum = 0, name = “Hai”, address = null, nextID = 1 Đối tượng được tham chiếu bởi std1: idNum = 1 (giá trị nextID hiện tại), name = “Hau”, address = “Hung Yen”, nextID = 2 (giá trị nextID sau khi tăng) Biến this - Biến this được sử dụng như một tham chiếu đến đối tượng hiện tại - Trong một constructor có thể dùng biến this để gọi một contructor khác. Nó phải được đặt trong dòng đầu tiên của contructor sử dụng nó. - Biến this không thể được sử dụng trong một phương thức static Ví dụ 3.11: 56
  57. class Student { private long idNum; private String name; private String address; private static long nextID = 0; private static LinkedList studentList = new LinkedList(); . . . Student(String name, String address) { this.name = name; this.address = address; } private void inQueue() { studentList.add(this); // Them vao danh sach lien ket } . . . } 3.3. Kế thừa và đa hình 3.3.1. Mô tả kế thừa Tính kế thừa trong Java là môt kỹ thuật mà trong đó một đối tượng thu được thuộc tính và hành vi của đối tượng cha. Ý tưởng đằng sau tính kế thừa trong Java là có thể tạo các lớp được xây dựng dựa trên các lớp đang tồn tại. Khi kế thừa từ một lớp đang tồn tại, có thể tái sử dụng các phương thức và thuộc tính của lớp cha, và cũng có thể bổ sung thêm các phương thức và các thuộc tính khác. Tính kế thừa biểu diễn mối quan hệ IS-A, còn được gọi là mối quan hệ cha-con. Lớp con sử dụng từ khóa extend để có thể kế thừa các thuộc tính của lớp cha trừ các thuộc tính private của lớp cha. 3.3.2. Kế thừa đơn Sự kế thừa được sử dụng khi muốn tạo một lớp mới từ một lớp đã biết. Khi đó, tất cả các thuộc tính và phương thức của lớp cũ đều trở thành thuộc tính và phương thức của lớp mới. Lớp cũ được gọi là lớp cha, lớp mới được gọi là lớp con. Khai báo lớp kế thừa được thực hiện bởi từ khoá extends: extends { } Ví dụ 3.12: class Person { 57
  58. public String name; public int age; // Phương thức khởi tạo public Person(String name, int age) { this.name = name; this.age = age; } public void show() { System.out.println( name + “ is ” + age + “ years old!”); } } class Employee extends Person { public float salary; // Phương thức khởi dựng public Employee(String name, int age, float salary) { super(name, age); this.salary = salary; } } Như vậy, khi có lớp: class EmployeeDemo1 { public static void main(String args[]) { Employee myEmployee = new Employee(“Nam”, 20, 300f); myEmployee.show(); } } 58
  59. Ví dụ 3.13: public class xe { public String tenxe; public String hangxe; public xe(String tenxe, String hangxe){ this.tenxe = tenxe; this.hangxe = hangxe; } public void Show(){ System.out.println("Tenxe: " + tenxe + "hangxe: " + hangxe); } } public class oto extends xe { public int giaban; public int tocdo; public oto(String tenxe, String hangxe, int giaban, int tocdo){ super(tenxe,hangxe); this.giaban = giaban; this.tocdo = tocdo; } } public class chitietxe { public static void main(String[] args) { oto xeoto = new oto("HCS","SNC",232,454); xeoto.Show(); } } 59
  60. Ví dụ 3.14: Chương trình sau minh họa khai báo phương thức nạp chồng Show ( ) của lớp oto mà không sử dụng phương thức Show ( ) của lớp xe : public class oto extends xe { public int giaban; public int tocdo; public oto(String tenxe, String hangxe, int giaban, int tocdo){ super(tenxe,hangxe); this.giaban = giaban; this.tocdo = tocdo; } public void Show(){ System.out.println(tenxe+ "co gia ban ban " + giaban + " toc do: " + tocdo); } } Quy tắc truy nhập trong kế thừa Các quy tắc này quy định khả năng truy nhập của lớp con đối với các thuộc tính và phương thức của lớp cha: private: chỉ được truy nhập trong phạm vi lớp cha, lớp con không truy nhập được. Tất cả các lớp ngoài lớp cha đều không truy nhập được. protected: lớp con có thể truy nhập được. Tất cả các lớp không kế thừa từ lớp cha đều không truy nhập được. final: lớp con có thể sử dụng được nhưng không thể khai báo nạp chồng được. public: lớp con có thể sử dụng và nạp chồng được. Tất cả các lớp bên ngoài đều sử dụng được. 60
  61. 3.3.3. Kế thừa kép Nhằm tránh những nhập nhằng của tính chất đa kế thừa của C++, Java không cho phép kế thừa trực tiếp từ nhiều hơn một lớp cha. Nghĩa là Java không cho phép đa kế thừa trực tiếp, nhưng cho phép cài đặt nhiều giao diện (Interface) để có thể thừa hưởng thêm các thuộc tính và phương thức của các giao diện đó. 3.3.3.1. Giao diện (Interface) Cú pháp khai báo một giao diện như sau: [Tính chất] interface [extends ]{ } Trong đó  Tính chất: một giao diện luôn là public. Nếu không khai báo tường minh thì giá trị mặc định cũng là public.  Tên giao diện: tuân thủ theo quy tắc đặt tên biến của Java.  Danh sách các giao diện: danh sách các giao diện cha đã được định nghĩa để kế thừa, các giao diện cha được phân cách nhau bởi dấu phẩy. (Phần trong ngoặc vuông “[ ]” là tuỳ chọn). Lưu ý: Một giao diện chỉ có thể kế thừa từ các giao diện khác mà không thể được kế thừa từ các lớp sẵn có. Khai báo phương thức của giao diện Cú pháp : [Tính chất] ([ ]) [throws ]; Trong đó:  Tính chất: tính chất của một thuộc tính hay phương thức của giao diện luôn là public. Nếu không khai báo tường minh thì giá trị mặc định luôn là public. Đối với thuộc tính, thì chất chất luôn phải thêm là hằng (final) và tĩnh (static).  Kiểu giá trị trả về: có thể là các kiểu cơ bản của Java, cũng có thể là kiểu do người dùng tự định nghĩa (kiểu đối tượng).  Tên phương thức: tuân thủ theo quy tắc đặt tên phương thức của lớp  Các tham số: nếu có thì mỗi tham số được xác định bằng một cặp . Các tham số được phân cách nhau bởi dấu phẩy.  Các ngoại lệ: nếu có thì mỗi ngoại lệ được phân cách nhau bởi dấu phẩy. Lưu ý: - Các phương thức của giao diện chỉ được khai báo dưới dạng mẫu mà không có cài đặt chi tiết (có dấu chấm phẩy ngay sau khai báo và không có phần cài đặt trong dấu “{}”). Phần cài đặt chi tiết của các phương thức chỉ được thực hiện trong các lớp (class) sử dụng giao diện đó. 61
  62. - Các thuộc tính của giao diện luôn có là hằng (final), tĩnh (static) và public. Do đó, cần gán giá trị khởi đầu ngay khi khai báo thuộc tính của giao diện. Ví dụ 3.15: Khai báo một giao diện không kế thừa từ bất kì một giao diện nào: public interface Product{ } Việc khai báo một thuộc tính và một phương thức của giao diện Product được khai báo ở trên: thuộc tính lưu nhãn hiệu của nhà sản xuất sản phẩm; phương thức dùng để truy xuất giá bán của sản phẩm. public interface Product { public static final String MARK = “Adidas”; public float getCost(); } 3.3.3.2. Sử dụng giao diện Vì giao diện chỉ được khai báo dưới dạng các phương thức mẫu và các thuộc tính hằng nên việc sử dụng giao diện phải thông qua một lớp có cài đặt giao diện đó. Việc khai báo một lớp có cài đặt giao diện được thực hiện thông qua từ khoá implement: class implements { } Trong đó:  Tính chất và tên lớp được sử dụng như trong khai báo lớp thông thường.  Các giao diện: một lớp có thể cài đặt nhiều giao diện. Các giao diện được phân cách nhau bởi dấu phẩy. Khi đó, lớp phải cài đặt cụ thể tất cả các phương thức của tất cả các giao diện mà nó sử dụng. Lưu ý: Một phương thức được khai báo trong giao diện phải được cài đặt cụ thể trong lớp có cài đặt giao diện nhưng không được phép khai báo chồng. Nghĩa là số lượng các tham số của phương thức trong giao diện phải được giữ nguyên khi cài đặt cụ thể trong lớp. Chương trình sau minh họa việc cài đặt lớp giày (Shoe) viện dẫn giao diện Product với các thuộc tính và phương thức đã được khai báo trong chương trình trên. Ví dụ 3.16: public class Shoe implements Product{ // Cài đặt phương thức được khai báo trong giao diện public float getCost() public float getCost() 62
  63. { return 10f; } // Phương thức truy nhập nhãn hiệu sản phẩm public String getMark() { return MARK; } // Phương thức main public static void main(String args[]) { Shoe myShoe = new Shoe(); System.out.println(“This shoe is ” + myShoe.getMark() +“ having a cost of $” + myShoe.getCost()); } } Phương thức getMark() sẽ trả về nhãn hiệu của sản phẩm, là thuộc tính đã được khai báo trong giao diện. Phương thức getCost() là cài đặt riêng của lớp Shoe đối với phương thức đã được khai báo trong giao diện Product mà nó sử dụng, cài đặt này trả về giá trị 10 đối với lớp Shoe. 3.3.4. Lớp cha, lớp con Ta có thể sử dụng tính kế thừa tạo lớp tổng quát có những đặc tính chung đại diện cho một tập hợp các đối tượng có cùng mối quan hệ. Sau đó, lớp này có thể được kế thừa bởi một hay nhiều lớp khác và những đặc tính này trở thành những thành những đặc tính của lớp kế thừa - Lớp được kế thừa gọi là lớp cha (SuperClass) - Lớp kế thừa gọi là lớp con (SubClass) Lớp con kế thừa tất cả các biến và hàm định nghĩa trong lớp cha, ngoại trừ các thành phần private. - Một biến tham chiếu của lớp cha có thể gán để tham chiếu đến một lớp con bất kỳ dẫn xuất từ lớp cha. Khi một tham chiếu đến một lớp con được gán cho biến tham chiếu kiểu lớp cha, ta chỉ có quyền truy xuất những phần được định nghĩa bởi lớp cha. 63
  64. 3.3.5. Cách sử dụng từ khóa super Từ khóa super trong Java là một biến tham chiếu mà được sử dụng để tham chiếu đến đối tượng lớp cha gần nhất. Bất cứ khi nào tạo instance (thể hiện) của lớp con, một instance của lớp cha được tạo ngầm định, được tham chiếu bởi biến super. Cách sử dụng của từ khóa super trong Java  super được sử dụng để tham chiếu biến instance của lớp cha gần nhất.  super() được sử dụng để gọi Constructor của lớp cha gần nhất.  super được sử dụng để gọi phương thức của lớp cha gần nhất. 3.3.6. Cách nạp chồng phương thức Khi muốn thay đổi nội dung của các phương thức được kế thừa từ lớp cha, ta dùng nạp chồng phương thức. Thực ra là khai báo lại một phương thức mới có cùng tên và kiểu với một phương thức đã có trong lớp cha. Chương trình sau sẽ khai báo nạp chồng phương thức show() của lớp Employee mà không dùng lại phương thức show() của lớp Person. Ví dụ 3.17: class Employee extends Person { public float salary; // Phương thức khởi dựng public Employee(String name, int age, float salary) { super(name, age); this.salary = salary; } // Khai báo nạp chồng public void show() { System.out.println( name + “has a salary of” + salary + “$/month”); } } Khi đó, đoạn chương trình dưới sẽ in ra dòng thông báo “Nam has a salary of 300$/month” thay vì dòng thông báo “Nam is 20 years old!” như trong chương trình trên. Lí do là lúc này, đối tượng myEmployee sẽ gọi phương thức show() của lớp Employee mà không gọi phương thức show() của lớp Person nữa. class EmployeeDemo2 { 64
  65. public static void main(String args[]) { Employee myEmployee = new Employee(“Nam”, 20, 300f); myEmployee.show(); } } Ví dụ 3.18: Chương trình sau minh họa khai báo nạp chồng phương thức Show ( ) của lớp oto mà không sử dụng phương thức Show ( ) của lớp xe : public class oto extends xe { public int giaban; public int tocdo; public oto(String tenxe, String hangxe, int giaban, int tocdo){ super(tenxe,hangxe); this.giaban = giaban; this.tocdo = tocdo; } public void Show(){ System.out.println(tenxe+ "co gia ban ban " + giaban + " toc do: " + tocdo); } } 65
  66. 3.3.7. Đa hình 3.3.7.1. Nạp chồng Java cho phép trong cùng một lớp, có thể khai báo nhiều phương thức có cùng tên. Nạp chồng là hiện tượng các phương thức có cùng tên. Có hai kiểu nạp chồng trong Java: Các phương thức của cùng một lớp có cùng tên. Khi hai phương thức của một lớp có cùng tên thì bắt buộc chúng phải có: - Hoặc danh sách các tham số khác nhau - Hoặc kiểu trả về khác nhau - Hoặc kết hợp hai điều kiện trên. Nếu không, Java sẽ không phân biệt được chúng. Ví dụ nếu trong cùng một lớp: // Chấp nhận được public int add(int x, int y){ } public float add(float x, int y){ } // Không chấp nhận được public int add(int x, int y){ } public int add(int x, int y){ } Phương thức của lớp con cùng tên với phương thức của lớp cha. Trong trường hợp này, các phương thức nạp chồng có thể có cùng danh sách tham số và cùng kiểu trả về 3.3.7.2. Đa hình Đa hình là việc triệu gọi đến các phương thức nạp chồng của đối tượng. Khi một phương thức nạp chồng được gọi, chương trình sẽ dựa vào kiểu các tham số và kiểu trả về để gọi phương thức của đối tượng cho phù hợp. Chương trình sau minh họa việc khai báo nhiều hàm add() để cộng hai số hoặc cộng hai xâu kí tự. Ví dụ 3.19: public class Operator { // Cộng hai số nguyên public int add(int x, int y) { return (x + y); } // Cộng hai số thực 66
  67. public float add(float x, float y) { return (x + y); } // Cộng hai chuỗi kí tự public String add(String a, String b) { return (a + b); } // Phương thức main public static void main(String args[]) { Operator myOperator = new Operator(); System.out.println(“The (5+19) is ” + myOperator.add(5, 19)); System.out.println(“The (\”ab\” + \”cd\”) is \””+ myOperator.add(“ab”, “cd”) + “\””); } } Trong lớp Operator có hai phương thức cùng tên và cùng có hai tham số đầu vào là add(). Khi chương trình thực thi lệnh myOperator.add(5, 19), chương trình sẽ tự đối chiếu các kiểu tham số, thấy 5 và 19 có dạng gần với kiểu int nhất, nên phương thức add(int, int) sẽ được gọi và trả về giá trị là 24. Khi chương trình thực thi lệnh myOperator.add(“ab”, “cd”), chương trình sẽ tự đối chiếu các kiểu tham số, thấy ‘ab’ và ‘cd’ có dạng gần với kiểu String nhất, nên phương thức add(String, String) sẽ được gọi và trả về giá trị là ‘abcd’. Lưu ý: Khi gọi hàm với các kiểu dữ liệu khác với các hàm đã được khai báo, sẽ có sự chuyển đổi kiểu ngầm định diễn ra. Khi không thể thực hiện chuyển đổi kiểu ngầm định, Java sẽ phát sinh một thông báo lỗi. 67
  68. Chẳng hạn, trong chương trình trên, nếu thực thi lệnh myOperator.add(4.0f, 5) có dạng add(float, int), chương trình chuyển ngầm định số nguyên 5 thành float (chuyển từ kiểu int sang float là kiểu chuyển ngầm định trong Java) để có thể sử dụng được khai báo add(float, float) và kết quả sẽ là 9.0f. Nếu thực thi lệnh myOperator.add(‘ab’, 5) có dạng add(String, int), vì int không thể chuyển ngầm định thành String nên lệnh này sẽ phát sinh lỗi. Để tránh lỗi này, phải chuyển đổi kiểu tường minh cho số 5 thành kiểu String bằng một trong các cách sau: myOperator.add(“ab”, (new Int(5)).toString()); myOperator.add(“ab”, 5 + ””); 3.4. Lớp trừu tượng Lớp trừu tượng là một dạng lớp đặc biệt, trong đó các phương thức chỉ được khai báo ở dạng khuôn mẫu (template) mà không được cài đặt chi tiết. Việc cài đặt chi tiết các phương thức chỉ được thực hiện ở các lớp con kế thừa lớp trừu tượng đó. Lớp trừu tượng được sử dụng khi muốn định nghĩa một lớp mà không thể biết và định nghĩa ngay được các thuộc tính và phương thức của nó. Lớp trừu tượng được khái báo như cách khai báo các lớp thông thường, ngoại trừ có thêm từ khoá abstract trong phần tính chất: [public] abstract class { } Trong đó: Tính chất: mặc định là public, bắt buộc phải có từ khoá abstract để xác định đây là một lớp trừu tượng. Tên lớp: tuân thủ theo quy tắc đặt tên lớp thông thường của Java. Lưu ý: Lớp trừu tượng cũng có thể kế thừa một lớp khác, nhưng lớp cha cũng phải là một lớp trừu tượng. Chương trình sau khai báo một lớp trừu tượng là lớp động vật (Animal) một cách chung chung. abstract class Animal{ } Khai báo phương thức của lớp trừu tượng Tất cả các thuộc tính và phương thức của lớp trừu tượng đều phải khai báo là trừu tượng. Hơn nữa, các phương thức của lớp trừu tượng chỉ được khai báo ở dạng khuôn mẫu mà không có phần khai báo chi tiết. Cú pháp khai báo phương thức của lớp trừu tượng: [public] abstract ([ ]) [throws ]; Trong đó: 68
  69.  Tính chất: tính chất của một thuộc tính hay phương thức của lớp trừu tượng luôn là public. Nếu không khai báo tường minh thì giá trị mặc định cũng là public.  Kiểu dữ liệu trả về: có thể là các kiểu cơ bản của Java, cũng có thể là kiểu do người dùng tự định nghĩa (kiểu đối tượng).  Tên phương thức: tuân thủ theo quy tắc đặt tên phương thức của lớp  Các tham số: nếu có thì mỗi tham số được xác định bằng một cặp . Các tham số được phân cách nhau bởi dấu phẩy.  Các ngoại lệ: nếu có thì mỗi ngoại lệ được phân cách nhau bởi dấu phẩy. Lưu ý:  Tính chất của phương thức trừu tượng không được là private hay static. Vì phương thức trừu tượng chỉ được khai báo chi tiết (nạp chồng) trong các lớp dẫn xuất (lớp kế thừa) của lớp trừu tượng. Do đó, nếu phương thức là private thì không thể nạp chồng, nếu phương thức là static thì không thể thay đổi trong lớp dẫn xuất.  Phương thức trừu tượng chỉ được khai báo dưới dạng khuôn mẫu nên không có phần dấu móc “{}” mà kết thúc bằng dấu chấm phẩy “;”. Chương trình sau khái báo hai phương thức của lớp trừu tượng Animal trong chương trình trên: Phương thức getName() trả về tên loài động vật, dù chưa biết tên cụ thể loài nào nhưng kiểu trả về là String. Phương thức getFeet() trả về số chân của loài động vật, cũng chưa biết cụ thể là bao nhiêu chân nhưng kiểu trả về là int. abstract class Anima{ abstract String getName(); abstract int getFeet(); } Sử dụng lớp trừu tượng Lớp trừu tượng được sử dụng thông qua các lớp dẫn xuất của nó. Vì chỉ có các lớp dẫn xuất mới cài đặt cụ thể các phương thức được khai báo trong lớp trừu tượng. Chương trình sau khai báo lớp về loài chim (Bird) kế thừa từ lớp Animal đã khai báo ở trên. Lớp này cài đặt chi tiết hai phương thức đã được khai báo trong lớp Animal: phương thức getName() sẽ trả về tên loài là “Bird”; phương thức getFeet() trả về số chân của loài chim là 2. public class Bird extends Animal { // Trả về tên loài chim public String getName() { return “Bird”; 69
  70. } // Trả về số chân của loài chim public int getFeet() { return 2; } } Đồng thời ta xây dựng khai báo lớp về loài mèo (Cat) cũng kế thừa từ lớp Animal. Lớp này cài đặt chi tiết hai phương thức đã được khai báo trong lớp Animal: phương thức getName() sẽ trả về tên loài là “Cat”; phương thức getFeet() trả về số chân của loài mèo là 4. public class Cat extends Animal { // Trả về tên loài mèo public String getName() { return “Cat”; } // Trả về số chân của loài mèo public int getFeet() { return 4; } } Cuối cùng xây dựng lớp sử dụng lại hai lớp Bird và Cat trong các chương trình trên. Ví dụ 3.20: public class AnimalDemo { public static void main(String args[]) { Bird myBird = new Bird(); System.out.println(“The ” + myBird.getName() + “ has ”+ myBird.getFeet() + “ feets”); Cat myCat = new Cat(); System.out.println(“The ” + myCat.getName() +“ has ”+ myCat.getFeet() + “ feets”); }} 70
  71. 3.5. Lớp Object Tất cả các lớp trong java kế thừa từ lớp Object hay lớp Object là lớp cha của tất cả các lớp. Sử dụng biến kiểu Object để tham chiếu tới các đối tượng thuộc bất kỳ kiểu nào. Ví dụ 3.21: Object obj = new Employee("Harry Hacker", 35000); Tất cả các kiểu mảng bất kế là mảng nguyên thủy hay mảng đối tượng đều kế thừa từ lớp Object. Ví dụ 3.22: Employee [ ] staff = new Employee[ ]; obj= staff; obj= new int [10]; 3.5.1. Phương thức equals Phương thức equals trong lớp Object dùng để kiểm tra tính tương đương giữa hai đối tượng. Ví dụ 3.23: class Employee { . . . public boolean equals(Object otherObject) { // Kiểm tra nếu các đối tượng là tương đương if (this == otherObject) return true; if (otherObject == null) return false; // Nếu các lớp khác nhau chúng không thể bằng nhau if (getClass() != otherObject.getClass()) return false; Employee other = (Employee) otherObject; // Kiểm tra các trường có các giá trị giống nhau 71
  72. return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay); } } 3.5.2. Phương thức toString ( ) Một phương thức quan trọng trong lớp Object là toString ( ), trả về một chuỗi biểu diễn giá trị của đối tượng. Ví dụ 3.24: public String toString() { return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay+ "]"; } 3.6. Giao diện 3.6.1. Mô tả giao diện Một Interface trong Java là một bản thiết kế của một lớp. Nó chỉ có các phương thức trừu tượng. Interface là một kỹ thuật để thu được tình trừu tượng hoàn toàn và đa kế thừa trong Java. Interface trong Java cũng biễu diễn mối quan hệ IS-A. Nó không thể được khởi tạo giống như lớp trừu tượng. Một Interface trong Java là một tập hợp các phương thức trừu tượng (abstract). Một class triển khai một interface, do đó kế thừa các phương thức abstract của interface. Một interface không phải là một lớp. Một lớp mô tả các thuộc tính và hành vi của một đối tượng. Một interface chứa các hành vi mà một class triển khai. Trừ khi một lớp triển khai interface là lớp trừu tượng abstract, còn lại tất cả các phương thức của interface cần được định nghĩa trong class. Một interface tương tự với một class bởi những điểm sau đây:  Một interface có thể bao gồm bất cứ phương thức nào.  Một interface được viết trong một file với định dạng .java, với tên của interface cùng với tên của file.  Bytecode của interface xuất hiện trong một .class file.  Interface xuất hiện trong package, những bytecode file tương ứng phải ở trong cấu trúc thư mục có cùng tên package. Mặc dù vây, một interface khác với một class ở một số điểm sau đây, bao gồm:  Không thể khởi tạo một interface.  Một interface không chứa bất cứ hàm contructor nào. 72
  73.  Tất cả các phương thức của interface đều là abstract.  Một interface không thể chứa một trường nào trừ các trường vừa static và final.  Một interface không thể kế thừa từ lớp, nó được triển khai bởi một lớp.  Một interface có thể kế thừa từ nhiều interface khác. Ví dụ 3.25: Tạo Printable Interface có phương thức print (), sau đó khai báo lớp triển khai interface vừa tạo. interface printable{ void print(); } class A6 implements printable{ public void print(){System.out.println("Hello");} public static void main(String args[]){ A6 obj = new A6(); obj.print(); } } 3.6.2. Mục đích sử dụng giao diện Trong một số trường hợp, Interface cần thiết cho các nhóm phát triển khác nhau đồng ý với một “contract”, từ đó xác định cách phần mềm của họ tương tác Mỗi nhóm nên có cách viết code theo cách riêng mà không cần biết cách các nhóm khác làm thế nào Các interface trong java có thể được sử dụng để xác định các contracts. Interface không thuộc bất kỳ lớp nào cho dù chúng làm việc kết hợp với các lớp khác. Java không cho phép đa kế thừa trong nhưng interface hỗ trợ điều đó Trong Java, một class có thể kế thừa từ chỉ một class nhưng nó có thể implement nhiều interfaces. 3.6.3. So sánh giao diện và lớp trừu tượng Cả lớp abstract và Interface đều sử dụng để thu được tính trừu tượng, từ đó cả hai đều có thể khai báo các phương thức trừu tượng. Cả lớp trừu tượng và Interface không thể được khởi tạo. Giữa lớp trừu tượng và Interface, có các điểm khác nhau như sau: Lớp trừu tượng Interface Lớp trừu tượng có thể có các phương thức Interface chỉ có thể có phương thức abstract và non-abstract abstract 73
  74. Lớp trừu tượng không hỗ trợ đa kế thừa Interface hỗ trợ đa kế thừa Lớp trừu tượng có thể có các biến final, non- Interface chỉ có các biến static và final final, static và non-static Lớp trừu tượng có thể có phương thức static, Interface không thể có phương thức phương thức main và constructor static, main hoặc constructor. Từ khóa abstract được sử dụng để khai báo Từ khóa interface được sử dụng để lớp trừu tượng khai báo Interface Lớp trừu tượng có thể cung cấp trình triển Interface không cung cấp trình triển khai của Interface khai cụ thể của lớp abstract Ví dụ: public abstract class Shape{ public Ví dụ: public interface Drawable{ void abstract void draw(); } draw(); } 3.7. Lớp nội Java cho phép định nghĩa một lớp nằm trong một lớp khác, những lớp như vậy được gọi là lớp lồng nhau (nested class). Cú pháp: class Outer{ class Nested{ // lớp lồng } } Lớp Outer định nghĩa bên ngoài, lớp Nested (lồng) định nghĩa bên trong lớp Outer. Lớp lồng được chia thành hai loại: static (Lớp lồng tĩnh) và no-static (Lớp nội). Lớp nội được gắn với đối tượng thuộc lớp bao bọc lấy nó và có thể truy xuất tới các phương thức và thuộc tính thuộc đối tượng đó, thậm chí các thành phần khai báo private. Để tạo một đối tượng lớp nội phải tạo lớp ngoài trước. Sau đó tạo đối tượng lớp nội theo cú pháp như sau: OuterClass.InnerClass innerObject = outerObject.new InnerClass(); 74
  75. Lớp lồng static- tĩnh được gắn kết với đối tượng thuộc lớp bao bọc lấy nó nhưng không thể truy xuất tới các thuộc tính và phương thức của đối tượng đó. Truy xuất tới lớp lồng static bằng cách thêm dấu chấm và tên lớp: OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass(); Lợi ích của việc sử dụng lớp nội: Gom nhóm các lớp có chung mục đích lại với nhau Tăng tính đóng gói Tăng tính dễ đọc và dễ bảo trì cho code 3.7.1. Các kiểu lớp nội 3.7.1.1. Lớp nội thành viên Một lớp non-static được tạo ra bên trong một lớp nhưng ngoài một phương thức được gọi là lớp nội thành viên member inner class trong java. Ví dụ 3.26: class MemberOuterExample { private int data = 30; class Inner { void msg() { System.out.println("data is " + data); } } public static void main(String args[]) { MemberOuterExample obj = new MemberOuterExample(); MemberOuterExample.Inner in = obj.new Inner(); in.msg(); } } Kết quả: 3.7.1.2. Lớp nội nặc danh Một lớp không có tên được gọi là lớp nội nặc danh (anonymous inner class). Được sử dụng nếu phải ghi đè phương thức của lớp hoặc interface. Anonymous inner class có thể được tạo ra bằng hai cách: 75
  76. Class: có thể là abstract class hoặc class cụ thể. Interface abstract class Person { abstract void eat(); } class TestAnonymousInner { public static void main(String args[]) { Person p = new Person() { void eat() { System.out.println("nice fruits"); } }; p.eat(); } } Kết quả: 3.7.1.3. Lớp nội địa phương Một lớp được tạo ra bên trong một phương thức được gọi là local inner class. Nếu muốn gọi các phương thức của lớp được khai báo bên trong một phương thức, phải tạo ra thể hiện của lớp này bên trong phương thức chứa nó. public class localInner1 { private int data = 30;// instance variable void display() { class Local { void msg() { System.out.println(data); } } Local l = new Local(); l.msg(); } 76
  77. public static void main(String args[]) { localInner1 obj = new localInner1(); obj.display(); } } Kết quả: 3.8. Xử lý ngoại lệ 3.8.1. Giới thiệu Exception là một loại lỗi đặc biệt. Lỗi này xuất hiện vào lúc thực thi chương trình. Các trạng thái không bình thường xảy ra trong khi thi hành chương trình tạo ra các exception. Nếu không xử lý các trạng thái này thì chương trình có thể bị kết thúc đột ngột. Ví dụ, việc chia cho 0 sẽ tạo một lỗi trong chương trình. Ngôn ngữ Java cung cấp cơ chế dùng để xử lý ngoại lệ rất hiệu quả. Việc xử lý này làm hạn chế tối đa trường hợp hệ thống bị hỏng (crash) hay hệ thống bị ngắt đột ngột. Tính năng này làm cho Java trở thành một ngôn ngữ lập trình mạnh. 3.8.2. Mục đích của việc xử lý ngoại lệ Một chương trình nên có cơ chế xử lý ngoại lệ. Nếu không, chương trình sẽ bị ngắt khi ngoại lệ xảy ra. Trong trường hợp đó, tất cả các nguồn tài nguyên mà hệ thống đã cấp không được giải phóng. Điều này gây lãng phí tài nguyên. Để tránh trường hợp này, tất cả các nguồn tài nguyên mà hệ thống cấp nên được thu hồi lại. 3.8.3. Lớp Exception Lớp Exception và các lớp con của nó là một dạng Throwable chỉ ra các điều kiện mà một ứng dụng có thể muốn nắm bắt. Lớp Exception dùng để kiểm tra ngoại lệ. Đoạn code sau đây sẽ thể hiện cấu trúc của một lớp Exception. public class Exception extends Throwable{ } 3.8.4. Xử lý ngoại lệ Khi một ngoại lệ xảy ra, đối tượng (object) tương ứng với ngoại lệ đó được tạo ra. Đối tượng này sau đó được truyền cho phương thức là nơi mà ngoại lệ xảy ra. Đối tượng này chứa thông tin chi tiết về ngoại lệ. 77
  78. Thông tin này có thể được nhận về và được xử lý. Lớp ‘Throwable’ là lớp trên cùng của lớp Exception, đây là lớp cha của tất cả các ngoại lệ khác. 3.8.5. Mô hình xử lý ngoại lệ Mô hình xử lý ngoại lệ của Java được gọi là ‘catch and throw’. Trong mô hình này, khi một ngoại lệ xảy ra, ngoại lệ sẽ bị chặn và chương trình chuyển đến một khối xử lý ngoại lệ. Người lập trình phải xử lý các ngoại lệ khác nhau phát sinh trong chương trình. Các ngoại lệ phải được xử lý, hoặc thoát khỏi chương trình khi nó xảy ra. Dưới đây là cấu trúc của mô hình xử lý ngoại lệ: try { // đoạn mã có khả năng gây ra ngoại lệ } catch(Exception e1) { // Nếu các lệnh trong khối ‘try’ tạo ra ngoại lệ có loại e1, thì thực hiện //xử lý ngoại lệ nếu không chuyển xuống khối 'catch' tiếp theo } catch(Exception e2) { // Nếu các lệnh trong khối ‘try’ tạo ra ngoại lệ có loại e2, thì thực hiện //xử lý ngoại lệ nếu không chuyển xuống khối 'catch' tiếp theo } catch(Exception eN) { // Nếu các lệnh trong khối ‘try’ tạo ra ngoại lệ có loại eN, thì thực hiện //xử lý ngoại lệ nếu không chuyển xuống khối 'catch' tiếp theo } finally { // khối lệnh nay luôn được thực hiện cho dù ngoại lệ có xảy ra hay không. } 3.8.5.1. Các ưu điểm của mô hình ‘catch và throw’ Mô hình ‘catch và throw’ có hai ưu điểm: Người lập trình chỉ phải xử lý ngoại lệ khi cần thiết. Không cần phải thực hiện tại mọi mức. 78