LỜI NÓI ĐẦU

Kích thước: px
Bắt đầu hiển thị từ trang:

Download "LỜI NÓI ĐẦU"

Bản ghi

1 MỤC LỤC DANH MỤC THUẬT NGỮ TIẾNG ANH LỜI NÓI ĐẦU...8 Chương 1. TỔNG QUAN VỀ LẬP TRÌNH JAVA Lịch sử ra đời và phát triển của Java Đặc trưng ngôn ngữ Java Các ứng dụng của Java Dịch và thực thi một chương trình viết bằng Java Kiến trúc chương trình xây dựng trên Java Kiến trúc chương trình Java Chương trình Java đầu tiên Chương 2. CẤU TRÚC LẬP TRÌNH CƠ BẢN TRONG JAVA Các kiểu dữ liệu Biến Toán tử Strings và StringBuider Lớp String StringBuffer và StringBuilder Nhập/Xuất dữ liệu Câu lệnh và các cấu trúc lệnh trong Java Lệnh, khối lệnh trong Java Câu lệnh if else Câu lệnh switch-case Vòng lặp While Vòng lặp do-while Vòng lặp for Lệnh break và continue Số lớn trong java Mảng Vòng lặp for each Khởi tạo mảng và mảng nặc danh Copy mảng Các tham số dòng lệnh Sắp xếp mảng

2 Mảng nhiểu chiều Chương 3. ĐỐI TƯỢNG, LỚP, KẾ THỪA, GIAO DIỆN Đối tượng, lớp, lớp trừu tượng Khái niệm Khai báo lớp Thuộc tính của lớp Phương thức của lớp Chỉ định truy xuất lớp Tạo đối tượng Kế thừa và đa hình Mô tả kế thừa Kế thừa đơn Kế thừa kép Lớp cha, lớp con Cách sử dụng từ khóa super Cách nạp chồng phương thức Đa hình Lớp trừu tượng Lớp Object Phương thức equals Phương thức tostring ( ) Giao diện Mô tả giao diện Mục đích sử dụng giao diện So sánh giao diện và lớp trừu tượng Lớp nội Các kiểu lớp nội Xử lý ngoại lệ Giới thiệu Mục đích của việc xử lý ngoại lệ Lớp Exception Xử lý ngoại lệ Mô hình xử lý ngoại lệ Các khối chứa nhiều Catch

3 Khối finally Các ngoại lệ được định nghĩa với lệnh throw và throws Danh sách các ngoại lệ Giao diện Collection Giới thiệu Các kiểu collection Collection frame work Thread Thread là gì, Tạo Thread Các trạng thái của Thread Các phương thức của lớp Thread Quản lý Thread Luồng chạy ngầm (deamon) Đa luồng và tương tranh Định nghĩa đa luồng Sử dụng phương thức isalive() và join() Điều kiện tương tranh và cách khắc phục Cách sử dụng phương thức wait() và notify() Định nghĩa deadlock và khắc phục CHƯƠNG 4. LẬP TRÌNH GIAO DIỆN VỚI JAVA SWING Giới thiệu Swing và mô hình MVC Lợi ích của sử dụng Swing so với AWT Mô tả container trong Swing Quản lý Layout BorderLayout GridLayout GirdBagLayout GroupLayout Text Input TextFields Label Password Fields TextAreas Scroll Panes

4 4.4. Các thành phần lựa chọn Checkboxes Radio Button ComboBoxes Slider Menu Xây dựng menu Icons và Menu Items Pop-Up Menu Hiển thị và ẩn các mục trong menu Toolbars Tooltips Dialog Boxes Tạo Dialogs Option Dialogs Trao đổi dữ liệu File Dialogs Các lựa chọn màu Chương 5. LUỒNG VÀ TẬP TIN Mở đầu Luồng Khái niệm luồng Luồng byte (Byte Streams) Luồng ký tự (Character Streams) Những luồng được định nghĩa trước (The Predefined Streams) Sử dụng luồng Byte Đọc dữ liệu từ Console Xuất dữ liệu ra Console Đọc và ghi file dùng luồng Byte Đọc và ghi dữ liệu nhị phân File truy cập ngẫu nhiên (Random Access Files) Sử dụng luồng ký tự Nhập Console dùng luồng ký tự Xuất Console dùng luồng ký tự

5 Đọc/ghi File dùng luồng ký tự Lớp File Chương 6. ỨNG DỤNG GIAO TIẾP VỚI CƠ SỞ DỮ LIỆU Giới thiệu Kiến trúc JDBC, cách tạo ứng dụng JDBC Các khái niệm cơ bản JDBC Driver JDBC URL Kết nối cơ sở dữ liệu với JDBC Đăng ký trình điều khiển Thực hiện kết nối Các ví dụ Cách tạo truy vấn và các kiểu truy vấn Mô tả Rowset, JDBCRowset và CatchedRowset CHƯƠNG 7. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI SOCKET Giới thiệu chung Lập trình thao tác với địa chỉ máy trạm Lập trình thao tác với địa chỉ IP, lớp URL Connection Ví dụ sử dụng các phương thức lớp InetAddress Lập trình ứng dụng mạng với TCP socket Lớp Socket và lớp Server Socket Kỹ thuật lập trình truyền thông với giao thức TCP Một số ví dụ Lập trình ứng dụng mạng với UDP Socket Một số lớp Java hỗ trợ lập trình với UDP Socket Kỹ thuật lập trình truyền thông với giao thức UDP Lấy dữ liệu web URL và URI Sử dụng URLConnection để truy xuất dữ liệu Gửi Khái niệm Session Cách tạo message Các bước để tạo và gửi message Các bước cần thiết để đọc một message

6 Các bước cần thiết để trả lời một TÀI LIỆU THAM KHẢO

7 DANH MỤC THUẬT NGỮ TIẾNG ANH Từ Abstract Trừu tượng Nghĩa của từ Assert Break Catch Continue Default Extends Final Finally Implements Import Instanceof Interface Native new Null Package Private Protected Public Return Super Synchronized This Được sử dụng để xác định vị trí lỗi chương trình nội bộ Dừng vòng lặp Từ khóa đầu của một khối bắt ngoại lệ Bỏ qua phần cuối vòng lặp, tiếp tục sang bước tiếp theo. Giá trị mặc định của hàm switch() Kế thừa Một hằng số, phương thức hay một lớp không được ghi đè Một phần của khối xử lý ngoại lệ try luôn được thực hiện Định nghĩa giao diện mà lớp thực hiện Khai báo một gói thư viện Kiểm tra một đối tượng là một thể hiện của lớp Giao diện Một phương thức được thực hiện bởi hệ thống máy chủ Tạo một đối tượng mới của lớp Tham chiếu rỗng Gói 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 của và các lớp khác trong cùng một gói Tiền tố có thể được truy cập bởi phương thức của tất cả các lớp Trả về của một phương thức Gọi phương thức khởi tạo của lớp cha Cho biết đây là một phương thức đồng bộ 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 Mục đích Chương 1. TỔNG QUAN VỀ LẬP TRÌNH JAVA 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 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ã 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 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 Kiến trúc chương trình xây dựng trên Java 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.*; class classname // Khai báo tên thư viện sẵn có, nếu cần dùng // 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ên gói>; Để đặ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 Lưu ý: 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. 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 <Tên thư viện>; 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 Chương trình Java đầu tiên 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 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 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 Admin */ public class HelloWorldApp /** 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: 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 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 8 bits short 16 bits int 32 bits long 64 bits l Kiểu số thực Kiểu dữ liệu Kích Phạm vi biểu diễn Giá trị float 32 bits -3.4e38 3.4e38 0.0f double 64 bits E Kiểu logic (boolean) E Nhận giá trị true hoặc false. Giá trị mặc định là false Kiểu ký tự (char) 0.00d Kiểu dữ liệu Kích Phạm vi biểu diễn Giá trị char 16 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 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: (<type>) <exp> Ví dụ 2.1: 2.2. Biến Trong đó: <type>: kiểu dữ liệu cần ép sang float fnum = 2.2; int icount = (int) fnum <exp>: là biểu thức cần ép kiểu cho giá trị trả về của biểu thức 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 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ử + Trả về giá trị tổng hai toán hạng - Trả về kết quả của phép trừ. Mô tả * 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 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: <biểu thức 1>? <biểu thức 2> : <biểu thức 3>; Trong đó: Toán tử gán 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 <biểu thức 1> xác định là True biểu thức 3: Là giá trị trả về nếu <biểu thức 1> xác định là False 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 Strings và StringBuider 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 char charat (int index) int compareto (Object o) Int compareto (String anotherstring) int comparetoignorecase (String str) String concat (String str) boolean contentequals (StringBuffer sb) static String copyvalueof (char[] data) static String copyvalueof (char[] data, int offset, int count) boolean endswith (String suffix) boolean equals (Object anobject) Boolean equalsignorecase (String anotherstring) byte[] getbytes ( ) Mô tả Trả về một ký tự tại vị trí có chỉ số được chỉ định. So sánh một String với một Object khác. So sánh hai chuỗi theo từ điển. (Phân biệt chữ hoa chữ thường) So sánh hai chuỗi theo từ điển. (Không phân biệt chữ hoa chữ thường) Nối chuỗi Trả về true nếu và chỉ nếu chuỗi này đại 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ự trong mảng quy định. Trả về một chuỗi đại diện cho chuỗi ký tự trong mảng quy định. Kiểm tra nếu chuỗi này kết thúc với hậu tố quy định. So sánh với một đối tượng So sánh với một String khác, không phân 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 đị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 byte[] getbytes (String charsetname) void getchars (int srcbegin, int srcend, char[] dst, int dstbegin) int indexof (int ch) int indexof (int ch, int fromindex) int indexof (String str) int indexof(string str, int fromindex) int lastindexof (int ch) int lastindexof (int ch, int fromindex) int lastindexof (String str) int length ( ) boolean matches (String regex) boolean regionmatches (int offset, String other, int ooffset, int len) String replace (char oldchar, char newchar) 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 trước, lưu trữ kết quả vào một mảng byte mới. Copy các ký tự từ chuỗi này vào mảng ký tự đích. Trả về chỉ số trong chuỗi này xuất hiện đầu tiên của ký tự cụ thể. Trả về chỉ số trong chuỗi này xuất hiện đầ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 đầu tiên của chuỗi quy định. Trả về chỉ số trong chuỗi này xuất hiện đầ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 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 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 cùng bên phải của chuỗi quy định. Trả về độ dài chuỗi. Kiểm tra chuỗi này khớp với biểu thức chính quy chỉ định hay không. Kiểm tra chuỗi có một phần giống nhau. Trả về một chuỗi mới từ thay thế tất cả các lần xuất hiện của ký tự oldchar trong chuỗi này với ký tự newchar. 25

26 String replaceall (String regex, String replacement) String replacefirst (String regex, String replacement) String[] split (String regex) boolean startswith (String prefix) CharSequence subsequence (int beginindex, int endindex) String substring (int beginindex, int endindex) char[] tochararray ( ) String tolowercase ( ) String tolowercase (Locale locale) String tostring ( ) String touppercase ( ) String touppercase (Locale locale) Thay thế tất cả các chuỗi con của chuỗi này khớp với biểu thức chính quy bởi String mới replacement Thay thế chuỗi con đầu tiên của chuỗi này khớp với biểu thức chính quy bởi một String mới replacement Tách chuỗi này thành các chuỗi con, tại 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ố quy định. Trả về một chuỗi ký tự mới là một dãy con của dãy này. Trả về một chuỗi ký tự mới là một dãy con của dãy này. Từ chỉ số bắt đầu cho tới chỉ số cuối. Chuyển chuỗi này thành mảng ký tự. 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) 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. Trả về String này. 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) 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 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 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 Kết quả: 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); 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 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 lệnh 1.1 lệnh 1.2 // khối 2 lệnh 2.1 lệnh 2.2 // kết thúc khối lệnh 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 Câu lệnh if else Java đưa ra hai cấu trúc lệnh if else như sau: Dạng 1: if (<điều_kiện>) <khối_lệnh>; Dạng 2: if (<điều_kiện>) 32

33 <khối _lệnh1>; else <khối _lệnh2>; Trong đó: <điều kiện> là Biểu thức boolean như toán tử so sánh. <khối _lệnh1>, <khối _lệnh2> 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 ) else System.out.println( Hom nay la chu nhat\n ); System.out.println( Hom nay khong la chu nhat\n" ); 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 : break; default: actionn statement; 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 : break; System.out.println( Sunday ); 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 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 Vòng lặp do-while do Cú pháp: 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 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 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 BigInteger BigInteger 2.8. Mảng floatvalue() Chuyển BigInteger thành kiểu float. max(biginteger val) Trả về giá trị lớn nhất của BigInteger và val. min(biginteger val) Trả về giá trị nhỏ nhất của BigInteger và val 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 <kiểu dữ liệu> <tên mảng>[]; hoặc <kiểu dữ liệu>[] <tên mảng>; 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 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) Ví dụ 2.13: //phan than vong lap 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ả: 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 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ị 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! 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 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[][]; 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: Hoặc [Kiểu_dữ_liệu][][] Tên_mảng; [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= l; d. Fload pi= ; e. Double p= e-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< b 36). Bài 3. Hãy viết chương trình tính tổng các chữ số của một số nguyên bất kỳ. Ví dụ: Số có tổng các chữ số là: = 32. Bài 4. Viết chương trình phân tích một số nguyên thành các thừa số nguyên tố Ví dụ: Số 28 được phân tích thành 2 x 2 x 7 Bài 5. Viết chương trình liệt kê tất cả các số nguyên tố nhỏ hơn n cho trước. Bài 6. Viết chương trình liệt kê n số nguyên tố đầu tiên. Bài 7. Dãy số Fibonacci được định nghĩa như sau: F0 =1, F1 = 1; Fn = Fn-1 + Fn-2 với n>=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 , 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à: // số phần tử chẵn = 2 f) Mảng được nhập vào là: // 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 Mục đích Chương 3. ĐỐI TƯỢNG, LỚP, KẾ THỪA, GIAO DIỆN 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 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 Khai báo lớp class <ClassName> <kiểu dữ liệu> <field_1>; <kiểu dữ liệu> <field_2>; 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; Thuộc tính của lớp class <ClassName> // khai báo những thuộc tính của lớp <tiền tố> <kiểu dữ liệu> 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 Ví dụ 3.4: - public: Có thể được truy cập từ bất kỳ một lớp nào 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 Phương thức của lớp Khai báo: <Tiền tố> <kiểu trả về> <Tên phương thức> (<danh sách đối số>) <khối lệnh>; Trong đó: <Tiền tố>: Để 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. <kiểu trả về>: có thể là kiểu void, kiểu cơ sở hay một lớp. <Tên phương thức>: đặt theo qui ước giống tên biến. 52

53 <danh sách đối số>: 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 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 Kế thừa và đa hình 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 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: <thuộc tính> <tên lớp con> extends <tên lớp cha> 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() " + tocdo); System.out.println(tenxe+ "co gia ban ban " + giaban + " toc do: 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 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 đó Giao diện (Interface) Cú pháp khai báo một giao diện như sau: [Tính chất] interface <tên giao diện> [extends <danh sách giao diện>] 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] <kiểu giá trị trả về> <tên phương thức> ([<các tham số>]) [throws <danh sách ngoại lệ>]; 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 <kiểu tham số> <tên tham số>. 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(); 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: <tính chất> class <tên lớp> implements <các giao diện> 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 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 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 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 Đa hình 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ề Đ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 <tên lớp> 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 <kiểu dữ liệu trả về> <tên phương thức> ([<các tham số>]) [throws <các ngoại lệ>]; 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 <kiểu tham số> <tên tham số>. 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]; 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); 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 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(); 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 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 Lớp trừu tượng có thể có các phương thức abstract và non-abstract Interface Interface chỉ có thể có phương thức abstract 73

74 Lớp trừu tượng không hỗ trợ đa kế thừa Lớp trừu tượng có thể có các biến final, nonfinal, static và non-static Interface hỗ trợ đa kế thừa Interface chỉ có các biến static và final Lớp trừu tượng có thể có phương thức static, phương thức main và constructor Từ khóa abstract được sử dụng để khai báo lớp trừu tượng Lớp trừu tượng có thể cung cấp trình triển khai của Interface Ví dụ: public abstract class Shape public abstract void draw(); Interface không thể có phương thức static, main hoặc constructor. Từ khóa interface được sử dụng để khai báo Interface Interface không cung cấp trình triển khai cụ thể của lớp abstract Ví dụ: public interface Drawable void 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 Các kiểu lớp nội 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ả: 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ả: 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ệ 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 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 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 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 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. try Dưới đây là cấu trúc của mô hình xử lý ngoại lệ: // đ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 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

79 Thông báo lỗi có thể được hiện ra khi tiến hành xử lý ngoại lệ Các khối try và catch Khối try-catch được sử dụng để thi hành mô hình catch và throw của việc xử lý ngoại lệ. Một hay nhiều khối catch có thể theo sau một khối try. Các khối catch này bắt các ngoại lệ có khả năng tạo ra trong trong khối try. Ví dụ Try dofileprocessing(); // phương thức do người sử dụng định nghĩa displayresults(); catch (Exeption e) // thể hiện của ngoại lệ System.err.println( Error : + e.tostring()); e.printstacktrace(); Ở đây, e là đối tượng của lớp Exception. Chúng ta có thể sử dụng đối tượng này để in các chi tiết về ngoại lệ. Các phương thức tostring và printstacktrace được sử dụng để mô tả các ngoại lệ xảy ra. Hình sau chỉ ra kết xuất của phương thức printstacktrace(). Khối Try và Catch Để xử lý được ngoại lệ nào, ta phải chỉ ra kiểu ngoại lệ tương ứng catch(exception e). Khi ngoại lệ không biết thuộc kiểu nào, chúng ta có thể sử dụng lớp Exception để bắt ngoại lệ đó Các khối chứa nhiều Catch Nhiều khối catch xử lý các loại ngoại lệ khác nhau một cách độc lập. Chúng được liệt kê trong đoạn mã sau: try dofileprocessing(); 79

80 displayresults(); catch(lookupexception e) // e LookupException object handlelookupexception(e); // phương thức xử lý lỗi do người sử dụng //định nghĩa catch(exception e) System.err.println( Error: + e.printstacktrace()); Trong trường hợp này, khối catch đầu tiên sẽ bắt giữ một LockupException. Khối catch thứ hai sẽ xử lý kiểu ngoại lệ khác với khối catch thứ nhất. Một chương trình cũng có thể chứa các khối try lồng nhau. Ví dụ đoạn mã dưới đây: try statement 1; statement 2; try statement1; statement2; catch(exception e) // của khối try trong catch(exception e) // của khối try ngoài Khi sử dụng các try lồng nhau, khối try bên trong được thi hành đầu tiên. Bất kỳ ngoại lệ nào bị chặn trong khối try sẽ bị bắt giữ trong các khối catch theo sau. Nếu khối catch thích hợp không được tìm thấy thì các khối catch của các khối try bên ngoài sẽ được xem xét. Nếu không, Java Runtime Environment xử lý các ngoại lệ. Ví dụ 3.27: Chương trình sau minh họa cách sử dụng các khối try và catch class TryClass public static void main(string args[]) int demo=0; try System.out.println(20/demo); 80

81 catch(arithmeticexception a) System.out.println( Cannot Divide by zero ); Kết xuất của chương trình: Trong chương trình này, một số được chia cho 0. Đây không là phép toán số học hợp lệ. Do đó một ngoại lệ xảy ra và được bắt giữ trong khối catch. Khi nhận biết được loại ngoại lệ nào có thể xảy ra, ta viết lệnh trong khối catch tương ứng. Ở đây, a được sử dụng như một đối tượng của ArithmeticException để in các chi tiết về ngoại lệ. Nếu thay thế lệnh System.out.println của khối catch bằng System.out.println (a.getmessage()) thì kết xuất của chương trình như sau: Khi các khối try được sử dụng mà không có các khối catch nào, chương trình sẽ biên dịch mà không gặp lỗi nào nhưng sẽ bị ngắt khi thực thi. Vì ngoại lệ đã xảy ra khi thực thi chương trình mà không được xử lý Khối finally Khối finally thực hiện việc thu dọn khi ngoại lệ xảy ra. Khối này có thể kết hợp với khối try. Khối finally chứa các câu lệnh thu hồi tài nguyên hoặc in ra các câu lệnh thông báo. Các lệnh này bao gồm: try Đóng tập tin. Đóng ResultSet (được sử dụng trong chương trình cơ sở dữ liệu). Đóng lại các kết nối được tạo trong cơ sở dữ liệu. dosomethingthatmightthrowanexception(); 81

82 finally cleanup(); Phương thức cleanup() được gọi nếu phương thức dosomething That Might Throw AnException() gây ra ngoại lệ. Mặt khác cleanup() cũng được gọi ngay khi không có ngoại lệ nào xảy ra và thực hiện tiếp phần sau khối lệnh finally. Khối finally là tuỳ ý, không bắt buộc. Khối này đặt sau khối catch cuối cùng. Chương trình sẽ thực thi câu lệnh đầu tiên của khối finally ngay sau khi gặp câu lệnh return hay lệnh break trong khối try. Khối finally bảo đảm lúc nào cũng được thực thi, bất chấp có ngoại lệ xảy ra hay không. Ví dụ 3.28: class FinallyDemo String name; FinallyDemo(String args[]) try name=new String( Aptech Limited ); System.out.println(name); System.out.println( Division Result is + no1/no2); catch(arithmeticexception i) finally System.out.println( Cannot Divide by zero ); name=null; // clean up code System.out.println( Finally executed ); public static void main(string args[] new FinallyDemo(args); Kết xuất của chương trình: 82

83 Các ngoại lệ được định nghĩa với lệnh throw và throws Các ngoại lệ có thể được tạo ra bằng cách sử dụng từ khoá throw. Toán hạng của throw là một đối tượng thuộc lớp được thừa kế từ Throwable. try Đoạn lệnh sau chỉ ra cách sử dụng của lệnh throw : if (flag<0) throw new MyException(); // user-defined Một phương thức có thể tạo ra nhiều ngoại lệ. Để làm được điều này, chỉ cần liệt kê danh sách các ngoại lệ mà phương thức có thể tạo ra trong phần định nghĩa phương thức. Đoạn mã sau minh họa cách sử dụng của từ khoá throws để tạo nhiều ngoại lệ: public class Example // Các ngoại lệ cách nhau bởi dấu phẩy public void exceptionexample() throws ExException, LookupException Ví dụ 3.29: try // các lệnh catch(exexception exmp) catch(lookupexception lkpex) class ArraySizeException extends NegativeArraySizeException ArraySizeException() // constructor super( You have passed an illegal array size ); 83

84 class ThrowDemo int size, array[]; ThrowDemo(int s) size=s; try checksize(); catch(arraysizeexception e) System.out.println(e); void checksize() throws ArraySizeException if (size < 0) throw new ArraySizeException(); else System.out.println( The array size is ok. ); array = new int[3]; for (int i=0; i<3; i++) array[i] = i+1; public static void main(string arg[]) new ThrowDemo(Integer.parseInt(arg[0])); Danh sách các ngoại lệ Ngoại lệ RuntimeException Lớp cha của thứ tự phân cấp ngoại lệ Lớp cơ sở cho nhiều ngoại lệ Java.lang 84

85 ArthmeticException Lỗi về số học, ví dụ như chia cho 0. IllegalAccessException IllegalArgumentException ArrayIndexOutOfBoundsExeption NullPointerException ClassNotFoundException NumberFormatException AWTException IOException FileNotFoundException Lớp không thể truy cập. Đối số không hợp lệ. Lỗi tràn mảng. Khi truy cập đối tượng null. Không thể nạp lớp yêu cầu. Việc chuyển đối từ chuỗi sang số thực không thành công. Ngoại lệ về AWT Lớp cha của các lớp ngoại lệ I/O Không thể định vị tập tin 3.9. Giao diện Collection Giới thiệu Cấu trúc tập hợp (collection) cho phép tập hợp các đối tượng lại để xử lý như một đơn vị. Các đối tượng bất kỳ có thể được lưu trữ, tìm kiếm và được thao tác như là các phần tử của tuyển tập và các phần tử là không thuần nhất Các kiểu collection List (danh sách) Cấu trúc list là dạng tập hợp các phần tử được sắp xếp theo thứ tự trong đó cho phép lặp (hai phần tử giống nhau). Ngoài những hàm mà nó được kế thừa từ Collection, List còn bổ sung thêm những hàm sau: - Object get (int index): Trả lại phần tử xác định bởi index. - Object set (int index,object elem) : Thay thế phần tử được xác định bới index bằng elem. - Void add (int index,object elem) : Chèn elem vào sau mỗi phần tử được xác định bởi index. - Object remove (int index): Bỏ đi phần tử xác định bởi index - Boolean addall (int index,collection c): Chèn các phần tử của tập hợp c vào vị trí được xác định bới index. - Int indexof(object elem): Cho biêt vị trí xuất hiện lần đầu tiên của elem trong danh sách. - Int lastdexof(object elem): Cho biêt vị trí xuất hiện cuối cùng của elem trong danh sách. - ListIterator ListIterator (): Trả về các phần tử liên tiếp bắt đầu từ phần tử đầu tiên. 85

86 Lớp ArrayList ArrayList là lớp kiểu mảng động (kích thước thay đổi được). ArrayList là cấu trúc hiệu quả nhất để cài đặt cấu trúc danh sách List. Lớp này được ưa chuộng trong java vì chức năng và tính linh hoạt mà nó cung cấp. Hầu hết các lập trình viên đều chọn ArrayList thay thế cho cách dùng Array truyền thống, bị giới hạn với kích thước xác định. Nếu một số phần tử trong mảng bị xóa đi thì bộ nhớ chứa các phần tử đó không được thu hồi. Ngược lại kích thước của ArrayList có thể tăng hoặc giảm tùy thích. Khởi tạo ArrayList Có 2 kiểu khởi tạo ArrayList, đó là non-generic và generic ArrayList list = new ArrayList(); // non-generic - kiểu cũ ArrayList<String> list = new ArrayList<String>(); // generic - kiểu mới Các phương thức cơ bản của lớp ArrayList Phương thức boolean add (Object o) void add (int index, Object element) Get (int i) Remove (int index) Contains (Object item) booleancontains(object o) isempty( ) object get(int index) int size () Mô tả Được sử dụng để nối thêm phần tử được chỉ định vào cuối một danh sách. Được sử dụng để chèn các phần tử được chỉ định tại các chỉ số vị trí quy định trong một danh sách. Trả về đối tượng tại vị trí i trong danh sách Xóa đối tượng tại vị trí có chỉ số index Trả về true nếu danh sách chứa đối tượng item Được sử dụng để trở về true nếu danh sách có chứa đối tượng Trả về true nếu danh sách rỗng Trả về đối tượng của ArrayList có chỉ số index Số phần tử có trong ArrayList. Ví dụ 3.30: Sử dụng ArrayList và các phương thức của lớp này: import java.util.*; public class ArrayListExample public static void main(string args[]) /*Khai báo mảng động dùng ArrayList có kiểu là String*/ 86

87 ArrayList<String> obj = new ArrayList<String>(); /*Cách thêm các phần tử vào mảng động*/ obj.add ("Sinh viên 1"); obj.add ("Sinh viên 2"); obj.add ("Sinh viên 3"); obj.add ("Sinh viên 4"); obj.add ("Sinh viên 5"); /* Hiển thị các phần tử của mảng */ System.out.println("Hiện tại mảng gồm các phần tử:"+obj); /*Thêm phần tử khác vào đầu mảng*/ obj.add (0, "Sinh viên 6"); obj.add (1, "Sinh viên 7"); /*Xóa phần tử đã cho trong mảng*/ obj.remove ("Sinh viên 3"); obj.remove ("Sinh viên 2"); System.out.println ("Hiện tại mảng gồm các phần tử:"+obj); /*Xóa phần tử trong mảng theo chỉ mục*/ obj.remove (1); System.out.println("Hiện tại mảng gồm các phần tử:"+obj); Kết quả: LinkedList Lớp LinkedList trong java sử dụng cấu trúc danh sách liên kết Doubly (Doubly Linked List) để lưu trữ các phần tử. Những điểm cơ bản về lớp LinkedList: Lớp LinkedList trong java có thể chứa các phần tử trùng lặp. Lớp LinkedList duy trì thứ tự của phần tử được thêm vào. Trong java lớp LinkList, thao tác nhanh vì không cần phải dịch chuyển nếu bất kỳ phần tử nào bị xoá khỏi danh sách. 87

88 Lớp LinkedList trong java có thể được sử dụng như list (danh sách), stack (ngăn xếp) hoặc queue (hàng đợi). Khởi tạo LinkedList trong java Có 2 kiểu khởi tạo LinkedList, đó là non-generic và generic LinkedList list = new LinkedList(); // non-generic - kiểu cũ LinkedList<String> list = new LinkedList<String>(); // generic - kiểu mới Các phương thức cơ bản của LinkedList Phương thức boolean add (Object o) void add (int index, Object element) void addfirst (Object o) void addlast (Object o) int size( ) boolean contains(object o) boolean remove(object o) Object getfirst( ) Object getlast( ) int indexof(object o) Mô tả Được sử dụng để nối thêm phần tử được chỉ định vào cuối một danh sách. Được sử dụng để chèn các phần tử được chỉ định tại các chỉ số vị trí quy định trong một danh sách. Được sử dụng để chèn phần tử được chỉ định vào đầu danh sách. Được sử dụng để chèn phần tử được chỉ định vào cuối danh sách. Được sử dụng để trả lại số lượng các phần tử trong một danh sách Được sử dụng để trở về true nếu danh sách có chứa một phần tử được chỉ định. Được sử dụng để xóa phần tử được chỉ định đầu tiên trong một danh sách. Được sử dụng để trả về phần tử đầu tiên trong một danh sách. Được sử dụng để trả lại phần tử cuối cùng trong một danh sách. Được sử dụng để trả về chỉ mục trong một danh sách với sự xuất hiện đầu tiên của phần tử được chỉ định, hoặc -1 nếu danh sách không chứa bất kỳ phần tử nào. 88

89 int lastindexof(object o) Được sử dụng để trả lại chỉ mục trong danh sách với sự xuất hiện cuối cùng của phần tử được chỉ định, hoặc -1 nếu danh sách không chứa bất kỳ phần tử nào Map (ánh xạ) Map định nghĩa các ánh xạ các từ khóa (keys) vào các giá trị. Lưu ý : Các khóa phải là duy nhất không cho phép lặp. Mỗi khóa được ánh xạ sang nhiều nhất 1 giá trị, được gọi là ánh xạ đơn Giao diện Map khai báo những hàm sau: - Object put (Object key,object value): Chèn vào một cặp <key,value> - Object get(object key): Đọc giá trị được xác định bới key nếu có ánh xạ,ngược lại trả về null nếu không có ánh xạ tương ứng với key - Object remove(object key): Loại bỏ ánh xạ xác định bởi key - boolean containskey(object key): Cho giá trị true nếu key được ánh xạ sang một giá trị nào đó, ngược lại là false. Lớp HashMap và HashTable Lớp này đặt giao diện Map và xây dựng trong java.lang. Chúng cho phép tạo ra các ánh xạ mới có thể rỗng hoặc có những kích thước tùy ý. Ví dụ 3.31: import java.util.*; public class CollectionsDemo public static void main(string[] args) Map m1 = new HashMap(); m1.put("zara", "8"); m1.put("mahnaz", "31"); m1.put("ayan", "12"); m1.put("daisy", "14"); System.out.println(); System.out.println(" Cac phan tu cua Map"); System.out.print("\t" + m1); Cho kết quả sau: Các phần tử của Map 89

90 Ví dụ 3.32: Chương trình sau minh họa các phương thức được hỗ trợ bởi lớp HashMap trong Java: import java.util.*; public class HashMapDemo public static void main(string args[]) // Tao mot hash map HashMap hm = new HashMap(); // Dat cac phan tu vao map hm.put("zara", new Double( )); hm.put("mahnaz", new Double(123.22)); hm.put("ayan", new Double( )); hm.put("daisy", new Double(99.22)); hm.put("qadir", new Double(-19.08)); // Lay mot tap hop cac entry Set set = hm.entryset(); // Lay mot iterator Iterator i = set.iterator(); // Hien thi cac phan tu while(i.hasnext()) Map.Entry me = (Map.Entry)i.next(); System.out.print(me.getKey() + ": "); System.out.println(me.getValue()); System.out.println(); // Gui 1000 vao trong tai khoan cua Zara double balance = ((Double)hm.get("Zara")).doubleValue(); hm.put("zara", new Double(balance )); System.out.println("Balance hien tai cua Zara la: " + hm.get("zara")); 90

91 Kết quả chạy chương trình: Collection frame work Collection Framework trong Java là một cấu trúc thống nhất để biểu diễn và thao tác với các tập hợp, cung cấp cho lập trình viên truy cập tới các cấu trúc dữ liệu đã đóng gói trước (Prepackage) cũng như các thuật toán để thao tác trên các cấu trúc dữ liệu này. Các Class và Interface của Collection Framework nằm trong gói java.util. Collection framework được thiết kế với mục đích như sau: Framework là hiệu năng cao. Sự triển khai cho các tập hợp cơ bản (các mảng động, linked list, tree và hashtable) được sử dụng với hiệu quả cao. Framework cho phép các kiểu tập hợp khác nhau làm việc theo một cách tương tự nhau với độ phân hóa cao. Kế thừa các tập hợp dễ dàng. Tất cả collections framework đều chứa: Giao diện lõi (Core interface) cho phép các tập hợp được xử lý độc lập với cài đặt. Tập các cài đặt của các giao diện: cung cấp cấu trúc dữ liệu cho các chương trình có thể sử dụng. Tập các thuật toán được sử dụng để thực hiện các phép toán trên các tuyển tập Phần giao diện lõi Một Collection framework định nghĩa trước vài loại interface. Các interface lõi là cơ sở phát triển, mở rộng thành các giao diện khác 91

92 Hình 3.1. Cấu trúc phân cấp theo quan hệ kế thừa của các giao diện lõi Các giao diện lõi của cấu trúc Collection được mô tả trong các bảng sau: interface Collection Set SortedSet List Map SortedMap Mô tả Interface cơ sở định nghĩa tất cả các phép toán cơ bản cho các lớp cần duy trì thực hiện và cài đặt chúng. Kế thừa từ Collection để cài đặt cấu trúc tập hợp, trong đó không có phần tử được lặp lại và không được săp xếp. Kế thừa từ Set để cài đặt cấu trúc tập hợp được sắp, trong đó không có phần tử được lặp và chúng không được săp xếp theo thứ tự. Kế thừa từ Collection để cài đặt cấu trúc danh sách, trong đó các phần tử được sắp xếp theo thứ tự và có lặp. Interface cơ sở định nghĩa các phép toán để các lớp sử dụng và cài đặt các ánh xạ từ khóa sang các giá trị. Kế thừa từ Map để cài đặt các ánh xạ khóa theo thứ tự. Trên cùng của cấu trúc phân cấp là Collection interface. Interface này chứa các phương thức size, isempty: cho biết thông tin về số phần tử tồn tại trong collection. Ví dụ 3.33: - contains: Dùng để kiểm tra một đối tượng nếu nó trong collection. - add, remove: Thêm và loại bỏ phần tử khỏi collection. - iterator: Duyệt các phần tử trong tập hợp. public interface Iterator<E> 92

93 boolean hasnext(); E next(); void remove(); //optional Ngoài ra để duyệt các phần tử còn sử dụng for-each Ví dụ 3.34: for (Object obj : collection) System.out.println(obj); Phần cài đặt Gói java.util cung cấp tập các lớp collection tiêu chuẩn có thể triển khai các collection interface lõi, để tạo ra những cấu trúc dữ liệu thường sử dụng như: Vector, ArrayList, HashSet, LinkedList, TreeSet, vvv Những lớp này và giao diện lõi được xây dựng theo cấu trúc phân cấp như trong hình Phần thuật toán Hình 3.2. Các giao diện lõi và các lớp cài đặt chúng Lớp Java.util.Collection (khác với giao diện Collection) cung cấp một số hàm thực hiện những thuật toán cho những phép toán khác nhau trên tập hợp, kể cả sắp xếp, tìm kiếm và dịch chuyển các phần tử. 93

94 3.10. Thread Thread là gì, Tạo Thread Thread là một dòng các điều khiển trong một tiến trình hay một ứng dụng. Với Java có thể xây dựng các chương trình đa luồng. Mỗi luồng được gán một công việc cụ thể, chúng được thực thi đồng thời với các luồng khác. Có hai cách tạo luồng Cách 1: Tạo ra một lớp kế thừa từ lớp Thread và ghi đè phương thức run của lớp Thread như sau: class MyThread extends Thread public void run() //Mã lệnh của luồng Cách 2: Tạo ra một lớp triển khai từ giao diện Runnable, ghi đè phương thức Run: class MyThread implements Runnable public void run() //Mã lệnh của luồng Các trạng thái của Thread Một thread đi qua các giai đoạn (trạng thái) khác nhau. Ví dụ, một thread được sinh ra, bắt đầu, chạy và sau đó hủy. Hình sau biểu diễn một vòng đời đầy đủ của một thread. Hình 3.3. Các trạng thái của Thread 94

95 Giải thích: New: Một thread mới bắt đầu vòng đời trong trạng thái new. Nó tồn tại trong trạng thái này tới khi chương trình bắt đầu thread. Runnable: Sau khi một thread mới sinh ra được bắt đầu, thread trở thành runnable. Một thread trong trạng thái runable được xem như đang thực hiện tác vụ của nó. Waiting: Thread rơi vào thái waiting (đợi) thread khác thực hiện một tác vụ nào đó. Một thread chuyển về trạng thái runnable chỉ khi thread khác ra hiệu cho thread đang đợi tiếp tục thực thi. Timed waiting: Một thread trong trạng thái runnable có thể đi vào trạng thái timed waiting trong một khoảng thời gian nào đó. Một thread trong trạng thái này chuyển về trạng thái runnable khi khoảng thời gian chờ kết thúc hoặc khi sự kiện nó đang đợi xuất hiện. Terminated: Một thread trong trạng thái runnable có thể đi vào trạng thái terminated khi hoàn thành tác vụ hoặc chấm dứt Các phương thức của lớp Thread public static Thread currentthread() - Đây là phương thức tĩnh, trả về một đối tượng của lớp Thread,có thể gọi ở bất kỳ đâu mà không cần tạo đối tượng của lớp Thread. - Cú pháp : Thread.currentThread() public long getid() - Phương thức này trả về một số nguyên là định danh của thread. Con số này do máy ảo Java (JVM) tạo ra. public final String getname() - Phương thức này trả về tên của thread. public final void setname(string name) - Thay đổi tên của thread. public Thread.State getstate() - Trả về trạng thái hiện tại của thread, là một hằng của lớp Thread. - Các giá trị có thể là : + NEW : thread chưa được khởi hoạt. + RUNNABLE : thread đang hoạt động trong JVM. + BLOCKED : thread chờ một monitor để unlock một đối tượng mà nó cần. + WAITING : thread chờ không giới hạn cho đến khi một thread khác đánh thức + ERMINATED : thread đã kết thúc công việc của nó. public static void yield() 95

96 - Đây là phương thức tĩnh, tạm ngưng hoạt động thread hiện thời để nhường cho một thread khác. public void start() - Phương thức này chính thức tạo ra một đối tượng thread. Và JVM sẽ tự động gọi phương thức run của đối tượng - Luồng được bắt đầu hoạt động sau khi phương thức này được gọi thành công. public String tostring() - Trả về thể hiện dạng String của thread này: bao gồm tên, độ ưu tiên và nhóm. public void run() Java gọi phương thức run ( ) để thực thi luồng thi hành. public void interrupt() Gián đoạn luồng thi hành public static boolean isinterrupt() Kiểm tra luồng có bị ngắt không isdaemon() Kiểm tra một luồng là chạy ngầm hay không. setdeamon(boolean on) Đánh dấu luồng là luồng chạy ngầm Quản lý Thread Java cung cấp các lớp điều khiển quản lý các chương trình đa luồng. Người dùng có thể phát triển một chương trình đa luồng bao gồm các giai đoạn: tạm ngưng, tiếp tục hoặc dừng hoàn toàn. Sử dụng các phương thức sau để quản lý và điều khiển luồng: public void stop() Có chức năng ngưng luồng thi hành, phương thức này không an toàn, nên gán null vào biến Thread để để dừng luồng, thay vì sử dụng phương thức stop() public void suspend() Có chức năng tạm ngừng luồng, trong Java 2, phương thức này ít được dùng vì không giải phóng tài nguyên, do vậy nguy cơ dẫn đến deadlock, thay vào đó dùng phương thức wait() public void resume() Tiếp tục vận hành luồng nếu nó đang bị ngưng. Nếu luồng đang thi hành thì phương thức này bị bỏ qua, thông thường phương thức này dùng kết hợp với phương thức suspend(), kể từ Java 2 phương thức này cùng với phương thức suspend() không được sử dụng, do vậy nên dùng phương thức notify () public static void sleep( long millis) Threadows InterruptedException Đặt luồng thi hành vào trạng thái ngủ, trong khoảng thời gian xác định bằng mili giây. chú ý sleep() là phương thức tĩnh. public void destroy() Hủy luồng đang thi hành Luồng chạy ngầm (deamon) Trong Java có hai loại luồng: - Luồng người sử dụng : Do người dùng tạo ra 96

97 - Luồng chạy ngầm (deamon): cung cấp các dịch vụ cho các luồng khác. Khi máy ảo Java thực hiện tiến trình thoát, lúc đó chỉ còn duy nhất luồng deamon còn sống. Máy ảo Java có ít nhất một luồng deamon là luồng garbage collection (thu lượm tài nguyên - dọn rác). Luồng dọn rác thực thi chỉ khi hệ thống không có tác vụ nào. Đây là một luồng có quyền ưu tiên thấp. Lớp luồng có hai phương thức để làm việc với luồng deamon: - public void setdaemon(boolean on) - public boolean isdaemon() Đa luồng và tương tranh Định nghĩa đa luồng Là khả năng thực hiện đồng thời nhiều phần khác nhau của một chương trình. Đa luồng quản lý nhiều tác vụ tương tranh, các tác vụ phân biệt dựa trên độ ưu tiên. Hệ thống xử lý đa luồng trong Java được xây dựng trên class Thread và interface Runnable trong packaged java.lang. Sử dụng đa luồng làm tăng quá trình xử lý của các hệ thống xử lý đơn và làm giảm thời gian rỗi của CPU Ví dụ 3.35: Tạo đa luồng, hiển thị tên của mỗi luồng đang chạy package test; public class MultipleThreads extends Thread String name; public void run() while(true) name = Thread.currentThread().getName(); System.out.println(name); try Thread.sleep(500); catch( InterruptedException e) break; public static void main(string args[]) MultipleThreads t1 = new MultipleThreads(); MultipleThreads t2 = new MultipleThreads(); 97

98 t1.setname( Thread2 ); t2.setname( Thread3 ); t1.start(); t2.start(); System.out.println( Number of threads running: + Thread. activecount()); Kết quả: Ví dụ 3.36: Tạo 2 luồng public class Thread1 extends Thread /* Biến lưu tên luồng */ String public void run() while(true) name = Thread.currentThread().getName(); for (int i=1;i<=5;i++) System.out.println("Thong diep tu luong"+name+" "+i); try Thread.sleep(500); catch( InterruptedException e) break; 98

99 public class Thread2 extends Thread String public void run() while(true) name = Thread.currentThread().getName(); for (int i=1;i<=5;i++) System.out.println("Thong diep tu luong"+name+" "+i); try Thread.sleep(500); catch( InterruptedException e) break; public class Test public static void main(string[] args) Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); t1.setname("thread1"); t2.setname("thread2"); t1.start(); t2.start(); System.out.println("Number of threads running:"+ Thread. activecount()); 99

100 Kết quả: số luồng đang chạy là 3, vì thêm một luồng chạy ngầm mặc định Sử dụng phương thức isalive() và join() Một luồng được xem là alive khi đang chạy. Cú pháp: public final boolean isalive() Ví dụ 3.37: public static void main(string [] args) ThreadDemo Obj = new ThreadDemo(); Thread t = new Thread(Obj); System.out.println( The thread is alive : + t.isalive()); public final void join(long millis) throws InterruptedException public final void join(long millis, int nanos) throws InterruptedException public final void join() throws InterruptedException Đây là phương thức được gọi bởi một thread khác. - Phương thức này làm cho thread gọi phải ngưng hoạt động và chờ trong một khoảng thời gian millis millisecond hoặc chờ trong khoảng thời gian millis millisecond và nanos nanosseconds, hoặc chờ cho đến khi thread này kết thúc thì mới tiếp tục hoạt động. - Phương thức này có thể phát sinh một ngoại lệ InterruptedException nếu nó bị một thread khác làm ngưng hoạt động. Ví dụ 3.38: try System.out.println( I am in the main and waiting for the thread to 100

101 finish ); // objth is a Thread object objth.join(); catch( InterruptedException e) System.out.println( Main thread is interrupted ); Điều kiện tương tranh và cách khắc phục Điều kiện tương tranh - Khi tạo ra nhiều thread hoạt động song song độc lập (tức là không có dữ liệu chia sẻ) thì không xảy ra vấn đề gì. - Tuy nhiên khi hai hay nhiều thread cùng sử dụng chung một biến hay một phương thức thì : vì các thread trong một Process có thể được thực thi đồng thời (hệ thống nhiều CPU) hoặc được luân phiên gọi trong một thời gian rất ngắn (hệ thống đơn CPU) việc sử dụng dữ liệu chia sẻ là gần như đồng thời. Điều này dẫn đến vấn về Race condition. - Race condition (điều kiện tranh chấp) : xảy ra khi có hai hay nhiều thread cùng chia sẻ dữ liệu, khi chúng cùng đọc, cùng ghi dữ liệu chia sẻ đó đồng thời. Kết quả nhận được sẽ phụ thuộc vào tác động lên biến chia sẻ là gì và lúc nào. Và người lập trình không thể biết được chính xác điều gì xảy ra Khắc phục Trong Java không có biến toàn cục, chỉ có thuộc tính của đối tượng, tất cả các thao tác có thể dẫn đến hỏng hóc đều thực hiện qua phương thức, do vậy Java cung cấp từ khoá synchronized sử dụng cho phương thức hay đoạn mã cần bảo vệ, từ khoá này được thêm vào định nghĩa của phương thức báo cho Java biết đây là một phương thức đồng bộ, mỗi đối tượng sẽ có một bộ khóa quản lý, bộ khóa quản lý này chỉ cho 1 phương thức synchronized của đối tượng đó chạy tại một thời điểm Khai báo như sau : public synchronized void protectedmethod(object obj) - Method protectedmethod sẽ được đảm bảo thực thi tuần tự. - Nghĩa là khi có 2 thread A và B gọi method protectedmethod thì nếu method A gọi trước thì chỉ khi nào method protectedmethod thực thi xong thì thread B mới được gọi. Và trong thời gian đó thì thread B phải chờ. Mấu chốt của sự đồng bộ hóa là khái niệm monitor (giám sát), hay còn gọi semaphore (cờ hiệu). Một monitor là một đối tượng có khóa độc quyền. Chỉ một luồng có thể có monitor tại mỗi thời điểm. Tất cả các luồng khác cố gắng thâm nhập vào 101

102 monitor sẽ bị trì hoãn, cho đến khi luồng đầu tiên thoát khỏi monitor. Các luồng khác được báo chờ đợi monitor. Một luồng có thể monitor một đối tượng nhiều lần Cách sử dụng phương thức wait() và notify() Java cung cấp cơ chế giao tiếp liên-quá trình bằng cách sử dụng phương thức wait(), notify() và notifyall(). Các phương thức wait(), notify() and notifyall() chỉ được gọi từ bên trong một phương thức được đồng bộ hóa (synchronized method). public final void wait() throws InterruptException Đặt luồng vào trạng thái chờ (sleeping) một luồng khác, cho đến khi có một luồng khác thông báo notify() thì nó lại tiếp tục, đây là phương thức của lớp cơ sở Object public final void notify () Đánh thức luồng đầu tiên đang ở trạng thái sleeping public final void notifyall() đánh thức tất cả các luồng đang ở trạng thái sleeping. Khi tất cả các luồng thoát khỏi trạng thái sleeping, luồng có độ ưu tiên cao nhất sẽ chạy đầu tiên. Ví dụ 3.39: package thread.relationship; public class Customer private int balance = 1000; public Customer() System.out.println("Tai khoan cua ban la " + balance); private synchronized void withdraw(int amount) System.out.println("Giao dich rut tien dang thuc hien " + amount + " "); if(balance < amount) System.out.println("Cannot withdraw!"); try wait(); catch (InterruptedException ie) System.out.println(ie.toString()); balance -= amount; System.out.println("Rut tien thanh cong. Tai khoan hien tai cua ban la " + 102

103 balance); private synchronized void deposit(int amount) System.out.println("Giao dich nap tien " + amount + ""); balance += amount; System.out.println("Nap tien thanh cong. Tai khoan hien tai cua ban la " + balance); notify(); public static void main(string[] args) Customer c = new Customer(); Thread t1 = new Thread() public void run() c.withdraw(2000); ; t1.start(); Thread t2 = new Thread() ; public void run() t2.start(); c.deposit(3000); Định nghĩa deadlock và khắc phục Deadlock mô tả tình huống mà hai hoặc nhiều luồng bị chặn mãi mãi, chờ đợi luồng khác giải phóng tài nguyên. Đôi khi, xảy ra khi hai luồng bị khóa không được dùng tài nguyên tương ứng của chúng, và phải đợi mở khóa để thực hiện trao đổi tài nguyên. Trong tình huống đó, trạng thái chờ sẽ xảy ra mãi mãi vì cả hai đều đang trong trạng thái lẫn lộn: luồng nào sẽ rời khỏi khóa và luồng nào sẽ đi vào vùng tài nguyên. Đây là tình trạng tắc nghẽn (deadlock) luồng trong Java. Tình trạng bế tắc khiến việc thực thi chương trình bị dừng lại. 103

104 Ví dụ 3.40: Minh hoạ deadlock public class DeadlockDemo implements Runnable public static void main(string args[]) DeadlockDemo objdead1 = new DeadlockDemo(); DeadlockDemo objdead2 = new DeadlockDemo(); Thread objth1 = new Thread (objdead1); Thread objth2 = new Thread (objdead2); objdead1.grabit = objdead2; objdead2.grabit = objdead1; objth1.start(); objth2.start(); System.out.println( Started ); try objth1.join(); objth2.join(); catch( InterruptedException e) System.out.println( error occurred ); System.exit(0 DeadlockDemo grabit; public synchronized void run() try Thread.sleep(500); catch( InterruptedException e) System.out.println( error occurred ); grabit.syncit(); public synchronized void syncit() try Thread.sleep(500); System.out.println( Sync ); 104

105 catch(interruptedexception e) System.out.println( error occurred ); System.out.println( In the syncit() method ); Giải thích: Chương trình tạo hai luồng con, mỗi luồng sử dụng đồng bộ phương thức run(). Khi luồng 1 được đánh thức, nó gọi phương thức syncit( ) của đối tượng objectdead1. Do luồng objth2 sử dụng điều khiển của đối tượng objectdead2, luồng 1 rơi vào trạng thái chờ điều khiển. Khi objth2 được đánh thức, nó cố gắng gọi phương thức syncit() của đối tượng objdead2. Tại thời điểm này, objth2 cũng buộc phải chờ do objth1 đang sở hữu điều khiển của đối tượng objdead1. Do cả hai luồng đều đang chờ nhau, nên sẽ không thức dậy. Đây là tình trạng bế tắc. Chương trình bị chặn và không tiếp tục. Khắc phục tình trạng deadlock. - Tránh yêu cầu nhiều hơn 1 khóa tại một thời điểm. - Trong chương trình có nhiều khóa được sắp xếp theo thứ tự xác định, nhất quán. 105

106 CÂU HỎI ÔN TẬP, THẢO LUẬN Câu 1. Tính kế thừa (Inheritance) là gì? Câu 2. Định nghĩa tính đa hình (Polymorphism)? Câu 3. Khi nào phương thức abstract được sử dụng? Câu 4. Sự khác nhau giữa nạp chồng và ghi đè (Overloading vs Overriding)? Câu 5. Sự khác nhau giữa hai lớp StringBuffer và StringBuilder? Câu 6. Giải thích dòng code sau bằng ngôn ngữ Java: public static void main (String args[ ]) Câu 7. Thread có thể được tạo bằng hai cách nào? Câu 8. Hãy cho biết kết quả khi dịch và chạy chương trình sau : public class TryCatchDemo static public void main (String args[]) int k = 0; try int i=5/k; catch(arithmeticexception e) System.err.println( Loi chia cho 0! ); catch(runtimexception e) System.err.println( Loi khi thuc hien! ); catch(exception e) System.err.println( Loi vao/ra! ); finally System.out.println( Cong viec can thuc hien! ); System.out.println ( Cuoi cung ); BÀI TẬP ỨNG DỤNG Bài 1. Thiết kế và cài đặt lớp XeOTo, chứa các thông tin chi tiết sau: - Tên xe. - Hãng sản xuất - Năm sản xuất Viết phương thức khởi dựng tương ứng. 106

107 Bài 2. Thiết kế và cài đặt cho lớp XeTai thừa kế từ lớp XeOTo, lưu các thông tin của xe tải: - Mã xe - Biển số xe - Trọng tải Trong lớp XeTai, cài đặt các phương thức sau: - Phương thức khởi dựng. - Phương thức nhập thông tin cho xe tải. - Phương thức hiển thị thông tin xe tải Bài 3. Thiết kế và cài đặt ứng dụng QuanLyXeTai, với các chức năng: 1. Nhập thêm một xe tải mới. 2. Tìm kiếm xe tải khi biết biển số xe 3. In danh sách xe tải theo thứ tự trọng tải tăng dần 4. Kết thúc Bài 4. Hãy xây dựng lớp SinhVien có các thuộc tính riêng (private): -Số báo danh -Họ và tên sinh viên -Địa chỉ -Môn Học -Điểm thi học kì I,II a, Viết các hàm truy nhập tới các thành phần dữ liệu và các toán tử tạo lập cho lớp Sinhvien b,viết chương trình chính để tạo danh sách Sinhvien và hiển thị thực đơn: 1.Nhập vào thông tin về sinh viên 2.Xem thông tin sinh viên 3.Tìm sinh viên theo điểm 4.Kết thúc chương trình c,viết các hàm thành phần của 1 lớp để thực hiện các nhiệm vụ trên Bài 5. Một công ty được giao nhiệm vụ quản lý các phương tiện giao thông gồm các loại xe ôtô,xe máy,xe tải. + Mỗi phương tiện giao thông cần quản lý : mô hình,năm sản suất,giá bán và màu + Các ôtô cần quản lý : chỗ ngồi,kiểu động cơ + Xe máy : công suất, + Xe tải : Trọng tải a, Xây dựng các lớp XeTai, XeMay và oto kế thừa từ lớp PhuongTienGT 107

108 b, Xây dựng các hàm để truy nhập,hiển thị,và kiểm tra các thuộc tính của các lớp c, Phát triển lớp Kiemke để quản lý tất cả các phương tiện(xe máy,ôtô xe tải), trong đó có hàm chính thực hiện theo thực đơn: 1.Nhập đăng ký phương tiện, 2.Tìm phương tiện theo mô hình 3.Tìm phương tiện theo màu 4.Kết thúc. d,viết các hàm để thực hiện nhiệm vụ trên. Bài 6. Viết chương trình để quản lý dịch vụ ATM với các thuộc tích đều có kiểu số nguyên gồm: accid (số PIN của tài khoản), balance (số dư hiện có của tài khoản), amount (tổng số tiền rút ra từ tài khoản) với các yêu cầu: - Phương thức void input() thực hiện: nhập giá trị đầu vào của accid và balance. - Phương thức void withdraw() thực hiện: kiểm soát giá trị của amount xem amount có nhỏ hơn hoặc bằng giá trị của balance không? Biết, balance = balance - amount. - Phương thức void displaybalance() thực hiện: đưa ra giá trị của balance sau khi đã thực hiện rút tiền khỏi tài khoản. - Trong phương thức của main thực hiện: tạo các đối tượng của ATM và gọi các phương thức trên. Bài 7. Viết chương trình quản lý điểm môn Tin học của sinh viên với các yêu cầu sau: a) Tên sinh viên và điểm của sinh viên đó được nhập sẵn trong chương trình. b) Hiển thị danh sách sinh viên và điểm tương ứng của sinh viên đó. c) Xóa một sinh viên khỏi danh sách. d) Hiển thị danh sách sinh viên sau khi thực hiện xóa. Bài 8. Viết chương trình quản lý thông tin về lương cho một Công ty với các yêu cầu sau: a. Các thông tin chung gồm: - Họ và tên - Tuổi - Năm vào Công ty - Phụ cấp. Trong đó phục cấp được tính như sau: Phụ cấp = * số năm công tác tại Công ty b. Các thông tin riêng: * Các nhân viên làm việc tại văn phòng: - Nhập hệ số lương (ví dụ như 2.34, 2.67, 3.00, 3.33,...) 108

109 - Nhập số ngày nghỉ trong tháng, - Lương Trong đó lương được tính như sau: Lương = hệ số lương * phụ cấp - lương nghỉ việc Lương nghỉ việc = số ngày nghỉ * (hệ số lương * )/20 * Các nhân viên sản xuất - Nhập số sản phẩm đã nộp trong tháng - Lương Trong đó lương được tính như sau: Lương = số sản phẩm * phụ cấp c. Đưa ra màn hình kết quả tổng lương của các nhân viên trong Công ty sao cho kết quả hiển thị là một số thực có phần thập phân không quá 2 chữ số. Ví dụ sau khi nhập: * Nhân viên văn phòng: - Họ tên: Nguyen Tuan Anh - Tuổi: 25 - Năm vào Công ty: Số ngày nghỉ trong tháng: 0 * Nhân viên sản xuất: - Họ tên: Vu Minh Tri - Tuổi: 25 - Năm vào Công ty: Số sản phẩm đã nộp trong tháng: 100 * Kết quả: Lương của hai nhân viên đó là: Bài 9. Một thư viện cần quản lý các tài liệu bao gồm Sách, Tạp chí, Báo Mỗi tài liệu có các thuộc tính: Mã tài liệu, Tên nhà xuất bản, Số bản phát hành. Các loại sách cần quản lý: Tên tác giả, Tên sách, số trang Các tạp chí cần quản lý: Số phát hành, tháng phát hành Các báo cần quản lý: ngày phát hành. (Date) 1.Xây dựng các lớp để quản lý các loại tài liệu trên sao cho việc sử dụng lại được nhiều nhất. 2. Xây dựng lớp QuanLySach cài đặt các phương thức thực hiện các công việc sau: a. Nhập thông tin về các tài liệu b. Hiển thị thông tin về các tài liệu c. Tìm kiếm tài liệu theo loại d. Tìm kiếm tài liệu theo tên tác giả 109

110 Bài 10. * Tạo interface Person nằm trong gói person.info nó có 2 phương thức sau: - Public void input(); - Public void display(); * Tạo một lớp Student nằm trong gói student.info thực thi giao diện trên và bổ sung thêm thuộc tính: - String name; - Int Age; - String nativeplace; - String id. Cài đặt các constructor. Override tất cả các phương thức có trong lớp Person. * Tạo một lớp ManagerStudent nằm trong gói manager.info kế thừa từ lớp Student và bổ sung thêm: - int n; (Chỉ ra số sv có trong ds) - Mảng n phần tử kiểu đối tượng sinh viên, để chứa các thông tin về mỗi sinh viên. 110

111 Mục đích CHƯƠNG 4. LẬP TRÌNH GIAO DIỆN VỚI JAVA SWING Chương này trình bày chi tiết phương pháp lập trình giao diện bằng SWING trong Java, cụ thể như: Giới thiệu Swing và mô hình MVC Quản lý Layout Các thành phần lựa chọn: Checkboxes, Radio Button, ComboBoxes Xây dựng menu và các Dialog Boxes Giới thiệu Swing và mô hình MVC Java Swing là một phần của Java Foundation Classes (JFC) được sử dụng để tạo các ứng dụng Window-Based. Được xây dựng trên cùng của AWT (Abstract Windowing Toolkit) API và viết hoàn toàn bằng Java. Không giống AWT, Java Swing cung cấp các thành phần (Component) gọn nhẹ và độc lập nền tảng. Javax.swing. Package cung cấp các lớp cho Java Swing như JButton, JTextField, JTextArea, JRadioButton, JCheckbox, JMenu, JcolorChooser, Java Foundation Classes (JFC) là một tập hợp các thành phần GUI giúp đơn giản hóa việc phát triển các ứng dụng Desktop. Hình dưới đây cho thấy cái nhìn nhìn tổng quan về cấu trúc theo thứ bậc của các lớp trong Java Swing. Hình 4.1. Các lớp trong java Swing 111

112 Kiến trúc MVC Swing sử dụng một kiểu riêng đơn giản của thiết kế MVC gọi là Model - Delegate. Thiết kế này kết hợp View và Controller thành yếu tố đơn nhất là UI delegate. Kết hợp khả năng đồ họa và xử lý sự kiện một cách dễ dàng trong Java. View Model Controller Logic Program Hình 4.2. Mô hình MVC Mỗi thành phần Swing chứa một Model và một UI delegate. Model chịu trách nhiệm nắm giữ thông tin về trạng thái của thành phần. UI delegate chịu trách nhiệm nắm giữ thông tin về việc xuất hiện các thành phần (component) trên màn hình. Thêm vào đó, UI delegate (tập hợp với AWT) tương tác lại với những sự kiện riêng biệt để truyền lại thông qua component Lợi ích của sử dụng Swing so với AWT The GUI STT Java AWT Java Swing 1 Các thành phần của AWT là phụ thuộc vào nền tảng Các thành phần của Swing là độc lập nền tảng 2 Các thành phần của AWT: nặng Các thành phần của Swing gọn nhẹ hơn 3 4 AWT không hỗ trợ pluggable Look & Feel AWT cung cấp ít thành phần hơn Swing Swing hỗ trơ puggable Look & Feel Swing cung cấp các thành phần mạnh mẽ hơn như là tables, lists, scrollpanes, color chooser, tabbedpane, 5 AWT không theo kiến trúc MVC Swing hỗ trợ kiến trúc MVC 112

113 Mô tả container trong Swing Container là thành phần chủ chốt của SWING GUI. Một Container cung cấp một không gian, là nơi đặt một thành phần. Một Container chính là một Component có thêm khả năng để thêm các thành phần khác vào. Khi xem xét về Container, cần chú ý các điểm sau: Các lớp con của Container được gọi là Container. Một số ví dụ về các lớp con của Container là JPanel, JFrame và JWindow. Container chỉ có thể thêm Component vào chính nó Một layout mặc định có mặt trong mỗi container. Layout này có thể bị ghi đè bởi sử dụng phương thức setlayout(). Bảng bên dưới sẽ trình bày các loại container. Loại Tên Miêu tả Top level General Purpose Special Purpose JApplet, JDialog, JFrame JPanel,JScollPane, JTabbedPane, JToolBar JInternalFrame, JLayeredPane, JRootPane Những thành phần này xuất hiện trong bất kỳ ứng dụng Swing và được sử dụng để chứa các thành phần khác Container phổ biến Container đặc biệt 4.2. Quản lý Layout Layout là sự bố trí sắp xếp các thành phần bên trong Container theo cách thức nhất định. Để xử lý một số lượng thành phần lớn với kích cỡ, hình dạng khác nhau và bố trí theo cách mong muốn thì việc sử dụng các LayoutManager là cần thiết BorderLayout Lớp BorderLayout sắp xếp các thành phần phù hợp với 5 miền: đông, tây, nam, bắc và trung tâm. Nó là layout mặc định của Frame hoặc Window. Mỗi khu vực (miền) chỉ có thể chứa một thành phần và mỗi thành phần trong mỗi khu vực được nhận diện bởi các hằng tương ứng: public static final int NORTH public static final int SOUTH public static final int EAST public static final int WEST public static final int CENTER Cú pháp để khai báo lớp Java.awt.BorderLayout là: public class BorderLayout 113

114 extends Object implements LayoutManager2, Serializable Các hàm tạo của lớp BorderLayout: BorderLayout(): Xây dựng một Border Layout mới, ở đó giữa các thành phần là không có khoảng cách. BorderLayout(int hgap, int vgap): Xây dụng một Border Layout với các khoảng cách gap theo chiều dọc và ngang đã xác định giữa các thành phần. Các phương thức của lớp BorderLayout: STT Phương thức và miêu tả 1 void addlayoutcomponent (Component comp, Object constraints) Thêm thành phần comp vào layout, sử dụng đối tượng Constraint cho trước 2 void addlayoutcomponent (String name, Component comp) Thêm thành phần comp vào layout, liên kết nó với chuỗi xác định bởi tên. 3 int gethgap() Trả về khoảng cách theo chiều ngang giữa các thành phần 4 float getlayoutalignmentx(container parent) Trả về căn chỉnh theo trục x 5 float getlayoutalignmenty(container parent) Trả về căn chỉnh theo trục y 6 int getvgap() Trả về khoảng cách theo chiều dọc giữa các thành phần 7 void invalidatelayout(container target) Vô hiệu hóa layout, chỉ rằng nếu Layout Manager đã lưu thông tin thì nên loại bỏ 8 void layoutcontainer(container target) Bố trí tham số container sử dụng Border Layout này 9 Dimension maximumlayoutsize(container target) Trả về kích cỡ các chiều tối đa cho layout, cung cấp các thành phần trong target đã cho. 10 Dimension minimumlayoutsize(container target) Xác định kích cỡ tối thiểu cho target sử dụng layout manager 11 void removelayoutcomponent(component comp) Xóa thành phần comp đã cho từ Border Layout 114

115 12 String tostring() Trả về một biểu diễn chuỗi Border Layout này GridLayout GridLayout sắp xếp các thành phần trong một lưới hình chữ nhật. Một thành phần được hiển thị trong một hình chữ nhật. Cú pháp khai báo cho lớp java.awt.gridlayout là: public class GridLayout extends Object implements LayoutManager, Serializable Các hàm tạo của lớp GirdLayout Manager: GridLayout(): Tạo một gridlayout mặc định là một cột GridLayout(int rows, int columns): Tạo một grid layout với số hàng và cột đã cho, và không có khoảng cách giữa các thành phần. GridLayout(int rows, int columns, int hgap, int vgap): Tạo một grid layout với các hàng và cột đã cho cùng với khoảng cách theo chiều dọc và ngang xác định trước. Các phương thức của lớp GirdLayout Manager STT Phương thức và mô tả void layoutcontainer(container parent) Bố trí container đã cho bởi sử dụng layout này. Dimension minimumlayoutsize(container parent) Xác định kích cỡ tối thiểu của tham số container Dimension preferredlayoutsize(container parent) Xác định kích cỡ được ưu tiên của tham số container. void removelayoutcomponent(component comp) Xóa thành phần đã cho từ layout. void setcolumns(int cols) Thiết lập số cột trong layout void sethgap(int hgap) Thiết lập khoảng cách theo chiều ngang giữa các thành phần void setrows(int rows) Thiết lập số hàng trong layout void setvgap(int vgap) Thiết lập khoảng cách theo chiều dọc giữa các thành phần 115

116 GirdBagLayout GridBagLayout là một lớp quản lý layout linh động. Đối tượng của GridBagLayout căn chỉnh các thành phần theo chiều dọc, ngang hoặc theo baseline mà không yêu cầu các thành phần phải cùng kích cỡ. Cú pháp khai báo cho lớp java.awt.gridbaglayout là: public class GridBagLayout extends Object implements LayoutManager2, Serializable GroupLayout GroupLayout nhóm các thành phần theo cấu trúc thứ bậc để đặt chúng trong một Container. Cú pháp khai báo cho lớp javax.swing.grouplayout là: public class GroupLayout extends Object implements LayoutManager2 Hàm tạo GroupLayout(Container host) tạo một GroupLayout cho container đã cho Text Input TextFields JTextField là một đối tượng cho phép người dùng nhập một dòng văn bản. Thường dùng để nhập dữ liệu với các thông tin ngắn. Các hàm khởi tạo JTextField JTextField(): Tạo mới 1 JTextField JTextField(Document doc, String text, int columns): Tạo 1 JTextField sử dụng mô hình lưu trữ văn bản với đoạn text và số cột (coloumns) JTextField(int columns): Tạo JTextField trống với độ rộng là columns JTextField(String text): Tạo JTextField với text cho trước JTextField(String text, int columns): Tạo JTextField với text và độ rộng cho trước. 116

117 Một số phương thức thường dùng gettext(): Lấy dữ liệu (chuỗi) từ JTextField settext(): Đặt dữ liệu cho JTextField requestfocus(): Đặt con trỏ nhập liệu đến JTextField được chọn seteditable(boolean edit): Cho phép nhập dữ liệu cho JTextfield hay không Label Lớp (class) JLabel trong Java Swing được sử dụng để hiển thị một dòng văn bản, người dùng chỉ có thể đọc mà không thể thao tác trực tiếp. Các hàm khởi tạo Jlabel () Constructor Jlabel (String text) Jlabel (Icon icon) Jlabel (String text, int horizontalalignment) Jlabel (Icon i, int horizontalalignment) Jlabel (String text, Icon i, int horizontalalignment) Mô tả Tạo một nhãn không có hình ảnh và tiêu đề là rỗng. Tạo một nhãn (JLabel) với văn bản (text) được chỉ định. Tạo một nhãn (JLabel) với biểu tượng (icon) được chỉ định. Tạo một nhãn (JLabel) với văn bản (text) và horizontalalignment được chỉ định. Tạo ra một nhãn (JLabel) với biểu tượng (icon) và horizontalalignment được chỉ định. Tạo ra một nhãn (JLabel) với văn bản (text) và biểu tượng (icon) và horizontalalignment được chỉ định. Các phương thức của lớp Jlabel STT Tên phương thức 1 void settext(string text) Thiết lập (set) văn bản (text) cụ thể cho JLabel. 117

118 2 String gettext() Lấy văn bản (text) của JLabel. 3 void sethorizontalalignment(int alignment) Đặt alignment cho label trên trục x. 4 int gethorizontalalignment() Lấy alignment của label trên trục x. 5 Icon geticon() Phương thức geticon được sử dụng để lấy biểu tượng (icon) của JLabel Password Fields Lớp JPasswordField trong Java Swing được kế thừa lớp (class) JTextField, là một thành phần văn bản chuyên để nhập mật khẩu. Các hàm tạo của lớp JPasswordField: Constructor JPasswordField() JPasswordField(int columns) JPasswordField(String text) JPasswordField(String text, int columns) Mô tả Tạo ra JPasswordField với text là null. Tạo ra JPasswordField với text là null và columns được chỉ định. Tạo ra JPasswordField với text được chỉ định. Tạo ra JPasswordField với text và columns được chỉ định. Các phương thức của lớp JPasswordField được kế thừa từ lớp JTextField TextAreas Lớp JTextArea trong Java Swing kế thừa lớp JTextComponent là một vùng hiển thị văn bản nhiều dòng, cho phép người dùng thao tác và chỉnh sửa. 118

119 Hàm tạo của lớp JTextArea JTextArea() Constructor JTextArea(String text) JTextArea(int row, int column) JTextArea(String text, int row, int column) Mô tả Tạo một JTextArea với văn bản (text) hiển thị rỗng. Tạomột JTextArea với văn bản (text) được chỉ định. Tạo một JTextArea với hàng và cột được chỉ định. Tạo một JTextArea văn bản (text) hiển thị theo hàng và cột được chỉ định. STT Các phương thức của lớp JTextArea Tên phương thức 1 void setrows (int rows) Thiết lập số lượng hàng được chỉ định trong JTextArea. 2 void setcolumns (int cols) Thiết lập số lượng cột được chỉ định trong JTextArea. 3 void setfont (Font f) Thiết lập font được chỉ định trong JTextArea. 4 void insert (String s, int position) Chèn văn bản vào vị trí được chỉ định trong JTextArea. 5 void append (String s) Nối văn bản được chỉ định trong JTextArea Scroll Panes Trong Swing, mặc định TextArea không có thanh cuộn. Nếu muốn có thanh cuộn phải đặt Textarea vào trong một scroll pane. Cú pháp khai báo: TextArea textarea = new TextArea(); 119

120 JScrollPane scrollpane = new JScrollPane(textArea); ScollPane sẽ quản lý giao diện của TextArea. Thanh cuộn sẽ tự động xuất hiện nếu có nhiều văn bản hơn vùng mà TextArea có thể hiển thị và nó sẽ biến mất nếu văn bản bị xóa đi vừa với vùng hiển thị. Thanh cuộn được tự động xử lý bởi ScrollPane, chương trình không phải xử lý sự kiện xuất hiện thanh cuộn. Để thêm thanh cuộn vào một component chỉ cần thêm vào trong một ScrollPane Các thành phần lựa chọn Checkboxes Lớp JCheckBox trong Java Swing kế thừa từ class JToggleButton, được sử dụng để chọn đúng hoặc sai, chọn hay không chọn về một vấn đề nào đó. Hàm tạo của lớp Jcheckbox JcheckBox () Constructor JchechBox (String s) JcheckBox (String text, boolean selected) JcheckBox (Action a) Mô tả Tạo một JCheckBox với trạng thái là không được chọn, không có văn bản (text) và biểu tượng hiển thị. Tạo một JCheckBox với trạng thái là không được chọn, với văn bản (text) hiển thị được chỉ định. Tạo một JCheckBox với trạng thái (selected) và văn bản (text) hiển thị được chỉ định. Tạo một JcheckBox các thuộc tính được lấy ra từ Action Radio Button Lớp (class) JRadioButton trong Java Swing kế thừa từ class JToggleButton được sử dụng để tạo ra một nút radio. JRadioButton được sử dụng trong trường hợp chọn một trong nhiều tùy chọn 120

121 Dưới đây là các hàm tạo cửa class JRadioButton. JRadioButton() Constructor JRadioButton(String s) JRadioButton(String s, boolean selected) Mô tả Tạo ra một JRadioButton với văn bản (text) hiển thị là rỗng. Tạo ra một JRadioButton với văn bản (text) được chỉ định. Tạo ra một JRadioButton với văn bản (text) và trạng thái đã được chỉ định. STT Một số phương thức thường được sử dụng trong JRadioButton. Tên phương thức 1 void settext(string s) Phương thức settext được sử dụng thiết lập văn bản (text) cho JRadioButton. 2 String gettext() Phương thức gettext được sử dụng để lấy văn bản (text) từ JRadioButton. 3 void setenabled(boolean b) Phương thức setenabled được sử dụng để kích hoạt hoặc vô hiệu JRadioButton. 4 void seticon(icon b) Phương thức seticon được sử dụng để thiết lập biểu tượng hình ảnh (Icon) cho JRadioButton. 5 Icon geticon() Phương thức geticon đươc sử dụng để lấy biểu tượng (Icon) từ JRadioButton ComboBoxes Lớp JComboBox trong Java Swing kế thừa từ class JComponent sử dụng popup menu để đưa ra những lựa chọn cho người dùng, và sự lựa chọn đó (của người dùng) sẽ được hiển thị ở đầu menu. 121

122 Các hàm tạo của lớp JComboBox: Constructor JComboBox() JComboBox(Object[] items) JComboBox(Vector<?> items) Mô tả Tạo ra một JComboBox với constructor rỗng. Tạo một JComboBox có chứa các phần tử trong mảng được chỉ định. Tạo một JComboBox có chứa các phần tử trong Vector được chỉ định. STT Một số phương thức thường được sử dụng trong JComboBox. Tên phương thức 1 void additem(object anobject) Phương thức additem được sử dụng để thêm một mục (Item) vào danh sách mục (Item). 2 void removeitem(object anobject) Phương thức removeitem được sử dụng để xóa một mục (Item) từ danh sách mục (Item). 3 void removeallitems() Phương thức removeallitems được sử dụng để xóa tất cả (Item) từ danh sách. 4 void seteditable(boolean b) Phương thức seteditable được sử dụng để xác định xem JComboBox có được chình sửa hay không. 5 void addactionlistener(actionlistener a) Phương thức addactionlistener được sử dụng để thêm hành động (ActionListener) lắng nghe cho đối tượng JComboBox. 6 void additemlistener(itemlistener i) Phương thức addactionlistener được sử dụng để thêm ItemListener cho đối tượng JComboBox. 122

123 Slider Lớp JSlider được sử dụng để tạo con trượt slider. Sử dụng JSlider, người dùng có thể lựa chọn một giá trị từ một dãy cụ thể. Cú pháp khai báo của lớp JSlider như sau: public class JSlider extends JComponent implements SwingConstants, Accessible Các hàm tạo phổ biến của lớp JSlider: Hình 4.3. JSlider JSlider(): tạo một slider với giá trị khởi tạo là 50 và dãy giá trị là từ 0 tới 100. JSlider(int orientation): tạo một slider với orientation đã cho được thiết lập bởi hoặc JSlider.HORIZONTAL hoặc JSlider.VERTICAL với dãy từ 0 tới 100 và giá trị khởi tạo là 50. JSlider(int min, int max): tạo một thanh slider ngang sử dụng giá trị min và max đã cho. JSlider(int min, int max, int value): tạo một thanh slider ngang sử dụng giá trị min, max và value đã cho. JSlider(int orientation, int min, int max, int value): tạo một slider bởi sử dụng orientation, min, max và value đã cho. Các phương thức được sử dụng phổ biến của lớp JSlider: STT Phương thức và mô tả 1 public void setminortickspacing(int n) Thiết lập khoảng cách tick nhỏ nhất cho slider. 2 public void setmajortickspacing(int n) Thiết lập khoảng cách tick lớn nhất cho slider. 3 public void setpaintticks(boolean b) 123

124 Xác định xem tick mark có được sơn màu hay không. 4 public void setpaintlabels(boolean b) Xác định xem label có được sơn màu hay không. 5 public void setpainttracks(boolean b) 4.5. Menu Xác định xem track có được sơn màu hay không Xây dựng menu Mỗi cửa sổ window có một thanh trình đơn (menu bar) được liên kết với nó. Thanh trình đơn này gồm các lựa chọn có sẵn tới người dùng cuối. Các điều khiển Menu và MenuItem là lớp con của lớp MenuComponent. Lớp JMenuBar cung cấp một trình triển khai của một thanh trình đơn (menu bar). Dưới đây là cú pháp khai báo cho lớp javax.swing.jmenubar: public class JMenuBar extends JComponent implements Accessible, MenuElement Hàm tạo của lớp JMenuBar: JMenuBar(): Tạo một thanh trình đơn mới Icons và Menu Items Lớp JMenu biểu diễn thành phần pull-down menu được triển khai từ một thanh trình đơn. Cú pháp khai báo cho lớp javax.swing.jmenu là: public class JMenu extends JMenuItem implements Accessible, MenuElement Các hàm tạo của lớp JMenu: STT Hàm tạo và Mô tả 1 JMenu() Xây dựng một JMenu mới không có text. 2 JMenu(Action a) Xây dựng một menu có các thuộc tính được nhận từ Action đã cho. 3 JMenu(String s) Xây dựng một JMenu mới với chuỗi s đã cho (như là text của nó). 4 JMenu(String s, boolean b) Xây dựng một JMenu mới với chuỗi s đã cho (như là text của nó) và một giá trị boolean để xác định có hay không một tear-off menu. 124

125 Lớp JMenuItem biểu diễn item thực sự trong một menu. Tất cả item trong một menu nên kế thừa từ lớp JMenuItem, hoặc một trong các lớp con của nó. Cú pháp khai báo cho lớp javax.swing.jmenuitem là: public class JMenuItem extends AbstractButton implements Accessible, MenuElement Các hàm tạo của lớp JMenu: STT Hàm tạo và Mô tả 1 JMenuItem() Tạo một JMenuItem không có text hoặc icon. 2 JMenuItem(Action a) Tạo một JMenuItem có các thuộc tính được nhận từ Action đã cho. 3 JMenuItem(Icon icon) Tạo một JMenuItem với icon đã cho. 4 JMenuItem(String text) Tạo một JMenuItem với text đã cho. 5 JMenuItem(String text, Icon icon) Tạo một JMenuItem với text và icon đã cho. 6 JMenuItem(String text, int mnemonic) Tạo một JMenuItem với text đã cho và mnemonic Pop-Up Menu Lớp JPopupMenu biểu diễn một menu mà có thể được popup một cách động tại một vị trí đã cho bên trong một thành phần. Cú pháp khai báo cho lớp javax.swing.jpopupmenu là: public class JpopupMen extends JComponent implements Accessible, MenuElement Các hàm tạo của lớp JpopupMenu: JPopupMenu(): Xây dựng một JPopupMenu không có "invoker". JPopupMenu(String label): Xây dựng một JPopupMenu với title đã cho Hiển thị và ẩn các mục trong menu Toolbars JToolBar là một thanh công cụ bao gồm các nút với biểu tượng tương ứng nằm dưới thanh menu. JToolBar cung cấp một thành phần hữu ích cho việc hiển thị, sử dụng Actions hoặc điều khiển. Với Look&Feel, người dùng có thể kéo ToolBar vào một cửa sổ riêng biệt. 125

126 Cú pháp khai báo của lớp JToolBar: public class JToolBar extends JComponent implements SwingConstants, Accessible Các hàm tạo của lớp JToolBar: STT Hàm tạo và Mô tả 1 JToolBar() Tạo mới một thanh công cụ, giá trị mặc định hướng tới HORIZONTAL 2 JToolBar(int orientation) Tạo mới một thanh công cụ, với giá trị là orientation 3 JToolBar(String name) Tạo mới thanh công cụ với tên là name 4 JToolBar(String name, int orientation) Tạo mới một thanh công cụ với giá trị name và orientation Cú pháp các phương thức của JToolBar STT Phương thức và Mô tả 1 JButton add(action a) Thêm mới một JButton với thuộc tính action đã cho 2 addimpl(component comp, Object constraints, int index) Nếu một JButton đã được thêm, ban đầu nó được thiết lập là disable 3 addseparator(dimension size) Thêm một dấu phân cách với kích thước size vào cuối thanh công cụ 4 createactionchangelistener(jbutton b) PropertyChangeListener: Tạo bộ lắng nghe khi có sự thay đổi 5 createactioncomponent(action a) Tạo ra JButton cho Action thêm vào JToolBar 126

127 Tooltips Tooltip là một chú thích xuất hiện khi rê chuột lên 1 đối tượng nào đó như văn bản, hình ảnh, liên kết và các phần tử giao diện khác. Nội dung trong tooltip có thể là văn bản, hình ảnh hoặc thông tin nào đó. Mục đích của tooltip là nói cho người dùng biết đối tượng này dùng để làm gì hoặc hiển thị thêm thông tin của đối tượng được rê chuột vào. Để tạo một Tooltips ta dùng phương thức settooltiptext(string). Ví dụ: btnlogin.settooltiptext("nhấn để đăng nhập"); Các phương thức - settooltiptext(string): Tạo một Tooltip với phần văn bản được khởi tạo - String gettooltiptext(): Trả về chuỗi văn bản được định nghĩa trong settooltiptext 4.6. Dialog Boxes Một cửa sổ hộp thoại (Dialog) là một cửa sổ con độc lập mang theo thông báo tạm thời ngoài cửa sổ chính của Swing Application. Phần lớn hộp thoại được dùng để hiển thị thông báo lỗi hoặc cảnh báo tới người dùng, nhưng hộp thoại cũng có thể hiển thị ảnh, cây thư mục, hoặc bất kỳ điều gì tương thích với ứng dụng Swing đang quản lý Tạo Dialogs Lớp JOptionPane là một thành phần cung cấp các phương thức chuẩn để popup một hộp thoại dialog chuẩn cho một giá trị hoặc thông báo người dùng một thông tin nào đó trong quá trình sử dụng ứng dụng. Để tạo một hộp thoại chuẩn, đơn giản ta có thể sử dụng lớp JoptionPane, để tạo hộp thoại tùy chỉnh thì ta sử dụng lớp trực tiếp là JDialog. Ví dụ 4.1: JOptionPane.showMessageDialog(mainFrame, Show Message dialog box"); 127

128 Option Dialogs JOptionPane cung cấp các hỗ trợ cho việc bố cục các hộp thoại chuẩn, cung cấp các icon, chỉ định tiêu đề hộp thoại văn bản, tùy chỉnh chữ trong nút lệnh. Những đặc điểm khác cũng cho phép tùy chỉnh các thành phần hiển thị hộp thoại và chỉ định nơi hiển thị hộp thoại trên màn hình. Cũng có thể chỉ định một option pane để đặt chính nó vào một internal frame (JInternalFrame) thay vì JDialog. Các đối số thường dùng trong các phương thức của lớp JoptionPane - Component parentcomponent: Đối số đầu tiên trong mỗi phương thức showxxxdialog luôn luôn là thành phần cha, trong đó bao gồm một Frame, một thành phần bên trong Frame, hoặc null. - Object message: Tham số bắt buộc này chỉ ra hộp thoại sẽ hiển thị trong khu vực chính của nó. Nói chung, chỉ định một chuỗi, mà kết quả trong hộp thoại hiển thị một nhãn với văn bản được chỉ định, có thể chia nhỏ các thông điệp trên nhiều dòng bằng cách đặt ký tự newline (\n) bên trong chuỗi thông báo. - String title: Tiêu đề của hộp thoại. - int optiontype: Chỉ định tập các nút lệnh xuất hiện phía cuối hộp thoại. - Chúng được chọn từ một trong các tập sau: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION, OK_CANCEL_OPTION. - int messagetype: Tham số này xác định icon được hiển thị trong hộp thoại. Icon được chọn từ một trong các giá trị sau: PLAIN_MESSAGE (no icon), ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE. - Object[] options: Thường được sử dụng để xác định chuỗi hiển thị bởi mỗi nút ở dưới cùng của hộp thoại. - Object initialvalue: Xác định giá trị mặc định được chọn showmessagedialog Hiển thị hộp thoại modal có một nút lệnh có nhãn là "OK". Ta có quyền chỉ định thông báo, icon và tiều đề cho hộp thoại hiển thị. Dưới đây là một số ví dụ của loại hộp thoại showmessagedialog: Ví dụ 4.2: Hiển thị Dialogbox với tiêu đề và icon mặc định //tiêu đề và icon mặc định JOptionPane.showMessageDialog(rootPane, "Hiển thị Dialog với tiêu đề mặc định."); 128

129 Ví dụ 4.3: Hiển thị Dialogbox với tiêu đề tùy chỉnh và icon cảnh báo //Tùy chỉnh tiêu đề và icon cảnh báo JOptionPane.showMessageDialog(rootPane, "Hiển thị Dialog cảnh báo.", "Cảnh báo!!!",joptionpane.warning_message); Ví dụ 4.4: Hiển thị Dialogbox với tiêu đề và icon tùy chỉnh Icon icon = new ImageIcon("C:\\Users\\LeeTC\\Desktop\\img\\uneti.png"); JOptionPane.showMessageDialog(rootPane, "Show Custom dialog!", "Custom Dialog!!!", JOptionPane.INFORMATION_MESSAGE, icon); showoptiondialog Hiển thị hộp thoại modal trong đó cho phép chỉ định nút lệnh, icon, thông báo, tiêu đề,... Phương thức này còn cho phép thay đổi văn bản xuất hiện trên các nút lệnh của hộp thoại chuẩn. Ta cũng có thể thực hiện được nhiều tùy chỉnh khác nữa. Ví dụ 4.5: //tùy chỉnh văn bản cho nút lệnh Object[] options = "Có, tôi muốn", "Không, cảm ơn", "Không, tôi sẽ tìm hiểu sau"; int n = JOptionPane.showOptionDialog(rootPane, 129

130 "Bạn có muốn tạo một hộp thoại tuỳ chọn?", "Tiêu đề Dialog", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); Tùy chỉnh văn bản nút lệnh Khi sử dụng JOptionPane để tạo ra một hộp thoại, có thể sử dụng văn bản tiêu chuẩn hoặc chỉ định văn bản khác. Theo mặc định, loại cửa sổ tùy chọn sẽ xác định có bao nhiêu nút xuất hiện. Ví dụ, hộp thoại YES_NO_OPTION có hai nút, và YES_NO_CANCEL_OPTION có ba nút. Mã lệnh sau đây dùng để tạo hai hộp thoại Yes/No. Hộp thoại đầu tiên được thực hiện với showconfirmdialog, trong đó sử dụng từ ngữ của look and feel cho hai nút lệnh. Hộp thoại thứ hai sử dụng showoptiondialog tùy chỉnh từ ngữ. Ví dụ 4.6: //icon mặc định, tiêu đề tùy chỉnh int n = JOptionPane.showConfirmDialog( rootpane, "Bạn có muốn thay đổi mật khẩu?", "Xác nhận", JOptionPane.YES_NO_OPTION); Object[] options = "Có, tôi muốn", "Không phải bây giờ!"; 130

131 int n = JOptionPane.showOptionDialog(rootPane, "Bạn có muốn thay đổi mật khẩu?", "Xác nhận", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, //không sử dụng icon tùy chỉnh options, //các tiêu để của các nút options[0]); //tiêu đề nút mặc định Lấy dữ liệu của người dùng từ hộp thoại Hộp thoại showxxxdialog trả về một đối tượng. Đối tượng này nói chung là một chuỗi phản ánh sự lựa chọn của người dùng. Dưới đây là một ví dụ sử dụng hộp thoại showinputdialog để cho phép người dùng chọn một trong bốn chuỗi: Ví dụ 4.7: Object[] option = "353 Trần Hưng Đạo - Nam Định", "Mỹ Xá - Nam Định", "456 Minh Khai - Hà Nội", "218 Lĩnh Nam - Hà Nội"; String s = (String) JOptionPane.showInputDialog(rootPane, "Chọn địa điểm đang học?", "Customized Dialog", JOptionPane.PLAIN_MESSAGE, icon, option, "456 Minh Khai - Hà Nội"); Nếu không quan tâm tới giới hạn lựa chọn của người dùng thì ta có thể sử dụng form của phương thức showinputdialog gồm một vài đối số hoặc chỉ định null cho mảng đối tượng. Trong Java look and feel thì việc thay thế null bằng possibilities sẽ cho kết quả là một hộp thoại giống như hình dưới đây: 131

132 Trao đổi dữ liệu File Dialogs Lớp JFileChooser là một thành phần cung cấp một kỹ thuật đơn giản cho người dùng để lựa chọn một file. Các hàm tạo của lớp JFileChooser: STT Hàm tạo và Mô tả 1 JFileChooser() Xây dựng một JFileChooser trỏ tới thư mục mặc định của người dùng. 2 JFileChooser(File currentdirectory) Xây dựng một JFileChooser sử dụng File đã cho như là path. 3 JFileChooser(File currentdirectory, FileSystemView fsv) Xây dựng JfileChooser sử dụng thư mục hiện tại đã cung cấp và FileSystemView. 4 JFileChooser(FileSystemView fsv) Xây dựng một JFileChooser sử dụng FileSystemView đã cho. 5 JFileChooser(String currentdirectorypath) Xây dựng một JFileChooser sử dụng path đã cho. 6 JFileChooser(String currentdirectorypath, FileSystemView fsv) Xây dựng một JfileChooser sử dụng thư mục hiện tại đã cung cấp và FileSystemView. 132

133 Các phương thức của JFileChooser STT Phương thức & Miêu tả 1 boolean removechoosablefilefilter(filefilter f) Xóa trình lọc filter từ danh sách các trình lọc file có thể lựa chọn của người dùng 2 void rescancurrentdirectory() Quét lại danh sách file từ thư mục hiện tại 3 void resetchoosablefilefilters() Phục hồi danh sách trình lọc file có thể lựa chọn về trạng thái ban đầu của nó 4 void setdialogtitle(string dialogtitle) Thiết lập chuỗi trong thanh tiêu đề của cửa sổ JFileChooser 5 void setdialogtype(int dialogtype) Thiết lập kiểu của dialog 6 int showdialog(component parent, String approvebuttontext) Popup một hộp thoại file chooser tùy biến với một nút xác nhận tùy biến 7 int showopendialog(component parent) Hiển thị hộp thoại "Open File" 8 int showsavedialog(component parent) Hiển thị một hộp thoại "Save File" Các lựa chọn màu Lớp JColorChooser cung cấp một pane cho các control được thiết kế cho phép người dùng thao tác và lựa chọn một màu. Các hàm tạo của lớp JColorChooser: JColorChooser(): Tạo một bảng chọn màu với một màu ban đầu là màu trắng. JColorChooser(Color initialcolor): Tạo bảng chọn màu với màu khởi tạo đã cho. 133

134 JColorChooser(ColorSelectionModel model): Tạo một bảng chọn màu với ColorSelectionModel đã cho. Các phương thức xử lý sự kiện của JColorChooser STT Phương thức & Miêu tả 1 void addchooserpanel(abstractcolorchooserpanel panel) Thêm một bảng chọn màu tới ColorChooser 2 static JDialog createdialog(component c, String title, boolean modal, JColorChooser chooserpane, ActionListener oklistener, ActionListener cancellistener) Tạo và trả về một hộp thoại dialog chứa bảng ColorChooser cùng với các nút "OK", "Cancel", và "Reset" 3 void setcolor(color color) Thiết lập màu hiện tại của Color Chooser 4 void setcolor(int c) Thiết lập màu hiện tại của Color Chooser 5 void setcolor(int r, int g, int b) Thiết lập màu hiện tại của Color Chooser thành màu RBG 6 static Color showdialog(component component, String title, Color initialcolor) Hiển thị một hộp thoại dialog 134

135 CÂU HỎI ÔN TẬP, THẢO LUẬN do. Câu 1. Xác định xem từng khẳng định sau là đúng hay sai. Nếu sai, giải thích lý a. Một JPanel là một JComponent. b. Một JPanel là một Component. c. Một JLabel là một Container. d. Một JList là một JPanel. e. Một AbstractButton là một JButton. f. Một JTextField là một Object. g. ButtonGroup là một lớp con của JComponent. Câu 2. Tìm lỗi trong mỗi dòng mã sau đây, và giải thích làm thế nào để sửa chúng. a. import Javax.swing.JFrame b. panelobject.gridlayout( 8, 8 ); // set GridLayout c. container.setlayout( new FlowLayout( FlowLayout.DEFAULT ) ); d. container.add( eastbutton, EAST ); // BorderLayout Câu 3. Những khẳng định sau đúng hay sai. Nếu sai giải thích tại sao? a. Phương thức Graphics drawpolygon tự động kết nối các điểm cuối của đa giác. b. Phương thức Graphics DrawLine vẽ một đường thẳng giữa hai điểm. c. Phương thức Graphics fillarc sử dụng độ để xác định góc. d. Trong hệ tọa độ Java, giá trị tăng dần trên trục y từ trái sang phải. e. Graphics kế thừa trực tiếp từ lớp Object. f. Graphics là một lớp trừu tượng. g. Lớp Font kế thừa trực tiếp từ lớp Graphics. Câu 4. Khẳng định sau đúng hay sai? Nếu sai giải thích tại sao? a. Chỉ có một layout manager có thể được sử dụng cho mỗi container. b. Các thành phần GUI có thể được thêm vào một container theo thứ tự bất kỳ trong một BorderLayout. c. JRadioButtons cung cấp một loạt các tùy chọn loại trừ nhau (chỉ có thể chọn được một tùy chọn tại một thời điểm). d. Phương pháp GUI setfont được sử dụng để thiết lập font cho các văn bản. e. JList hiển thị một thanh cuộn nếu có nhiều hơn các mục danh sách có thể hiển thị. f. Một đối tượng Mouse có một phương pháp gọi là mousedragged 135

136 Câu 5. Tạo các giao diện sau. Không cần phải tạo chức năng bất kỳ nào. Câu 6. Tạo ra các giao diện sau đây. Không cần phải tạo chức năng bất kỳ nào. Câu 7. Tạo ra các giao diện sau đây. Không cần phải tạo chức năng bất kỳ nào. Câu 8. Tạo ra các giao diện sau đây. Không cần phải tạo chức năng bất kỳ nào. Câu 9. Hãy thiết kế giao diện đồ họa với các yêu cầu sau: Nhập số n, tính tổng n, in ra các ước của n, số nhập vào có phải số nguyên tố không? Phân tích số vừa nhập vào? 136

137 BÀI TẬP ỨNG DỤNG Bài 1. Viết một ứng dụng chuyển đổi nhiệt độ có thể chuyển đổi từ độ F sang độ C. Nhiệt độ F được nhập từ bàn phím (thông qua một JTextField). Một JLabel nên được sử dụng để hiển thị nhiệt độ chuyển đổi. Sử dụng công thức sau cho việc chuyển đổi Bài 2. Viết một ứng dụng hiển thị các sự kiện khi chúng xảy ra trong một JTextArea. Cung cấp một JComboBox với tối thiểu là bốn lựa chọn. Người dùng sẽ có thể lựa chọn một sự kiện theo dõi từ JComboBox. Khi mà sự kiện xảy ra, hiển thị thông tin về sự kiện trong JTextArea. Sử dụng phương pháp to String trên đối tượng sự kiện để chuyển đổi nó thành một chuỗi. Bài 3. Tạo một ứng dụng sử dụng các thành phần Swing để hiển thị ngày tháng trong một nhãn. Ngày tháng hoàn chỉnh bao gồm ngày trong tuần (dạng text), tháng (dạng text), ngày trong tháng và năm; trong đó phần ngày, tháng và năm lấy từ các slider tương ứng phía dưới. Mỗi khi có sự thay đổi một trong các slider thì lập tức nó sẽ được cập nhật trên nhãn. Riêng phần ngày trong tuần phải lấy được đúng ngày dựa trên các giá trị tương ứng của các slider. Giao diện của ứng dụng trông sẽ như hình dưới đây:+ Bài 4. Tạo một form có dạng sau: 137

138 Gợi ý: Sử dụng ButtonGroup để nhóm các radio button. Bài 5. Xây dựng ứng dụng hiển thị Đồng hồ sử dụng Java Swing có dạng sau: Bài 6. Xây dựng ứng dụng Notepad sử dụng cửa sổ duyệt file (Open Dialog box) để mở một file text v 138

139 Mục đích Chương 5. LUỒNG VÀ TẬP TIN Nội dung chương này trình bày cụ thể về luồng byte (Byte Streams), luồng ký tự (Character Streams) cũng như cách nhập, xuất dữ liệu từ Console và Files 5.1. Mở đầu Việc lưu trữ dữ liệu trong các biến chương trình có tính chất tạm thời, dữ liệu sẽ mất đi khi biến ra khỏi tầm ảnh hưởng của nó hoặc khi chương trình kết thúc. Files giúp cho các chương trình có thể lưu trữ một lượng lớn dữ liệu, trong một thời gian dài ngay khi chương trình kết thúc. Trong chương này chúng ta sẽ tìm hiểu làm thế nào các chương trình Java có thể tạo, đọc, ghi xử lý các files tuần tự và các file truy cập ngẫu nhiên thông qua một số ví dụ minh họa. Các nội dung chính như sau: 5.2. Luồng Thư viện các lớp về luồng trong Java: luồng byte, luồng ký tự. Xuất nhập Console dùng luồng byte, luồng ký tự. Xuất nhập files dùng luồng ký tự và luồng byte. Vấn đề xử lý truy cập ngẫu nhiên dùng lớp RandomAccessFile. Xử lý file và thư mục dùng lớp File Khái niệm luồng Tất cả những hoạt động nhập/xuất dữ liệu (nhập dữ liệu từ bàn phím, lấy dữ liệu từ mạng về, ghi dữ liệu ra đĩa, xuất dữ liệu ra màn hình, máy in, ) đều được quy về một khái niệm gọi là luồng (stream). Luồng là nơi có thể sản xuất và tiêu thụ thông tin. Luồng thường được hệ thống xuất nhập trong Java gắn kết với một thiết bị vật lý. Java hiện thực luồng bằng tập hợp các lớp phân cấp trong gói Java.io. Java định nghĩa hai kiểu luồng: byte và ký tự. Luồng byte (hay luồng dựa trên byte) hỗ trợ việc xuất nhập dữ liệu trên byte, thường được dùng khi đọc ghi dữ liệu nhị phân. Luồng ký tự được thiết kế hỗ trợ việc xuất nhập dữ liệu kiểu ký tự (Unicode). Trong một vài trường hợp luồng ký tự sử dụng hiệu quả hơn luồng byte, nhưng ở mức hệ thống thì tất cả những xuất nhập đều phải qui về byte. Luồng ký tự hỗ trợ hiệu quả chỉ đối với việc quản lý, xử lý các ký tự Luồng byte (Byte Streams) Các luồng byte được định nghĩa dùng hai lớp phân cấp. Mức trên cùng là hai lớp trừu tượng InputStream và OutputStream. InputStream định nghĩa những đặc điểm chung cho những luồng nhập byte. OutputStream mô tả cách xử lý của các luồng xuất byte. Các lớp con dẫn xuất từ hai lớp InputStream và OutputStream sẽ hỗ trợ chi tiết tương ứng với việc đọc ghi dữ liệu trên những thiết bị khác nhau. 139

140 Lớp luồng byte BufferedInputStream BufferedOutputStream Luồng nhập vào bộ đệm Luồng xuất ra bộ đệm Ý nghĩa ByteArrayInputStream ByteArrayOutputStream DataInputStream DataOutputStream FileInputStream FileOutputStream FilterInputStream FilterOutputStream InputStream OutputStream PrintStream RandomAccessFile SequenceInputStream Đọc dữ liệu từ một mảng byte Ghi dữ liệu đến một mảng byte Luồng nhập có những phương thức đọc những kiểu dữ liệu chuẩn trong Java Luồng xuất có những phương thức ghi những kiểu dữ liệu chuẩn trong Java Luồng nhập cho phép đọc dữ liệu từ file Luồng xuất cho phép ghi dữ liệu xuống file Hiện thực lớp trừu tượng InputStream Hiện thực lớp trừu tượng OutputStream Lớp trừu tượng, là lớp cha của tất cả các lớp luồng nhập kiểu Byte Là lớp cha trừu tượng của tất cả các lớp xuất nhập kiểu Byte Luồng xuất có chứa phương thức print() và println() Hỗ trợ các thao tác đọc, ghi đối với file truy cập ngẫu nhiên Là một luồng nhập được tạo nên bằng cách nối kết logic các luồng nhập khác Luồng ký tự (Character Streams) Các luồng ký tự được định nghĩa dùng hai lớp phân cấp. Mức trên cùng là hai lớp trừu tượng Reader và Writer. Lớp Reader dùng cho việc nhập dữ liệu của luồng, lớp Writer dùng cho việc xuất dữ liệu cua luồng. Những lớp dẫn xuất từ Reader và Writer thao tác trên các luồng ký tự Unicode. Lớp luồng ký tự Ý nghĩa BufferedReader BufferedWriter CharArrayReader CharArrayWriter Luồng nhập ký tự đọc dữ liệu vào một vùng đệm Luồng xuất ký tự ghi dữ liệu tới một vùng đệm Luồng nhập đọc dữ liệu từ một mảng ký tự Luồng xuất ghi dữ liệu tời một mảng ký tự 140

141 FileReader FileWriter FilterReader FilterWriter InputStreamReader LineNumberReader Luồng nhập ký tự đọc dữ liệu từ file Luồng xuất ký tự ghi dữ liệu đến file Lớp đọc dữ liệu trung gian (lớp trừu tượng) Lớp xuất trung gian trừu tượng Luồng nhập chuyển bytes thành các ký tự Luồng nhập đếm dòng OutputStreamWriter Luồng xuất chuyển những ký tự thành các bytes PrintWriter PushbackReader Reader StringReader StringWriter Writer Luồng ghi văn bản ra thiết bị xuất (chứa phương thức print() và println() ) Luồng nhập cho phép đọc và khôi phục lại dữ liệu Lớp nhập dữ liệu trừu tượng Luồng nhập đọc dữ liệu từ chuỗi Luồng xuất ghi dữ liệu ra chuỗi Lớp ghi dữ liệu trừu tượng Những luồng được định nghĩa trước (The Predefined Streams) Tất cả các chương trình viết bằng Java luôn tự động import gói Java.lang. Gói này có định nghĩa lớp System, bao gồm một số đặc điểm của môi trường run-time, nó có ba biến luồng được định nghĩa trước là in, out và err, các biến này là các trường được khai báo static trong lớp System. System.out: luồng xuất chuẩn, mặc định là console. System.out là một đối tượng kiểu PrintStream. System.in: luồng nhập chuẩn, mặc định là bàn phím. System.in là một đối tượng kiểu InputStream. System.err: luồng lỗi chuẩn, mặc định cũng là console. System.out cũng là một đối tượng kiểu PrintStream giống System.out Sử dụng luồng Byte Như chúng ta đã biết hai lớp InputStream và OutputStream là hai siêu lớp (cha) đối với tất cả những lớp luồng xuất nhập kiểu byte. Những phương thức trong hai siêu lớp này ném ra các lỗi kiểu IOException. Những phương thức định nghĩa trong hai siêu lớp này là có thể dùng trong các lớp con của chúng. Vì vậy tập các phương thức đó là tập tối tiểu các chức năng nhập xuất mà những luồng nhập xuất kiểu byte có thể sử dụng. Những phương thức định nghĩa trong lớp InputStream và OutputStream Phương thức Ý nghĩa 141

142 InputStream int available( ) void close( ) void mark(int numbytes) boolean marksupported( ) int read( ) int read(byte buffer[ ]) int read(byte buffer[ ], int offset, int numbytes) void reset( ) long skip(long numbytes) OutputStream void close( ) void flush( ) void write(int b) void write(byte buffer[ ]) void write(byte buffer[ ], int offset,int numbytes) Luồng nhập Trả về số lượng bytes có thể đọc được từ luồng nhập Đóng luồng nhập và giải phóng tài nguyên hệ thống gắn với luồng. Không thành công sẽ ném ra một lỗi IOException Đánh dấu ở vị trí hiện tại trong luồng nhập Kiểm tra xem luồng nhập có hỗ trợ phương thức mark() và reset() không. Đọc byte tiếp theo từ luồng nhập Đọc buffer.length bytes và lưu vào trong vùng nhớ buffer. Kết quả trả về số bytes thật sự đọc được Đọc numbytes bytes bắt đầu từ địa chỉ offset và lưu vào trong vùng nhớ buffer. Kết quả trả về số bytes thật sự đọc được Nhảy con trỏ đến vị trí được xác định bởi việc gọi hàm mark() lần sau cùng Nhảy qua numbytes dữ liệu từ luồng nhập Luồng xuất Đóng luồng xuất và giải phóng tài nguyên hệ thống gắn với luồng. Không thành công sẽ ném ra một lỗi IOException Ép dữ liệu từ bộ đệm phải ghi ngay xuống luồng Ghi byte dữ liệu chỉ định xuống luồng Ghi buffer.length bytes dữ liệu từ mảng chỉ định xuống luồng Ghi numbytes bytes dữ liệu từ vị trí offset của mảng chỉ định buffer xuống luồng Đọc dữ liệu từ Console Trước đây, khi Java mới ra đời để thực hiện việc nhập dữ liệu từ Console chỉ dùng luồng nhập byte. Về sau có thể dùng cả luồng byte và luồng ký tự. Sử dụng luồng ký tự dễ đơn giản và dễ bảo trì chương trình. Ví dụ 5.1: Chương trình minh họa việc đọc một mảng bytes từ System.in import java.io.*; 142

143 class ReadBytes public static void main(string args[]) throws IOException byte data[] = new byte[1000]; System.out.print("Enter some characters."); System.in.read(data); System.out.print("You entered: "); for(int i=0; i < data.length; i++) System.out.print((char) data[i]); Kết quả thực thi chương trình: Xuất dữ liệu ra Console Để xuất dữ liệu ra Console sử dụng cả luồng ký tự và luồng byte. Kể từ phiên bản 1.1 (có thêm luồng ký tự). Tuy nhiên, cho đến nay để xuất dữ liệu ra Console thường vẫn dùng luồng byte. Sử dụng phương thức print() và println(), write () để xuất dữ liệu ra Console. Ví dụ 5.2: Minh họa sử dụng phương thức System.out.write() để xuất ký tự X ra Console import java.io.*; class WriteDemo public static void main(string args[]) int b; b = 'X'; System.out.write(b); System.out.write('\n'); Kết quả thực thi chương trình: 143

144 Đọc và ghi file dùng luồng Byte Tạo một luồng Byte gắn với file chỉ định dùng FileInputStream và FileOutputStream. Để mở một file, đơn giản chỉ cần tạo một đối tượng của những lớp này, tên file cần mở là thông số trong constructor. Khi file mở, việc đọc và ghi dữ liệu trên file được thực hiện một cách bình thường thông qua các phương thức cung cấp trong luồng Đọc dữ liệu từ file Mở một file để đọc dữ liệu FileInputStream(String filename) throws FileNotFoundException Nếu file không tồn tại: thì bung ra ngoại lệ FileNotFoundException Đọc dữ liệu: dùng phương thức read() int read( ) throws IOException: đọc từng byte từ file và trả về giá trị của byte đọc được. Trả về -1 khi hết file, và bung ra IOException khi có lỗi đọc. Đóng file: dùng phương thức close() void close( ) throws IOException: sau khi làm việc xong cần đóng file để giải phóng tài nguyên hệ thống đã cấp phát cho file. Ví dụ 5.3: /* Hiển thị nội dung của một file tên test.txt lưu tạid:\test.txt */ import java.io.*; class ShowFile public static void main(string args[]) throws IOException int i; FileInputStream fin; try fin = new FileInputStream( D:\\test.txt ); catch(filenotfoundexception exc) System.out.println("File Not Found"); return; catch(arrayindexoutofboundsexception exc) System.out.println("Usage: ShowFile File"); 144

145 return; // read bytes until EOF is encountered do i = fin.read(); if(i!= -1) System.out.print((char) i); while(i!= -1); fin.close(); Kết quả thực thi chương trình: Ghi dữ liệu xuống file Mở một file để ghi dữ liệu FileOutputStream (String filename) throws FileNotFoundException Nếu file không tạo được: thì bung ra ngoại lệ FileNotFoundException Ghi dữ liệu xuống: dùng phương thức write ( ) void write (int byteval) throws IOException: ghi một byte xác định bởi tham số byteval xuống file, và bung ra IOException khi có lỗi ghi. Đóng file: dùng phương thức close ( ) void close ( ) throws IOException: sau khi làm việc xong cần đóng file để giải phóng tài nguyên. Ví dụ 5.4: Copy nội dung một file text đến một file text khác. import java.io.*; class CopyFile public static void main(string args[])throws IOException 145

146 int i; FileInputStream fin; FileOutputStream fout; try // open input file try fin = new FileInputStream( D:\\source.txt ); catch(filenotfoundexception exc) System.out.println("Input File Not Found"); return; // open output file try fout = new FileOutputStream( D:\\dest.txt ); catch(filenotfoundexception exc) System.out.println("Error Opening OutputFile"); return; catch(arrayindexoutofboundsexception exc) System.out.println("Usage: CopyFile From To"); return; do i = fin.read(); if(i!= -1) fout.write(i); while(i!= -1); 146

147 catch(ioexception exc) System.out.println("File Error"); fin.close(); fout.close(); Kết quả thực thi chương trình: chương trình sẽ copy nội dung của file D:\source.txt và ghi vào một file mới D:\dest.txt Đọc và ghi dữ liệu nhị phân Phần trên chúng ta đã đọc và ghi các bytes dữ liệu là các ký tự mã ASCII. Để đọc và ghi những giá trị nhị phân của các kiểu dữ liệu trong Java, chúng ta sử dụng DataInputStream và DataOutputStream. DataOutputStream: thực hiện interface DataOuput. Interface DataOutput có các phương thức cho phép ghi tất cả những kiểu dữ liệu cơ sở của Java đến luồng (theo định dạng nhị phân). Phương thức void writeboolean(boolean val) void writebyte (int val) void writechar (int val) void writedouble(double val) void writefloat (float val) void writeint (int val) void writelong (long val) void writeshort (int val) Ý nghĩa Ghi xuống luồng giá trị boolean được xác định bởi val Ghi xuống luồng một byte được xác định bởi val Ghi xuống luồng một Char được xác định bởi val Ghi xuống luồng giá trị Double được xác định bởi val Ghi xuống luồng giá trị floatđược xác định bởi val Ghi xuống luồng giá trị int được xác định bởi val Ghi xuống luồng giá trị long được xác định bởi val Ghi xuống luồng giá trị short được xác định bởi val Contructor: DataOutputStream(OutputStream outputstream) OutputStream: là luồng xuất dữ liệu. Để ghi dữ liệu ra file thì đối tượng outputstream có thể là FileOutputStream. DataInputStream: thực hiện interface DataInput. Interface DataInput có các phương thức cho phép đọc tất cả những kiểu dữ liệu cơ sở của Java (theo định dạng nhị phân). Phương thức Ý nghĩa 147

148 boolean readboolean( ) Byte readbyte( ) char readchar() double readdouble() float readfloat() int readint() Long readlong( ) short readshort( ) Đọc một giá trị boolean Đọc một byte Đọc một Char Đọc một giá trị Double Đọc một giá trị float Đọc một giá trị int Đọc một giá trị long Đọc một giá trị short Contructor: DataInputStream(InputStream inputstream) InputStream: là luồng nhập dữ liệu. Để đọc dữ liệu từ file thì đối tượng InputStream có thể là FileInputStream Ví dụ 5.5: Dùng DataOutputStream và DataInputStream để ghi và đọc những kiểu dữ liệu khác nhau trên file. import java.io.*; class RWData public static void main(string args[]) throws IOException DataOutputStream dataout; DataInputStream datain; int i = 10; double d = ; boolean b = true; try dataout = new DataOutputStream(new FileOutputStream("D:\\testdata")); catch(ioexception exc) System.out.println("Cannot open file."); return; try System.out.println("Writing " + i); 148

149 dataout.writeint(i); System.out.println("Writing " + d); dataout.writedouble(d); System.out.println("Writing " + b); dataout.writeboolean(b); System.out.println("Writing " * 7.4); dataout.writedouble(12.2 * 7.4); catch(ioexception exc) System.out.println("Write error."); dataout.close(); System.out.println(); // Now, read them back. try datain = new DataInputStream( new FileInputStream("D:\\testdata")); catch(ioexception exc) System.out.println("Cannot open file."); return; Kết quả thực thi chương trình: Dữ liệu ghi xuống file D:\\testdata Kết quả đọc và xuất ra Console: 149

150 5.4. File truy cập ngẫu nhiên (Random Access Files) Bên cạnh xử lý xuất nhập trên file theo kiểu tuần tự thông qua các luồng, Java cũng hỗ trợ truy cập ngẫu nhiên nội dung của một file nào đó dùng RandomAccessFile. RandomAccessFile không dẫn xuất từ InputStream hay OutputStream mà hiện thực các interface DataInput, DataOutput. RandomAccessFile hỗ trợ vấn đề định vị con trỏ file bên trong một file dùng phương thức seek(long newpos). Ví dụ 5.6: Minh họa việc truy cập ngẫu nhiên trên file. Chương trình ghi 6 số kiểu double xuống file, rồi đọc lên theo thứ tự ngẫu nhiên. import Java.io.*; class RandomAccessDemo public static void main(string args[]) throws IOException double data[] = 19.4, 10.1, , 33.0, 87.9, 74.25; double d; RandomAccessFile raf; try raf = new RandomAccessFile("D:\\random.dat","rw"); catch(filenotfoundexception exc) System.out.println("Cannot open file."); return ; // Write values to the file. for(int i=0; i < data.length; i++) 150

151 try raf.writedouble(data[i]); catch(ioexception exc) System.out.println("Error writing to file."); return ; try // Now, read back specific values raf.seek(0); // seek to first double d = raf.readdouble(); System.out.println("First value is " + d); raf.seek(8); // seek to second double d = raf.readdouble(); System.out.println("Second value is " + d); raf.seek(8 * 3); // seek to fourth double d = raf.readdouble(); System.out.println("Fourth value is " + d); System.out.println(); // Now, read every other value. System.out.println("Here is every other value: "); for(int i=0; i < data.length; i+=2) raf.seek(8 * i); // seek to ith double d = raf.readdouble(); System.out.print(d + " "); System.out.println("\n"); 151

152 catch(ioexception exc) System.out.println("Error seeking or reading."); raf.close(); Kết quả thực thi chương trình: 5.5. Sử dụng luồng ký tự Để quản lý xuất nhập dữ liệu kiểu character, Java đưa ra kiểu luồng character phục vụ cho việc xuất nhập dữ liệu kiểu character trên luồng. Mức trên cùng là hai lớp trừu tượng Reader và Writer. Lớp Reader dùng cho việc nhập dữ liệu, lớp Writer dùng cho việc xuất dữ liệu của luồng. Những lớp dẫn xuất từ Reader và Writer thao tác trên các luồng ký tự Unicode. Những phương thức định nghĩa trong lớp trừu tượng Reader và Writer Phương thức Ý nghĩa Reader abstract void close( ) void mark(int numchars) boolean marksupported( ) int read( ) int read(char buffer[ ]) abstract int read(char buffer[ ],int offset,int numchars) boolean ready( ) void reset( ) Đóng luồng Đánh dấu vị trí hiện tại trên luồng Kiểm tra xem luồng có hỗ trợ thao tác đánh dấu mark() không? Đọc một ký tự Đọc buffer.length ký tự cho vào buffer Đọc numchars ký tự cho vào vùng đệm buffer tại vị trí buffer[offset] Kiểm tra xem luồng có đọc được không? Dời con trỏ nhập đến vị trí đánh dấu trước đó 152

153 long skip(long numchars) Bỏ qua numchars của luồng nhập Writer abstract void close( ) abstract void flush( ) void write(int ch) void write(byte buffer[ ]) abstract void write(char buffer[],int offset, int numchars) void write(string str) void write(string str, int offset, int numchars) Đóng luồng xuất. Có lỗi bung ra IOException Dọn dẹp luồng (buffer xuất) Ghi một ký tự Ghi một mảng các ký tự Ghi một phần của mảng ký tự Ghi một chuỗi Ghi một phần của một chuỗi ký tự Nhập Console dùng luồng ký tự Việc nhập dữ liệu từ Console dùng luồng ký tự thường thuận lợi hơn dùng luồng byte. Lớp tốt nhất để đọc dữ liệu nhập từ Console là lớp BufferedReader. Tuy nhiên chúng ta không thể xây dựng một lớp BufferedReader trực tiếp từ System.in. Thay vào đó phải chuyển thành một luồng ký tự. Để làm điều này dùng InputStreamReader chuyển bytes thành ký tự. Để có một đối tượng InputStreamReader gắn với System.in ta dùng constructor của InputStreamReader. InputStreamReader(InputStream inputstream) Tiếp theo dùng đối tượng InputStreamReader đã tạo ra để tạo ra một BufferedReader dùng constructor BufferedReader. BufferedReader(Reader inputreader) Tạo một BufferedReader gắn với Keyboard BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); Sau khi thực hiện câu lệnh trên, br là một luồng ký tự gắn với Console thông qua System.in. Ví dụ 5.7: Dùng BufferedReader đọc từng ký tự từ Console. Việc đọc kết thúc khi gặp dấu chấm (dấu chấm để kết thúc chương trình). import java.io.*; class ReadChars public static void main(string args[]) throws IOException 153

154 char c; BufferedReader br=newbufferedreader(new InputStreamReader(System.in)); System.out.println("Nhap chuoi ky tu,gioi han dau cham."); // read characters do c = (char) br.read(); System.out.println(c); while(c!= '.'); Kết quả thực thi chương trình: Ví dụ 5.8: Dùng BufferedReader đọc chuỗi ký tự từ Console. Chương trình kết thúc khi gặp chuỗi đọc là chuỗi stop import java.io.*; class ReadLines public static void main(string args[]) throws IOException BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; System.out.println("Nhapchuoi."); System.out.println("Nhap 'stop' ket thuc chuong trinh."); do str = br.readline(); System.out.println(str); while(!str.equals("stop")); 154

155 Kết quả thực thi chương trình: Xuất Console dùng luồng ký tự Trong ngôn ngữ Java, bên cạnh việc dùng System.out để xuất dữ liệu ra Console (thường dùng để debug chương trình), chúng ta có thể dùng luồng PrintWriter đối với các chương trình chuyên nghiệp. PrintWriter là một trong những lớp luồng ký tự. Việc dùng các lớp luồng ký tự để xuất dữ liệu ra Console thường được ưa chuộng hơn. Để xuất dữ liệu ra Console dùng PrintWriter cần thiết phải chỉ định System.out cho luồng xuất. Ví dụ: Tạo đối tượng PrintWriter để xuất dữ liệu ra Console PrintWriter pw = new PrintWriter(System.out, true); Ví dụ 5.9: Minh họa dùng PrintWriter để xuất dữ liệu ra Console import java.io.*; public class PrintWriterDemo public static void main(string args[]) PrintWriter pw = new PrintWriter(System.out, true); int i = 10; double d = ; double r = i+d; pw.println("using a PrintWriter."); pw.println(i); pw.println(d); pw.println(i + " + " + d + " = " + r); Kết quả thực thi chương trình: 155

156 Đọc/ghi File dùng luồng ký tự Thông thường để đọc/ghi file thường dùng luồng byte, nhưng đối với luồng ký tự chúng ta cũng có thể thực hiện được. Ưu điểm của việc dùng luồng ký tự là chúng thao tác trực tiếp trên các ký tự Unicode. Vì vậy luồng ký tự là chọn lựa tốt nhất khi cần lưu những văn bản Unicode. Hai lớp luồng thường dùng cho việc đọc/ghi dữ liệu ký tự xuống file là FileReader và FileWriter. Ví dụ 5.10: Đọc những dòng văn bản nhập từ bàn phím và ghi chúng xuống file tên là test.txt. Việc đọc và ghi kết thúc khi người dùng nhập vào chuỗi stop. import java.io.*; class KtoD public static void main(string args[]) throws IOException String str; FileWriter fw; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try fw = new FileWriter("D:\\test.txt"); catch(ioexception exc) System.out.println("Khong the mo file."); return ; System.out.println("Nhap ('stop' de ket thuc chuong trinh)."); do System.out.print(": "); 156

157 str = br.readline(); if(str.compareto("stop") == 0) break; str = str + "\r\n"; fw.write(str); while(str.compareto("stop")!= 0); fw.close(); Kết quả thực thi chương trình. Dữ liệu nhập từ Console: Dữ liệu ghi xuống file: Ví dụ 5.11: Đọc và hiển thị nội dung của file test.txt lên màn hình. import java.io.*; class DtoS public static void main(string args[]) throws Exception FileReader fr = new FileReader("D:\\test.txt"); BufferedReader br = new BufferedReader(fr); String s; while((s = br.readline())!= null) System.out.println(s); fr.close(); 157

158 Kết quả thực thi chương trình Nội dung của file test.txt: Kết quả đọc file và hiển thị ra Console: 5.6. Lớp File Lớp File không phục vụ cho việc nhập/xuất dữ liệu trên luồng. Lớp File thường được dùng để biết được các thông tin chi tiết về tập tin cũng như thư mục (tên, ngày giờ tạo, kích thước, ). Các Constructor: - Tạo đối tượng File từ đường dẫn tuyệt đối public File(String pathname) File f = new File( C:\\Java\\vd1.Java ); - Tạo đối tượng File từ tên đường dẫn và tên tập tin tách biệt public File(String parent, String child) File f = new File( C:\\Java, vd1.java ); - Tạo đối tượng File từ một đối tượng File khác public File(File parent, String child) File dir = new File ( C:\\Java ); File f = new File(dir, vd1.java ); Một số phương thức thường gặp của lớp File public String getname() public String getpath() public boolean isdirectory() public boolean isfile() public String[] list() Lấy tên của đối tượng File Lấy đường dẫn của tập tin Kiểm tra xem tập tin có phải là thư mục không? Kiểm tra xem tập tin có phải là một file không? Lấy danh sách tên các tập tin và thư mục con của đối tượng File đang xét và trả về trong một mảng 158

159 Ví dụ 5.12: import java.awt.*; import java.io.*; public class FileDemo public static void main(string args[]) Frame fr = new Frame ("File Demo"); fr.setbounds(10, 10, 300, 200); fr.setlayout(new BorderLayout()); Panel p = new Panel(new GridLayout(1,2)); List list_c = new List(); list_c.add("c:\\"); File driver_c = new File ("C:\\"); String[] dirs_c = driver_c.list(); for (int i=0;i<dirs_c.length;i++) File f = new File ("C:\\" + dirs_c[i]); if (f.isdirectory()) list_c.add("<dir>" + dirs_c[i]); else list_c.add(" " + dirs_c[i]); List list_d = new List(); list_d.add("d:\\"); File driver_d = new File ("D:\\"); String[] dirs_d = driver_d.list(); for (int i=0;i<dirs_d.length;i++) File f = new File ("D:\\" + dirs_d[i]); if (f.isdirectory()) list_d.add("<dir>" + dirs_d[i]); else list_d.add(" " + dirs_d[i]); 159

160 p.add(list_c); p.add(list_d); fr.add(p, BorderLayout.CENTER); fr.setvisible(true); Kết quả thực thi chương trình: 160

161 CÂU HỎI ÔN TẬP, THẢO LUẬN Điền vào chỗ trống để được câu hoàn thiện là các dàn ống (pipelines) để gửi và nhận thông tin trong các chương trình Java là luồng lỗi chuẩn. 3. Phương thức đọc các byte dữ liệu từ một luồng. 4. Phương thức trả về giá trị boolean, nêu rõ luồng có hỗ trợ các khả năng mark và reset hay không. 5. Phương thức xả sạch luồng. 6. Nhập/xuất mảng byte sử dụng các lớp và Lớp được sử dụng truy cập các đối tượng thư mục và tập tin là một khi chưa để lưu giữ dữ liệu. BÀI TẬP ỨNG DỤNG Bài 1. Viết chương trình nhận một dòng văn bản từ người dùng và hiển thị đoạn văn bản đó lên màn hình. Bài 2. Viết chương trình sao chép nội dụng một tập tin tới tập tin khác. Bài 3. Viết chương trình tạo ra một tập tin truy cập ngẫu nhiên. kết xuất hiển thị phía dưới đây. Các bản ghi nên được lưu ở dạng tập tin.dat, vì vậy người dùng truy cập chúng nhanh hơn. Bài 4. Viết chương trình ghi các giá trị kiểu int, float, double, string, tới một file có tên abc.txt. Đọc giá trị số nguyên và double từ tập tin và hiển thị chúng ra màn hình. Bài 5. Viết chương trình nhận một dòng văn bản từ người dùng và kiểm tra xem tập tin văn bản đã có (test.txt) có nội dung đó không? Nếu có thì in nội dung của văn bản đó ra màn hình. Bài 6. Một trung tâm tin học cần quản lý các phương tiện giao thông gồm các loại ô tô và xe máy.mỗi loại phương tiện (ô tô và xe máy ) cần quản lý : màu xe,giá thành,và hãng sản suất.ngoài ra đối với ô tô cần biết thêm : số ghế,loại máy động cơ; còn đối với xe máy thì cần biết thêm : công suất. 161

162 1/Xây dựng các lớp cho các phương tiện, trong đó lớp ô tô và xe máy là được kế thừa từ lớp phương tiện giao thông nói chung. 2/ Viết chương trình thực hiện theo các menu sau : Đăng ký loại phương tiện mới Xóa đi loại phương tiện Tìm theo hãng sản xuất Tìm theo màu Thoát khỏi hệ thống Người sử dụng có thể chọn một trong các mục nêu trên.nếu chọn a/ thì hệ thống sẽ hỏi là nhập mới otô hay xe máy, chọn b/ để xóa đi một mục đã đăng ký, c/ thì hệ thống sẽ hỏi tên hàng sản xuất cần tìm...vvv.viết chương trình thực hiện các chức năng trên.thông tin về các loại phương tiện giao thông khi nhậ vào phải được ghi lên tệp để sau đó được đọc để tìm kiếm theo hãng hay theo màu Bài 7. Phòng quản lý sinh viên của Đại học QG cần quản lý các thông tin về sinh viên của Trường. Mỗi sinh viên cần quản lý : họ và tên, số báo danh, ngày tháng năm sinh,môn học và điểm môn học.viết chương trình thực hiện theo các menu sau: 1. Nhập thông tin của sinh viên mới 2. Tìm theo số báo danh 3. Hiển thị tất cả danh sách các sinh viên 4. Thoát khỏi hệ thống. Người sử dụng có thể chọn một trong các mục trên.nếu chọn 1. thì người sử dụng có thể nhập vào một sinh viên mới, chọn 2. để tìm theo số báo danh...vv Viết chương trình thực hiện các chức năng trên. Thông tin về sinh viên khi nhập vào phải được ghi lên tệp để sau đó được đọc ra để tìm kiếm hay hiển thị danh sách 162

163 Mục đích Chương 6. ỨNG DỤNG GIAO TIẾP VỚI CƠ SỞ DỮ LIỆU Trong chương này, chúng tôi trình bày về kiến trúc JDBC, cách kết nối cơ sở dữ liệu với JDBC, sử dụng cơ sở dữ liệu lưu trữ trên các hệ quản trị khác nhau như MYSQL, SQLSERVER, ORACLE, cách tạo các truy vấn thao tác trên cơ sở dữ liệu 6.1. Giới thiệu Hầu hết các chương trình máy tính hiện này đều liên quan đến việc truy xuất thông tin trong cơ sở dữ liệu. Vì vậy các thao tác hỗ trợ lập trình cơ sở dữ liệu là chức năng không thể thiếu của các ngôn ngữ lập trình hiện đại, trong đó có Java. JDBC API là thư viện chứa các lớp và giao diện hỗ trợ lập trình viên Java kết nối và truy cập đến các hệ cơ sở dữ liệu. Phiên bản JDBC API mới nhất hiện nay là 3.0, là một thành phần trong J2SE, nằm trong 2 gói thư viện: - Java.sql: chứa các lớp và giao diên cơ sở của JDBC API. - Javax.sql: chứa các lớp và giao diện mở rộng. JDBC API cung cấp cơ chế cho phép một chương trình viết bằng Java có khả năng độc lập với các hệ cơ sở dữ liệu, có khả năng truy cập đến các hệ cơ sở dữ liệu khác nhau mà không cần viết lại chương trình. JDBC đơn giản hóa việc tạo và thi hành các câu truy vấn SQL trong chương trình Kiến trúc JDBC, cách tạo ứng dụng JDBC Kiến trúc của của JDBC tương tự như kiến trúc ODBC do Microsoft xây dựng. Theo kiến trúc này các thao tác liên quan đến cơ sở dữ liệu trong chương trình được thực hiện thông qua các JDBC API. Sau đó các JDBC API sẽ truyền các yêu cầu của chương trình đến bộ quản lý trình điều khiển JDBC, là bộ phận có nhiệm vụ lựa chọn trình điều khiển thích hợp để làm việc với cơ sở dữ liệu cụ thể mà chương trình muốn kết nối. DriverManager C Connection C Statement C ResultSet Establish to DB link Driver D Database Hình 6.1. Kiến trúc JDBC 163

164 Kiến trúc của JDBC gồm 2 tầng: tầng đầu tiên là các JDBC API, có nhiệm vụ chuyển các câu lệnh SQL cho bộ quản lý trình điều khiển JDBC; tầng thứ 2 là các JDBC Driver API, thực hiện nhiệm vụ liện hệ với trình điều khiển của hệ quản trị cơ sở dữ liệu cụ thể Các khái niệm cơ bản JDBC Driver Trình điều khiển JDBC là đoạn chương trình, do chính nhà xây dựng hệ quản trị CSDL hoặc do nhà cung ứng thứ ba cung cấp, có khả năng yêu cầu hệ quản trị CSDL cụ thể thực hiện các câu lệnh SQL. Danh sách các trình điều khiển JDBC cho các hệ quản trị CSDL khác nhau được Sun cung cấp và cập nhật tại địa chỉ: Các trình điều khiển JDBC được phân làm 04 loại khác nhau. Loại 1: có tên gọi là Bridge Driver.Trình điều khiển loại này kết nối với các hệ CSDL thông qua cầu nối ODBC. Đây là trình điều khiển được sử dụng phổ biến nhất trong những ngày đầu Java xuất hiện. Trình điều khiển loại này luôn được cung cấp kèm trong bộ J2SE với tên: sun.jdbc.odbc.jdbcodbcdriver Mã Java Java Application, Applet, Servlet JDBC Driver ODBC Mã Native Database Driver Database Hình 6.2. Trình điều khiển JDBC Driver Loại 2: Native API Driver. Trình điều khiển loại này sẽ chuyển các lời gọi của JDBC API sang thư viện hàm (API) tương ứng với từng hệ CSDL cụ thể. Trình điều khiện này thường do nhà xây dựng hệ CSDL cung cấp. Để có thể thi hành chương trình mã lệnh làm việc với hệ CSDL cụ thể cần phải được cung cấp đi kèm với chương trình. 164

165 Mã Java Java Application, Applet, Servlet JDBC Driver Database Driver Mã Native Database Hình 6.3. Trình điều khiển Native API Driver Loại 3: Có tên gọi là JDBC-Net Driver. Trình điều khiển loại này sẽ chuyển các lời gọi JDBC API sang một dạng chuẩn độc lập với các hệ CSDL, và sau được chuyển sang lời gọi của hệ CSDL cụ thể bởi 1 chương trình trung gian. Trình điều khiển của các nhà cung ứng thứ 3 thường thuộc loại này. Lợi thế của trình điều khiển loại này là không cần cung cấp mã lệnh kèm theo và có thể sử dụng cùng một trình điều khiển để truy cập đến nhiều hệ CSDL khác nhau. Mã Java Java Application, Applet, Servlet JDBC Driver Internet Mã Java Java Middle Ware Java Native Driver Database Hình 6.4. Trình điều khiển JDBC-Net Driver Loại 4: Native Protocol Driver. Trình điều khiển loại này chuyển các lời gọi JDBC API sang mã lệnh của CSDL cụ thể. Đây là trình điều khiển thuần Java, có nghĩa là không cần mã lệnh của hệ CSDL cụ thể khi thi hành chương trình. 165

166 Mã Java Java Application, Applet, Servlet JDBC Driver Internet Mã Java Java Native Driver Database JDBC URL Để kết nối với CSDL, chúng ta cần xác định nguồn dữ liệu cùng các thông số liên quan dưới dạng 1 URL như sau: Trong đó: Hình 6.5. Trình điều khiển Native Protocol Driver Jdbc : <subprotocol> : <dsn> : <others> <subprotocol>: được dùng để xác định trình điều khiển để kết nối với CSDL. <dsn>: địa chỉ CSDL. Cú pháp của <dsn> phụ thuộc vào từng trình điều khiển cụ thể. <other>: các tham số khác jdbc:odbc:dbname là URL để kết nối với CSDL tên dbname sử dụng cầu nối ODBC. jdbc:microsoft:sqlserver://hostname:1433 là URL để kết nối với CSDL Microsoft SQL Server. Trong đó hostname là tên máy cài SQL Server Kết nối cơ sở dữ liệu với JDBC Việc kết nối với CSDL bằng JDBC được thực hiện qua hai bước: Bước 1: Đăng ký trình điều khiển JDBC; Bước 2: Thực thi phương thức getconnection () của lớp DriverManager Đăng ký trình điều khiển Trình điều khiển JDBC được nạp khi mã bytecode được nạp vào JVM. Một cách đơn giản để thực hiện công việc này là thực thi phương thức Class.forName( <JDBC Driver> ). Để nạp trình điều khiển sử dụng cầu nối ODBC do Sun cung cấp, chúng ta sử dụng câu lệnh sau Class.forName( sun.jdbc.odbc.jdbcodbcdriver ). 166

167 Thực hiện kết nối Sau khi đã nạp trình điều khiển JDBC, việc kết nối với CSDL được thực hiện với một trong các phương thức sau trong lớp DriverManager: public static Connection getconnection(string url) throws SQLException: thực hiện kết nối với CSDL được yêu cầu. Bộ quản lý trình điều khiển sẽ tự động lựa chọn trình điều khiển phù hợp trong số các trình điều khiển đã được nạp. public static Connection getconnection(string url, String user, String pass) throws SQLException: kết nối tới CSDL với tài khoản user và mật mã pass. public static Connection getconnection(string url, Properties info) throws SQLException: tương tự hai phương thức trên ngoài ra cung cấp thêm các thông tin qui định thuộc tính kết nối thông qua đối tượng của lớp Properties. Kết quả trả về của các phương thức trên là một đối tượng của lớp Java.sql. Connection được dùng để đại diện cho kết nối đến CSDL. Tóm lại: Các bước cơ bản để kết nối với cơ sở dữ liệu từ một ứng dụng Java Bước 1: Nạp trình điều khiển try Class.forName( oracle.jdbc.driver.oracledriver ); catch(classnotfoundexception e) System.out.println( Loi nap trinh dieu khien: +e); Bước 2: Xác định URL cơ sở dữ liệu String host= dbhost.yourcompany.com ; String dbname= somename ; int port=1234; String oracaleurl= +host+ : +port+dbname; Bước 3: Thiết lập liên kết String username= hoan_td2001 ; String password= topsecret ; Connection con=drivermanager.getconnecton(oracleurl,username,password); Bước 4: Tạo ra một đối tượng Statement Statement s=con.createstatement(); 167

168 Bước 5: Xử lý truy vấn String q= Select col1, col2, col3 from sometable ; ResultSet rs=s.executequery(q); Bước 6: Xử lý kết quả while(rs.next()) System.out.println(rs.getString(1)+ + rs.getstring(2)+ + rs.getstring(3)); Cột đầu tiên có chỉ mục là 1 chứ không phải là 0. Bước 7: Đóng liên kết con.close(); Các ví dụ Trong phần ví dụ này tìm hiếu các cách khác nhau để kết nối với tập tin CSDL SQL Server JDB có một bảng tên Products. Bảng này gồm các cột ID, Name, và Number. Bảng Products được tạo như sau : Sau khi đã hoàn tất công việc tạo CSDL JDB, chúng ta có thể sử dụng đoạn mã sau để tiến hành kết nối CSDL. Ví dụ 6.1: package testconnection; import java.sql.connection; import java.sql.drivermanager; import java.sql.resultset; import java.sql.sqlexception; import java.sql.statement; 168

169 public class TestConnection private Connection con; public TestConnection() throws ClassNotFoundException, SQLException Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String url="jdbc:odbc:driver=sql Server; Server=VINHVU-PC\\SQLEXPRESS;Database=JDB;UserName=sa;Password=123456"; this.con=drivermanager.getconnection(url); public ResultSet GetData(String tbname) throws SQLException ResultSet kq = null; Statement statement = this.con.createstatement(); String sql = "select * from Products"; kq=statement.executequery(sql); return kq; public void Close() throws SQLException if (this.con!=null) this.con.close(); public static void main(string[] args) throws ClassNotFoundException, SQLException TestConnection pt = new TestConnection(); pt.updatedetail("products"); ResultSet rs = pt.getdata("products"); while (rs.next()) System.out.print(rs.getString("ID")+""+rs.getString("Name")+" "+rs.getstring("number")); System.out.println(""); pt.close(); Sau khi biên dịch, dữ liệu trong bảng Products được hiển thị ra màn hình : 169

170 Ví dụ 6.2: Một ví dụ khác về cách kết nối đến CSDL trên SQLSERVER package connectmysqldata; import java.sql.*; public class ConnectMysqlData public static void main(string[] args) throws ClassNotFoundException Connection conn = null; try String username = "root"; // user dang nhap vao MySQl String password = "1amclney"; // password dang nhap vao MySql String url = "jdbc:mysql:// :3306/qlsv"; Class.forName ("com.mysql.jdbc.driver").newinstance(); conn = DriverManager.getConnection(url,userName,passWord); //System.out.println("ke noi CSDl."); Statement s = conn.createstatement(); String sql = "SELECT * FROM sinhvien"; ResultSet rs = s.executequery(sql); while (rs.next()) String name = rs.getstring("ht"); int ns = rs.getint("ns"); String que = rs.getstring("que"); System.out.println("Ho ten:"+name + " Nam Sinh : "+ ns + Que : " + que); rs.close(); s.close(); conn.close(); System.out.println("ngăt ket noi."); 170

171 catch (Exception e) e.printstacktrace(); finally if(conn!=null) try conn.close(); catch(exception e) // Bo qua loi luc dong CSDL. Ví dụ 6.3: Cách kết nối CSDL oracle (kiểu 1) import java.sql.*; class DBOracle1 public static void main(string args[])throws ClassNotFoundException, SQLException try //Co the dung lenh nay de tai driver Class.forName("oracle.jdbc.OracleDriver"); DriverManager.registerDriver(new oracle.jdbc.driver.oracledriver()); //Lien ket toi co so du lieu Connection conn = "scott", "tiger"); Statement stmt = conn.createstatement( ); ResultSet rset = stmt.executequery("select empno, ename from emp"); ResultSetMetaData rst=rset.getmetadata(); int numcol=rst.getcolumncount(); 171

172 System.out.println("So cot cua bang la:"+numcol); System.out.println("Schema Name:" + rst.gettablename(1)); for(int i=1;i<numcol+1;i++) System.out.println(rst.getColumnName(i)+" "+rst.getcolumntypename(i)); while(rset.next( )) System.out.println(rset.getString("empno")); System.out.println(rset.getString("ename")); rset.close( ); stmt.close( ); conn.close( ); catch(exception e) System.err.println("Ex : "+e); 6.5. Cách tạo truy vấn và các kiểu truy vấn Các thao tác truy vấn CSDL chỉ có thể được thực hiện sau khi đã có đối tượng Connection, được tạo ra từ quá trình kết nối vào CSDL. Chúng ta sử dụng đối tượng của lớp Connection để tạo ra các thể hiện của lớp Java.sql.Statement. Sau khi tạo ra các đối tượng của lớp Statement thực hiện các thao tác trong các đối tượng statement trên connection tương ứng. Nội dung trong một statement chính là các câu SQL. Câu lệnh SQL trong các statement chỉ được thực hiện khi gửi chúng đến CSDL. Nếu câu lện SQL là một câu truy vấn nội dung thì kết quả trả về sẽ là một thể hiện của lớp Java.sql.ResultSet, ngược lại (các câu lệnh thay đổi nội dung CSDL) sẽ trả về kết quả là mộ số nguyên. Các đối tượng của lớp ResultSet cho phép truy cập đến kết quả trả về của các câu truy vấn. Các lớp cơ bản Java.sql.Statement Statement là một trong 3 lớp JDBC cơ bản dùng để thể hiện một câu lệnh SQL. Mọi thao tác trên CSDL được thực hiện thông qua 3 phương thức của lớp Statement. Phương thức executequery() nhận vào 1 tham số là chuỗi nội dung câu lện SQL và trả về 1 172

173 đối tượng kiểu ResultSet. Phương thức này được sử dụng trong các trường hợp câu lệnh SQL có trả về các kết quả trong CSDL. Phương thức executeupdate() nhận vào 1 tham số là chuỗi nội dung câu lệnh SQL. Tuy nhiên phương thức này chỉ sử dụng được đối với các câu lệnh cập nhật nội dung CSDL. Kết quả trả về là số dòng bị tác động bởi câu lệnh SQL. Phương thức execute() là trường hợp tổng quát của 2 phương thức trên. Phương thức nhận vào chuỗi nội dung câu lệnh SQL. Câu lệnh SQL có thể là câu lệnh truy vấn hoặc cập nhật. Nếu kết quả của câu lệnh là các dòng trong CSDL thì phương thức trả về giá trị true, ngược lại trả về giá trị false. Trong trường hợp giá trị true, sau đó có thể dùng phương thức getresultset() để lấy các dòng kết quả trả về. Java.sql.ResultSet Đối tượng resultset là các dòng dữ liệu trả về của câu lệnh truy vấn CSDL. Lớp này cung cấp các phương thức để rút trích các cột trong từng dòng kết quả trả về. Tất cả các phương thức này đều có dạng: type gettype(int String) Trong đó tham số có thể là số thứ tự của cột hoặc tên cột cần lấy nội dung. Tại 1 thời điểm chỉ có thể thao tác trên 1 dòng của resultset. Để thao tác trên dòng tiếp theo sử dụng phương thức next(). Phương thức trả về giá trị true trong trường hợp có dòng tiếp theo, ngược lại trả về giá trị false Ví dụ 6.4: Xây dựng lớp TestConnection. package testconnection; import java.sql.connection; import java.sql.drivermanager; import java.sql.resultset; import java.sql.sqlexception; import java.sql.statement; public class TestConnection private Connection con; public TestConnection() throws ClassNotFoundException,SQLException Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String url="jdbc:odbc:driver=sql Server; Server=VINHVU-PC\\SQLEXPRESS;Database=JDB;UserName=sa;Password=123456"; this.con=drivermanager.getconnection(url); public ResultSet GetData(String tbname) throws SQLException ResultSet kq = null; 173

174 Statement statement = this.con.createstatement(); String sql = "select * from Products"; kq=statement.executequery(sql); return kq; public void Update(int id,string name,int number) throws SQLException Statement sta = this.con.createstatement(); String sql1 ="Update Products set Name='"+name+"' where ID ="+id+""; String sql2 ="Update Products set Number="+number+" where ID ="+id+""; sta.executeupdate(sql1); sta.executeupdate(sql2); public void Insert(int id,string name,int number) throws SQLException Statement sta = this.con.createstatement(); String sql1 = "Insert Products values("+id+",'"+name+"',"+number+")"; sta.executeupdate(sql1); public void Delete(int id) throws SQLException Statement sta = this.con.createstatement(); String sql ="delete Products where ID ="+id+""; sta.executeupdate(sql); public void Close() throws SQLException if (this.con!=null) this.con.close(); Để quản lý CSDL tạo một JFrame như sau : 174

175 - Các jbutton: btngetdata, btncapnhat, btnthem, btnxoa, btnthoat - Các jtextfield: txtid, txtname, txtnumber - jtable: tbsp Để hiển thị CSDL từ bảng Products ra tbsp, viết sự kiện Click cho jbutton btngetdata như sau: private void btngetdatamouseclicked(java.awt.event.mouseevent evt) tblsp.removeall(); try TestConnection pt = new TestConnection(); ResultSet rs = pt.getdata("products"); int i=0; while (rs.next()) tblsp.setvalueat(rs.getstring("id"), i, 0); tblsp.setvalueat(rs.getstring("name"), i, 1); tblsp.setvalueat(rs.getstring("number"), i, 2); i++; pt.close(); catch (ClassNotFoundException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE, null, ex); catch (SQLException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE, ex); null, 175

176 Dữ liệu được hiển thị như sau: Cập nhật CSDL Phương thức Update() được định nghĩa trong lớp TestConnection: public void Update(int id,string name,int number) throws SQLException Statement sta = this.con.createstatement(); String sql1 ="Update Products set Name='"+name+"' where ID ="+id+""; String sql2 ="Update Products set Number="+number+" where ID ="+id+""; sta.executeupdate(sql1); sta.executeupdate(sql2); Ví dụ 6.5: Để cập nhật CSDL ta viết sự kiện cho jbutton btncapnhat như sau. private void btncapnhatmouseclicked(java.awt.event.mouseevent evt) try TestConnection pt = new TestConnection(); pt.update(integer.parseint(txtid.gettext()),txtname.gettext(),integer.parsei nt(txtnumber.gettext())); pt.close(); catch (ClassNotFoundException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE, null, ex); catch (SQLException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE, null, ex); 176

177 Phương thức Insert() được định nghĩa trong lớp TestConnection: public void Insert(int id,string name,int number) throws SQLException Statement sta = this.con.createstatement(); String sql1 = "Insert Products values ("+id+",'"+name+"',"+number+")"; sta.executeupdate(sql1); Ví dụ 6.6: Để thêm mới một bản ghi vào CSDL viết sự kiện cho btnthem như sau. private void btnthemmouseclicked(java.awt.event.mouseevent evt) try // TODO add your handling code here: TestConnection pt = new TestConnection(); pt.insert(integer.parseint(txtid.gettext()),txtname.gettext(), Integer.parseInt(txtnumber.getText())); btnthem.doclick(); catch (ClassNotFoundException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE,null, ex); catch (SQLException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE,null, ex); Phương thức Delete() được định nghĩa trong lớp TestConnection: public void Delete(int id) throws SQLException Statement sta = this.con.createstatement(); String sql ="delete Products where ID ="+id+""; sta.executeupdate(sql); Ví dụ 6.7: Để xóa một bản ghi trong CSDL viết sự kiện cho jbutton btnxoa như sau. private void btnxoamouseclicked(java.awt.event.mouseevent evt) try // TODO add )your handling code here: TestConnection pt = new TestConnection(); pt.delete(integer.parseint(txtid.gettext())); 177

178 catch (ClassNotFoundException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE,null, ex); catch (SQLException ex) Logger.getLogger(FrameConnection.class.getName()).log(Level.SEVERE,null, ex); 6.6. Mô tả Rowset, JDBCRowset và CatchedRowset Nếu cần thao tác với JavaBean, nên sử dụng Rowset Inteface. Rowset Interface cung cấp một tập hợp các thuộc tính của JavaBean cho phép một instance của RowSet được cấu hình để kết nối tới một Data source của JDBC và đọc dữ liệu từ Data source. Để truyền dữ liệu đầu vào tới thuộc tính command của một Rowset, sử dụng các phương thức setter bao gồm setint, setbytes, setstring, Command này là truy vấn SQL mà Rowset sử dụng khi nó lấy dữ liệu từ một Relational Database. JDBCRowset trong java bao gồm CachedRowSet, JdbcRowSet, WebRowSet, JoinRowSet, FilteredRowSet. Sử dụng RowSet sẽ giúp thực hiện các thao tác thêm, xoá và cập nhật một cách tiện lợi mà không cần phải viết các câu lệnh sql. CachedRowSet không yêu cầu phải duy trì một kết nối với cơ sở dữ liệu. Nó chỉ thực hiện kết nối khi có submit và điều này sẽ làm tăng hiệu suất cho cơ sở dữ liệu. CachedRowSet có thể thực hiện các hoạt động sau đây trên cơ sở dữ liệu - Insert: Để thực hiện thêm một dòng dữ liệu vào bảng, sử dụng phương thức movetoinsertrow() - Update: Phương thức updaterow():cập nhật một dòng dữ liệu của bảng - Delete: Sử dụng phương thức deleterow() để xoá một dòng dữ liệu của bảng. - Select: Một đối tượng CachedRowSet là có thể cuộn (scrollable) và điều này cho phép duyệt bản ghi theo nhiều cách. Một khi con trỏ nằm tại dòng mong muốn, các phương thức getter có thể được gọi để lấy các giá trị của các cột. 178

179 CÂU HỎI ÔN TẬP, THẢO LUẬN 1. JDBC là gì? 2. Trình bày chức năng và kiến trúc của JDBC trong Java? 3. Trình bày các mô hình hoạt động của JDBC? 4. Tìm hiểu cơ chế hoạt động của JDBC? 5. Trình bày các bước lập trình truy xuất cơ sở dữ liệu với JDBC? 6. Tìm hiểu đối tượng Statement( Định nghĩa, chức năng, các phương thức).? Cho ví dụ minh họa? 7. Tìm hiểu về đối tượng Resultset( Định nghĩa, chức năng, các kiểu Resultset, các phương thức, cơ chế hoạt động).? Cho ví dụ minh họa? 8. Tìm hiểu về đối tượng Rowset ( Định nghĩa, chức năng, các phương thức).? Cho ví dụ minh họa? 9. Tìm hiểu về đối tượng Metadata ( Định nghĩa, chức năng, các phương thức).? Cho ví dụ minh họa? 10. Tìm hiểu đối tượng CallableStatement( Định nghĩa, chức năng, các phương thức)? Cho ví dụ minh họa? 11. Tìm hiểu tổng quan về SQL? Các câu lệnh truy vấn dữ liệu? ví dụ minh họa? 12. Tìm hiểu cách kết nối và thực hiện các câu lệnh truy vấn CSDL giữa Java với SQL server? Cho ví dụ minh họa? 13. Giải thích các câu lệnh sau? public void connecttoandquerydatabase(string username, String password) Connection con = DriverManager.getConnection( "jdbc:mydriver:mydatabase",username, password); Statement stmt = con.createstatement(); ResultSet rs = stmt.executequery("select a, b, c FROM Table1"); while (rs.next()) int x = rs.getint("a"); String s = rs.getstring("b"); float f = rs.getfloat("c"); 179

180 BÀI TẬP ỨNG DỤNG Câu 1. Thực hiện demo về kết nối CSDL và thực thi các phưong thức của đối tượng Statement: -executequery - executeupdate - execute Câu 2. Cài đặt lớp NhanVien quản lý thông tin về nhân viên bao gồm: - Mã nhân viên - Họ tên nhân viên - Năm sinh - Hệ số lương - Lương cơ bản (thuộc tính lớp) Viết các phương thức khởi tạo tương ứng. Câu 3. Viết chương trình cài đặt lớp QuanLyNhanVien với các phương thức: - Nhập thông tin nhân viên (Không cho phép 2 nhân viên được trùng id). - Kiểm tra sự tồn tại của nhân viên dựa vào mã nhân viên. - Tính lương nhân viên. - Tìm nhân viên có lương cao nhất - Hiển thị thông tin nhân viên. - In bảng lương nhân viên. Câu 4. Viết chương trình thực hiện xây dựng menu như sau để cho phép người dùng lựa chọn các chức năng của quản lý nhân viên: - 1. Nhập thông tin nhân viên Tìm nhân viên có lương cao nhất In bảng lương tháng cho nhân viên Kết thúc. Câu 5.Viết ứng dụng kết hợp JDBC và API, tạo và kết nối CSDL Add row to table: Thêm một hàng vào bảng mỗi khi có mặt hàng mới thêm vào. Update database: Cập nhật bảng COFFES dựa trên dữ liệu trong bảng. 180

181 Discard changes: Trích rút nội dung của bảng COFFES, thay thế dữ liệu trong bảng dữ liệu. Câu 6. Tạo 1 database có tên EmployeeManagement. Tạo 1 table có tên Employees gồm các trường như sau: - EmpID int primary key - EmpName nvarchar(50) - BirthDate datetime, - Address nvarchar(250) Xây dựng 1 chương trình java sử dụng database trên và có các chức năng: 1. Nhập mới 1 nhân viên 2. Sửa thông tin nhân viên 3. Xoá thông tin nhân viên 4. Hiển thị toàn bộ thông tin nhân viên. Đưa ra màn hình tổng số nhân viên có trong database 5. Tìm kiếm 1 nhân viên theo tên và hiển thị tổng số nhân viên tìm thấy 6. Exit Câu 7. Cho DB có tên là APJ2_Assignment, có dạng như sau, dữ liệu đi kèm với file sql Assignment1.sql Sử dụng netbeans tạo giao diện như sau 181

182 Yêu cầu a. Ngay khi form được load lên, phải lấy được danh sách các Manufacturer ra và đưa vào trong combobox, lưu ý lúc hiển thị thì sử dụng tên, nhưng vẫn phải lưu được Id của manufacturer b. Khi bấm nút add new, sẽ thực hiện validate dữ liệu như sau i. Tất cả các trường phải được nhập ii. Nếu đã nhập đủ, kiểm tra productid đã tồn tại trong bảng Product hay chưa, 1. nếu tồn tại rồi thì báo lỗi, 2. nếu chưa mới cho insert vào db c. Khi bấm vào nút show sẽ đưa dữ liệu từ cả 2 bảng vào table phía dưới với dữ liệu tương ứng nhau (hiển thị tên manufacturer tương ứng với manufacturerid) d. Khi bấm nút find hiện dialog sau: Tương ứng với Id nhập vào, tìm kiếm trong DB 1. Nếu có productid đó thì hiện duy nhất dữ liệu của bản ghi đó vào bảng 2. Nếu không có productid đó thì hiện thông báo lỗi : Data not found. Câu 8. Cho DB có 1 bảng danh sách học viên với 3 cột Id, Ten, GioiTinh. Yêu cầu có ít nhất 5 bản ghi với các giới tính khác nhau (nam/nữ) a. Viết stored procedure SP_StudentQuery dữ liệu từ bảng DanhSachHocVien ra, stored procedure này có 3 tham số truyền vào INT. Yêu cầu 3 tham số này truyền vào được và lấy ra được dữ liệu từ bên trong stored procedure b. Viết class java bình thường, có hàm main, sử dụng JDBC với câu lệnh CallableStatement. Yêu cầu gọi đến SP_StudentQuery, lấy ra được danh sách học viên (resultset). Ngoài ra cũng sử dụng các tham số truyền vào để lấy ra được tổng số lượng học viên, số lượng nữ, số lượng nam và in ra màn hình. c. Với ResultSet học viên trả về, và với số lượng học viên đã lấy được là n, in ra thông tin của học viên n-1 và n-2 (nếu có) 182

183 CHƯƠNG 7. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI SOCKET Mục đích Chương này giới thiệu về lập trình mạng với socket sử dụng các lớp được cung cấp trong JAVA như: TCP socket, UDP Socket, cách lấy dữ liệu từ website, cách gửi và nhận Giới thiệu chung Lập trình ứng dụng mạng với socket là kỹ thuật được sử dụng phổ biến trong thực tế. Các ngôn ngữ lập trình hầu hết đều có thư viện hỗ trợ lập trình với socket như: Ngôn ngữ c/c++ có thư viện socket, VB có thư viện WinSock, C# có thư viện system.socket...trong Java các lớp thư viện hỗ trợ lập trình với socket hầu hết nằm trong gói java.net. Khi phát triển các ứng dụng mạng thì java và.net là hai ngôn ngữ hỗ trợ rất mạnh đối với socket sử dụng giao thức TCP (TCPsocket) và UDP (UDPsocket). Trong chương này giới thiệu lập trình ứng dụng mạng sử dụng TCPSocket, UDPSocket sử dụng các gói như java.net, java.io được cung cấp sẵn trong Java. Các lớp quan trong nhất trong gói java.net gồm 6 lớp: InetAddress, ServerSocket, Socket, DatagramPacket, DatagramSocket, URL. Với 6 lớp này Java cho phép phát triển tất cả các ứng dụng mạng từ ứng dụng đơn giản cho đến phức tạp, từ các ứng dụng nhỏ đến các ứng dụng lớn. Sau đây chúng ta sẽ khảo sát những kỹ thuật lập trình mạng cơ bản nhất sử dụng socket trong Java. Vấn đề lập trình mạng có thể được định nghĩa với công thức sau: LTM=KTM+MH+NN Trong đó: LTM: Lập trình mạng KTM: Kiến thức mạng truyền thông (mạng máy tính, PSTN...) MH: Mô hình lập trình mạng NN: Ngôn ngữ lập trình 7.2. Lập trình thao tác với địa chỉ máy trạm Lập trình thao tác với địa chỉ IP, lớp URL Connection Lớp InetAddress Java có các lớp quan trọng để thao tác với địa chỉ IP trong gói java.net. Lớp quan trọng nhất là lớp InetAddress. Lớp này cho phép lấy địa chỉ của một máy trạm bất kỳ trên mạng và cho phép dễ dàng hoán chuyển giữa địa chỉ IP và tên của một máy trạm (host). Mỗi đối tượng InetAddress chứa 2 thành phần chính của một máy trạm là hostname và địa chỉ IP của máy trạm đó.. 183

184 Hình 7.1. Lớp kế thừa từ lớp InetAddress và SocketAddress Lớp InetAddress được sử dụng phổ biến trong các lớp Socket, ServerSocket, URL, DatagramSocket, DatagramPacket và nó được kế thừa từ lớp Object: public class InetAddress extends Object implements Serializable Tóm tắt các phương thức của lớp Inet Address boolean equals(object obj) So sánh đối tượng với đối tượng ob Byte [] static InetAddress[] getaddress() Trả về địa chỉ IP chứa trong đối tượng InetAddress dạng mảng byte getallbyname(string host) Trả về mảng địa chỉ của tất cả các máy trạm có cùng tên trên mạng static InetAddress getbyaddress(byte[] addr) Trả về đối tượng InetAddress tương ứng với địa chỉ IP truyền cho phương thức dưới dạng mảng byte static InetAddress getbyaddress(string host, byte[] addr) Tạo đối tượng InetAddress dựa trên tên và địa chỉ IP static InetAddress getbyname(string host) Xác định địa chỉ IP của máy trạm từ tên của máy trạm (host) String String String static InetAdd getcanonicalhostname() Lấy tên miền của địa chỉ IP gethostaddress() Trả về địa chỉ IP chứa trong đối tượng INetAddress là chuỗi dạng a.b.c.d gethostname() Trả về tên máy trạm chưa trong đối tượng getlocalhost() Lấy đối tượng InetAddress của máy cục bộ int hashcode() Trả về hashcode của địa chỉ IP cục thể Đặc điểm của lớp InetAddress là lớp không có hàm tạo nên không thể tạo ra đối tượng InetAddress bằng toán tử new. Nhưng bù lại, lớp InetAddress có một số phương thức có thuộc tính static cho phép lấy địa chỉ của máy trạm bất kỳ trên mạng, cụ thể: 184

185 Kiểu boolean boolean boolean boolean boolean boolean boolean String isanylocaladdress() Tên phương thức Kiểm tra địa chỉ InetAddress có phải địa chỉ wildcard không? islinklocaladdress() Kiểm tra địa chỉ có phải là một địa chỉ link-local hay không. isloopbackaddress() Kiểm tra địa chỉ có phải là địa chỉ Loopback không. ismcglobal() Kiểm tra địa chỉ multicast có phạm vi toàn cục hay không? ismclinklocal() Kiểm tra địa chỉ multicast có phải là địa chỉ có phạm vi liên kết hay không? ismcnodelocal() Kiểm tra địa chỉ multicast có phải là địa chỉ phạm vi nút mạng hay không? ismulticastaddress() Kiểm tra địa chỉ InetAddress có phải là địa chỉ IP multicast hay không. tostring()chuyển địa chỉ IP thành chuỗi. Phương thức getbyname(): public static InetAddress getbyname(string hostname) throws UnknownHostException Phương thức này cho phép trả về địa chỉ của một máy trạm bất kỳ trên mạng được chỉ ra bởi tham số hostname. Tham số này có thể PCname, là tên miền DNS hoặc địa chỉ IP. Trong trường hợp không tồn tại máy trạm có tên chỉ ra trên mạng, phương thức ném trả về ngoại lệ UnknownHostException. Ví dụ 7.1 Đoạn chương trình sau để lấy địa chỉ của máy trạm có tên miền là và hiển thị địa chỉ ra màn hình: try InetAddress address = InetAddress.getByName (" System.out.println (address); catch (UnknownHostException ex) System.out.println("Could not find 185

186 Lệnh InetAddress.getByName() sử dụng được do phương thức getbyname() có thuộc tính static. Nếu máy trạm với tên miền chỉ ra không tồn tại thì ngoại lệ UnknownHostException được trả về và xử lý. Phương thức getallbyname(): Phương thức này cho phép trả về địa chỉ của tất cả các máy trạm có cùng tên trên mạng dưới dạng là một mảng đối tượng InetAddress. Phương thức có cú pháp như sau: InetAddress[] addresses = InetAddress.getAllByName(String name) throws UnknownHostException Ví dụ 7.2: In ra địa chỉ của tất cả các máy trạm trên mạng mà có cùng tên miền import java.net.*; public class AllAddr public static voidmain(string[]args) try InetAddress[] addresses = InetAddress.getAllByName (" ; for (int i = 0; i < addresses.length; i++) System.out.println (addresses[i]); catch (UnknownHostException ex) System.out.println("Could not find Dịch chạy chương trình trên máy tính có kết nối mạng Internet, kết quả trả về như sau: Phương thức getlocalhost():phương thức này cho phép trả về địa chỉ của máy cục bộ, nếu không tìm thấy trả về ngoại lệ tượng tự như phương thức getbyname(). 186

187 Cú pháp: public static InetAddress getlocalhost() throws UnknownHostException Ngoài các phương thức static trên, một số phương thức khác cho phép trả về địa chỉ IP hoặc tên của một máy trạm từ đối tượng InetAddress của máy trạm sau khi đã lấy được địa chỉ của máy trạm. Các phương thức tiêu biểu là: Phương thức gethosname():trả về tên máy trạm từ đối tượng InetAddress của máy trạm đó. Cú pháp: public String gethostname() Ví dụ 7.3: Cho địa chỉ, in ra tên máy trạm: import java.net.*; public class ReverseTest public static void main (String[] args) try InetAddress ia = InetAddress.getByName(" "); System.out.println(ia.getHostName( )); catch (Exception ex) System.. err.println (ex) ; Phương thức gethostaddress(): Trả về địa chỉ IP của máy trạm từ đối tượng InetAddress tương ứng là chuỗi địa chỉ dạng a.b.c.d. Cú pháp: public String gethostaddress() Ví dụ 7.4: In ra địa chỉ IP của máy cục bộ import java.net.*; public class MyAddress public static void main(string[] args) try InetAddress me = InetAddress.getLocalHost( ); String dottedquad = me.gethostaddress( ); System.out.println("My address is " + dottedquad); catch (UnknownHostException ex) System.out.println("I'm sorry. I don't know my own address."); 187

188 Phương thức getaddress(): Trả về địa chỉ IP của máy trạm từ đối tượng InetAddress của máy trạm tương ứng dưới dạng mảng byte. Cú pháp: public byte[] getaddress() Ví dụ 7.5: Phương thức getversion() lấy phiên bản địa chỉ IP của máy cục bộ: import java.net.*; public class AddressTests public static int getversion(inetaddress ia) byte[] address = ia.getaddress( ); if (address.length == 4) return 4; else if (address.length == 6) return 6; else return -1; Lưu ý: Khi in ra các byte địa chỉ IP, nếu giá trị của byte địa chỉ mà vượt qua 127 thi phải cộng với 256 để ra giá trị đúng (vì kiểu byte chỉ có giá trị trong khoảng từ 0128 đến +127), nếu không nó sẽ có giá trị âm. Ví dụ với mảng address trong ví dụ trên: for (int i=0;i<address.length;i++) System.out.println((address[i]>0)?address[i]: (address[i]+256)); Các phương thức khác của lớp InetAddress: - public boolean isanylocaladdress(): Phương thức này trả về giá trị true với địa chỉ wildcard, false nếu không phải. Địa chỉ wildcard trùng hợp với bất cứ địa chỉ nào của máy cục bộ. Phương thức này quan trọng nếu hệ thống cục bộ có nhiều card giao diện mạng, nhất là đối với server và gateway. Trong IPv4, địa chỉ wildcard là , trong IPv6 là 0:0:0:0:0:0:0:0. - public boolean isloopbackaddress(): Phương thức này kiểm tra một địa chỉ có phải là loopback hay không, nếu không phải trả về false. Địa chỉ loopback kết nối trực tiếp trong máy trạm trong lớp IP mà không sử dụng bất kỳ phần cứng vật lý nào. Với IPv4, địa chỉ loopback là , với IPv6 là 0:0:0:0:0:0:0:1. - public boolean islinklocaladdress(): Phương thức này trả về giá trị true nếu một địa chỉ là địa chỉ link-local IPv6, nếu không phải thì trả về giá trị false. Địa chỉ link-local là địa chỉ chỉ được hỗ trợ trong mạng IPv6 để tự cấu hình, tương tự như DHCP trên mạng IPv4 nhưng không cần server. 188

189 - public boolean ismulticastaddress(): Trả về true nếu địa chỉ là multicast, nếu không trả về giá trị false. Trong IPv4, địa chỉ multicast nằm trong dải địa chỉ IP: > (lớp D), trong IPv6 thì các địa chỉ này được bắt đầu với byte có giá trị FF Ví dụ sử dụng các phương thức lớp InetAddress Chương trình sau cho phép sử dụng các phương thức của lớp InetAddresss để hiển thị các đặc trưng của một địa chỉ IP được nhập vào từ trên dòng lệnh. import java.net.*; public class IPCharacteristics public static void try 189 main(string[]args) InetAddress address = InetAddress.getByName(args[0]); if (address.isanylocaladdress( )) System.out.println(address + " is a wildcard address."); if (address.isloopbackaddress( )) System.out.println(address + " is loopback address."); if (address.islinklocaladdress( )) System.out.println(address + " is a link-local address."); else if (address.issitelocaladdress( )) System.out.println(address + " is a site-local address."); else System.out.println(address + " is a global address."); if (address.ismulticastaddress( )) if (address.ismcglobal( )) System.out.println(address + " is a global multicast address.");

190 else if (address.ismcorglocal( )) System.out.println (address + " is an organization wide multicast address."); else if (address.ismcsitelocal( )) System.out.println(address+"is a site widemulticast address. ") ; else if (address.ismclinklocal( )) System.out.println (address + " is a subnet wide multicast address. ") ; else if (address.ismcnodelocal( )) System.out.println (address + " is an interface-local multicast address."); else System.out.println(address + " is an unknown multicast address type. ") ; else System.out.println(address + " is a unicast address."); catch (UnknownHostException ex) System.err.println("Could not resolve " + args[0]); Sau khi biên dịch chương trình, chạy chương trình với lệnh: java IPCharacteristics <addresss> [Enter] 7.3. Lập trình ứng dụng mạng với TCP socket Lớp Socket và lớp Server Socket Lớp Socket Lớp Socket dùng để tạo đối tượng socket cho phép truyền thông với giao thức TCP. (Với giao thức UDP sử dụng lớp DatagramSocket thay vì lớp Socket). Các hàm tạo: 190

191 public Socket(String host, int port) throws UnknownHostException, IOException Hàm tạo này cho phép tạo ra đối tượng Socket truyền thông với giao thức TCP và thực hiện kết nối với máy trạm từ xa có địa chỉ và số cổng được chỉ ra bởi tham số host và port tương ứng. Tham số host có thể là tên máy trạm, tên miền hoặc địa chỉ IP. Nếu không tìm thấy máy trạm từ xa hoặc đối tuợng Socket không được mở thì nó ném trả về ngoại lệ UnknownHostException hoặc IOException. Ví dụ 7.6: Đoạn chương trình sau cho phép mở socket và kết nối tới máy trạm từ xa có tên miền và số cổng là 80. try Socket toyahoo = new Socket(" 80); // Hoạt động gửi /nhận dữ liệu catch (UnknownHostException ex) System.err.println(ex); catch (IOException ex) System.err.println(ex); public Socket(InetAddress host, int port) throws IOException Hàm tạo này tương tự như hàm tạo trên, nhưng tham số thứ nhất là đối tượng InetAddress của máy trạm từ xa. Đối tượng InetAddress của máy trạm từ xa có thể lấy được bằng phương thức getbyname() của lớp InetAddress. public Socket(String host, int port, InetAddress interface, int localport) throws IOException, UnknownHostException Hàm tạo này cho phép tạo ra đối tượng Socket và kết nối với máy trạm từ xa. Hai tham số đầu là tên và số cổng của máy trạm từ xa, 2 tham số sau là giao diện mạng vật lý (NIC) hoặc ảo và số cổng được sử dụng trên máy cục bộ. Nếu số cổng cục bộ localport mà bằng 0 thì Java sẽ chọn sử dụng một số cổng cho phép ngẫu nhiên trong khoảng 1024 đến public Socket(InetAddress host, int port, InetAddress interface, int localport) throws IOException Tương tự như hàm tạo trên, nhưng tham số thứ nhất là đối tượng InetAddress của máy trạm từ xa. 191

192 protected Socket( ) Hàm tạo này tạo đối tượng socket mà không kết nối với máy trạm từ xa. Hàm tạo này được sử dụng khi chương trình có các socket lớp con. Một số phương thức quan trọng của lớp Socket public InetAddress getinetaddress(): Phương thức cho phép trả về địa chỉ của máy trạm từ xa hiện đang kết nối với socket. public int getport(): Trả về số cổng trên máy trạm từ xa mà hiện đang kết nối với socket. public int getlocalport() : Trả về số cổng trên máy cục bộ public InputStream getinputstream() throws IOException: Trả về luồng nhập của socket là đối tượng InputStream. public OutputStream getoutputstream() throws IOException: Trả về luồng xuất của socket là đối tượng OutputStream. public void close() throws IOException: Đóng socket Thiết đặt các tuỳ chọn Socket Tuỳ chọn socket chỉ ra làm thế nào lớp Java Socket có thể gửi /nhận dữ liệu trên native socket. Socket két có các tuỳ chọn sau: TCP_NODELAY SO_BINDADDR SO_TIMEOUT SO_LINGER Để thiết lập các tuỳ chọn và trả về trạng thái các tuỳ chọn, lớp socket có các phương thức tương ứng. Ví dụ để thiết đặt và trả về trạng thái tuỳ chọn TCP_NODELAY, lớp Socket có các phương thức sau: 192 public void settcpnodelay(boolean on) throws SocketException public boolean gettcpnodelay() throws SocketException Lớp ServerSocket Lớp ServerSocket cho phép tạo đối tượng socket phía server và truyền thông với giao thức TCP. Sau khi được tạo ra, nó được đặt ở trạng thái lắng nghe (trạng thái thụ động) chờ tín hiệu kết nới gửi tới từ client. Các hàm tạo public ServerSocket(int port) throws BindException, IOException Hàm tạo này cho phép tạo ra đối tượng ServerSocket với số cổng xác định được chỉ ra bởi tham số port. Nếu số cổng port=0 thì cho phép sử dụng một số cổng nặc danh nào

193 đó (anonymous port ). Hàm tạo trả về ngoại lệ khi socket không thể tạo ra được. Socket được tạo bởi hàm tạo này cho phép đáp ứng cực đại tới 50 kết nối đồng thời. public ServerSocket(int port, int queuelength) throws IOException, BindException Tương tự như hàm tạo trên nhưng cho phép chỉ ra số kết nối cực đại mà socket có thể đáp ứng đồng thời bởi tham số queuelenth. public ServerSocket() throws IOException Hàm tạo này cho phép tạo đối tượng ServerSocket nhưng không gắn kết thực sự socket với một số cổng cụ thể nào cả. Và như vậy nó sẽ không thể chấp nhận bất cứ kết nối nào gửi tới. Nó sẽ được gắn kết địa chỉ sau sử dụng phương thức bind(). Ví dụ 7.7: ServerSocket ss = new ServerSocket( ); SocketAddress http = new InetSocketAddress(80); ss.bind (http) ; Một số phương thức Phương thức accept() throws IOException Phương thức này khi thực hiện nó đặt đối tượng ServerSocket ở trạng thái nghe tại số cổng xác định chờ tín hiệu kết nối gửi đến từ client. Khi có tín hiệu kết nối gửi tới phương thức sẽ trả về đối tượng Socket mới để phuc vụ kết nối đó. Khi xảy ra lỗi nhập/xuất, phương thức sẽ trả về ngoại lệ IOException. Ví dụ 7.8: ServerSocket server = new ServerSocket(5776); while (true) Socket connection = server.accept( ); OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream ( )); out.write("you've connected to this server. Bye-bye no\n"); connection.close( ); Phương thức close() Cú pháp: public void close() throws IOException Phương thức này cho phép đóng soccket và giải phóng tài nguyên cấp cho socket. 193

194 Kỹ thuật lập trình truyền thông với giao thức TCP Trong chương trình ứng dụng mạng xây dựng theo mô hình client/server, để chương trình client và chương trình server có thể truyền thông được với nhau mỗi phía phải thực hiện các thao tác cơ bản sau đây (Hình 6.4): Chương trình phía server: 194 Hình 7.2. Quá trình khởi tạo truyền thông với TCPSocket Tạo đối tượng ServerSocket với một số hiệu cổng xác định Đặt đối tượng ServerSocket ở trạng thái nghe tín hiệu đến kết nối bằng phương thức accept(). Nếu có tín hiệu đến kết nối phương thức accept() tạo ra đối tượng Socket mới để phục vụ kết nối đó. Khai báo luồng nhập/xuất cho đối tượng Socket mới (tạo ra ở bước trên). Luồng nhập/xuất có thể là luồng kiểu byte hoặc kiểu char. Thực hiện truyền dữ liệu với client thông qua luồng nhập/xuất Server hoặc client hoặc cả 2 đóng kết nối Server trở về bước 2 và đợi kết nối tiếp theo Chương trình client Tạo đối tượng Socket và thiết lập kết nối tới server bằng cách chỉ ra các tham số của server. Khai báo lưồng nhập/xuất cho Socket. Luồng nhập/xuất có thể là luồng kiểu byte hoặc kiểu char. Lưu ý: Thực hiện truyền dữ liệu qua mạng thông qua luồng nhập/xuất Đóng Socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần. - Bình thường chương trình server luôn chạy trước chương trình client. - Một chương trình server có thể phục vụ nhiều client đồng thời hoặc lặp. Ví dụ 7.9:

195 import java.io.*; import java.net.*; public class EchoClient public static void main(string[] args) throws IOException Socket echosocket = null; PrintWriter out = null; BufferedReader in = null; try echosocket = new Socket("taranis", 7); out = new PrintWriter(echoSocket.getOutputStream (),true); in = new BufferedReader(new InputStreamReader(echoSocket. getinputstream ())); catch (UnknownHostEx' ption e) System.out.println("Don't know about host: taranis."); System.exit(1); catch (IOException e) System.out.println("Couldn't get I/O for "+ "the connection to: taranis."); System.exit(1); BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); String userinput; while ((userinput = stdin.readline())!= null) out.println (userinput); System.out.println("echo: " + in.readline()); out. close (); in. close (); stdin. close (); echosocket. close (); 195

196 Luồng nhập/xuất mạng và đọc/ghi dữ liệu qua luồng nhập/xuất Luồng nhập/xuất mạng cho phép chương trình client và server trao đổi dữ liệu với nhau qua mạng. Luồng nhập/xuất của socket có thể là luồng kiểu byte hoặc kiểu ký tự. Ở đây chúng tôi nêu một cách thông dụng nhất tạo luồng kiểu byte và kiểu ký tự để chương trình thực hiện đọc ghi dữ liệu với mạng. Luồng kiểu byte Giả sử đối tượng Socket được tạo ra với biến tham chiếu là cl. - Với luồng nhập: + Tạo luồng nhập cho socket: InputStream inp=cl.getinputstream(); + Đọc dữ liệu: Có ba cách 196 -/ Đọc mỗi lần một byte: inp.read() -/Đọc một khối dữ liệu và cất vào mảng b: - Với luồng xuất: byte [] b=new byte[1024]; inp.read(b) hoặc inp.read(b,offset, len) +Tạo luồng xuất: OutputStream outp=cl.getoutputstream(); + Viết dữ liệu: -/Viết mỗi lần một byte b: outp.write(b); -/ Viết cả khối dữ liệu chứa trong mảng b kiểu byte: //byte[] b; outp.write(b) hoặc outp.write(b,offset,len); Luồng kiểu char: - Với luồng nhập: +Tạo luồng nhập: BufferedReader inp=new BuferedReader(new InputStreamReader(cl.ge tlnputstream ())); + Đọc dữ liệu: -/Đọc từng ký tự: int ch=inp.read() -/ Đọc chuỗi: String s=inp.readline(); - Với luồng xuất: + Tạo luồng xuất:

197 PrintWriter outp=new PrintWriter(cl.getOutputStream(), true); + Viết dữ liệu: outp.println(<data>); Một số ví dụ Ví dụ 7.10: Chương trình quét cổng sử dụng Socket import java.net.*; import java.io.*; public class PortScanner public static void main(string[] args) String host = "localhost"; if (args.length > 0) host = args[0]; try InetAddress theaddress =InetAddress.getByName(host); for (int i = 1; i < 65536; i++) Socket connection = null; try connection = new Socket(host, i); System.out.println("There is a server on port " + i + " of " + host); catch (IOException ex) // must not be a server on this port finally try if (connection!= null) connection.close( ); catch (IOException ex) // end for // end try 197

198 catch (UnknownHostException ex) System.err.println (ex); // end main // end PortScanner Ví dụ 7.11: Chương trình quét cổng cục bộ dùng lớp ServerSocket import java.net.*; import java.io.*; public class LocalPortScanner public static void main(string[] args) for (int port = 1; port <= 65535; port++) try ServerSocket server = new ServerSocket (port); catch (IOException ex) System.out.println( "There is a server on port " + port + "."); // end catch // end for Ví dụ 7.12: Chương trình finger client Finger là một giao thức truyền thẳng theo RFC 1288, client tạo kết nối TCP tới server với số cổng 79 và gửi một truy vấn on-line tới server. Server đáp ứng truy vấn và đóng kết nối. import java.net.*; import java.io.*; public class FingerClient public final static int DEFAULT PORT = 79; public static void main(string[] args) 198

199 String hostname = "localhost"; try hostname = args[0]; catch (ArrayIndexOutOfBoundsException ex) hostname = "localhost"; Socket connection = null; try connection = new Socket (hostname, DEFAULT PORT); Writer out = new OutputStreamWriter(connection.getOutputStream ( ),"8859 1"); for (int i = 1; i < args.length; i++) out.write(args[i] + " "); out. write ("\r\n") ; out.flush( ); InputStream raw = connection.getinputstream ( ); BufferedInputStream buffer = new BufferedlnputStream(raw); InputStreamReader in = new InputStreamReader(buffer,"88591"); int c; while ((c = in.read( ))! = -1) if ((c >= 32 && c < 127 c == '\t' c == '\r' c == '\n') System.out.write (c) ; catch (IOException ex) System.err.println(ex); finally try if (connection!= null) connection.close( ); 199

200 catch (IOException ex) 7.4. Lập trình ứng dụng mạng với UDP Socket Một số lớp Java hỗ trợ lập trình với UDP Socket Lớp DatagramPacket Lớp này cho phép tạo gói tin truyền thông với giao thức UDP, kế thừa trực tiếp từ lớp Object. public final class DatagramPacket extends Object Gói tin là đối tượng của lớp này chứa 4 thành phần quan trọng: Địa chỉ, dữ liệu truyền thật sự, kích thước của gói tin và số hiệu cổng chứa trong gói tin. Các hàm tạo - Hàm tạo tạo gói tin nhận từ mạng: public DatagramPacket(byte[] inbuffer, int length) Tham số: inbuffer: Bộ đệm nhập, chứa dữ liệu của gói tin nhận length: kích cỡ của dữ liệu của gói tin nhận, nó thường được xác định bằng lệnh: length= buffer.length. Tạo gói tin nhận: byte[] inbuff=new byte[512];//bộ đệm nhập DatagramPacket indata=new DatagramPacket (inbuf, inbuff.length); - Hàm tạo tạo gói tin gửi: public DatagramPacket(byte[] outbuffer, int length, InetAddress destination, int port) Tham số: outbuffer: Bộ đệm xuất chưa dữ liệu của gói tin gửi length: Kích cỡ dữ liệu của gói tin gửi tính theo số byte và thường bằng outbuffer.length. destination: Địa chỉ nơi nhận gói tin. port: Số hiệu cổng đích, nơi nhận gói tin. Ví dụ 7.13: 200

201 String s=" Hello World!"; //Bộ đệm xuất và gán dữ liệu cho bộ đệm xuất byte[] outbuff=s.getbytes(); //Địa chỉ đích InetAddressaddrDest=InetAddress.getByName("localhost"); //Số cổng đích int portdest=3456; //Tạo gói tin gửi DatagramPacket outdata=new DatagramPacket(outBuff, outbuff.length, addrdest, portdest); Các phương thức public InetAddress getaddress(): Phương thức này trả về đối tượng InetAddress của máy trạm từ xa chứa trong gói tin nhận. public int getport(): Trả về số hiệu cổng của máy trạm từ xa chứa trong gói tin. public byte[] getdata() : Trả về dữ liệu chứa trong gói tin dưới dạng mảng byte. public int getlength(): Trả về kích cỡ của dữ liệu chưa trong gói tin tính theo số byte. Tương ứng với 4 phương thức getxxxx..(), lớp DatagramPacket có 4 phương thức setxxxx..() để thiết lập 4 tham số cho gói tin gửi Lớp DatagramSocket Lớp DatagramSocket cho phép tạo ra đối tượng socket truyền thông với giao thức UDP. Socket này cho phép gửi/nhận gói tin DatagramPacket. Lớp này được khai báo kế thừa từ lớp Object. public class DatagramSocket extends Object Các hàm tạo public DatagramSocket() throws SocketException: Hàm tạo này cho phép tạo ra socket với số cổng nào đó(anonymous) và thường được sử dụng phía chương trình client. Nếu tạo socket không thành công, nó ném trả về ngoại lệ SocketException. Ví dụ 7.14: try DatagramSocket client = new DatagramSocket( ); // send packets... catch (SocketException ex) 201

202 System.err.println(ex) ; public DatagramSocket(int port) throws SocketException: Hàm tạo này cho phép tạo socket với số cổng xác định và chờ nhận gói tín truyền tới. Hàm tạo này được sử dụng phía server trong mô hình client/server. Ví dụ 7.15: Chương trình sau sẽ cho phép hiển thị các cổng cục bộ đã được sử dụng: import java.net.*; public class UDPPortScanner public static void main(string[] args) for (int port = 1024; port <= 65535; port++) try // the next line will fail and drop into the catch block if // there is already a server running on port i DatagramSocket server = new DatagramSocket(port); server.close( ); catch (SocketException ex) System.out.println( "There is a server on port " + port + "."); // end try // end for Các phương thức public void send(datagrampacket Phương thức này cho phép gửi gói tin UDP qua mạng. 202 dp) throws IOException: Chương trình sau nhận một chuỗi từ bàn phím, tạo gói tin gửi và gửi tới server. import java.net.*; import java.io.*; public class UDPDiscardClient public final static int DEFAULT PORT = 9; public static void main(string[] args) String hostname; int port = DEFAULT_PORT;

203 if (args.length > 0) hostname = args[0]; try port = Integer.parseInt(args[1]); catch (Exception ex) else hostname = "localhost"; try InetAddress server = InetAddress.getByName(hostname); BufferedReader userinput= new BufferedReader(InputStreamReader(System.in)); DatagramSocket thesocket = new DatagramSocket( ); while (true) String theline = userinput.readline( ); if (theline.equals(".")) break; byte[] data = theline.getbytes( ); DatagramPacket theoutput= new DatagramPacket(data, data.length, server, port); thesocket.send (theoutput); // end while // end try catch (UnknownHostException uhex) System.err.println(uhex); catch (SocketException sex) System. err.println (sex) ; catch (IOException ioex) System.err.println (ioex) ; // end main 203

204 public void receive(datagrampacket dp) throws IOException: Phương thức nhận gói tin UDP qua mạng. public void close(): Phương thức đóng socket. Ví dụ 7.16: Chương trình sau sẽ tạo đối tượng DatagramSocket với số cổng xác định, nghe nhận gói dữ liệu gửi đến, hiển thị nội dung gói tin và địa chỉ, số cổng của máy trạm gửi gói tin. import java.net.*; import java.io.*; public class UDPDiscardServer public final static int DEFAULT PORT = 9; public final static int MAX PACKET SIZE = 65507; public static void main(string[] args) int port = DEFAULT_PORT; byte[] buffer = new byte[max PACKET SIZE]; try port = Integer.parseInt(args[0]); catch (Exception ex) // use default port try DatagramSocket server = new DatagramSocket(port); DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) try server.receive(packet) ; String s = new String(packet.getData( ), 0, packet.getlength( )); System.out.println(packet.getAddress( )+ " at port "+ packet.getport( )+ " says " + s); // reset the length for the next packet packet.setlength(buffer.length); catch (IOException ex) 204

205 System.err.println(ex) ; // end while // end try catch (SocketException ex) System.err.println(ex); // end catch // end main Kỹ thuật lập trình truyền thông với giao thức UDP Một số phương thức của lớp DatagramSocket void void void void bind (SocketAddress addr) Gắn kết DatagramSocket với địa chỉ và số cổng cụ thể connect( InetAddress address,int p o r t ) Kết nối socket với địa chỉ máy trạm từ xa connect ( SocketAddress addr ) Kết nối socket với địa chỉ socket từ xa. disconnect ()Huỷ bỏ kết nối boolean isbound ()Trả về trạng thái kết nối của socket. boolean iscỉosed ()Kiểm tra socket đã đóng hay chưa boolean isconnected ()Kiểm tra trạng thái kết nối 205 Hình 7.3. Quá trình khởi tạo truyền thông UDPSocket Trong mô hình client/server, để chương trình client và server có thể truyền thông được với nhau, mỗi phía phải thực hiện một số thao tác cơ bản sau đây (Hình 6.2) - Phía server

206 Tạo đối tượng DatagramSocket với số cổng cho trước Khai báo bộ đệm nhập /xuất inbuffer/outbuffer dạng mảng kiểu byte Khai báo gói tin nhận gửi indata/outdata là đối tượng DatagramPacket. Thực hiện nhận/gửi gói tin với phương thức receive()/send() Đóng socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần, không quay về bước 3. - Phía client Tạo đối tượng DatagramSocket với số cổng cho trước Khai báo bộ đệm xuất/nhập outbuffer/inbuffer dạng mảng kiểu byte Khai báo gói tin gửi/nhận outdata/indata là đối tượng DatagramPacket. Thực hiện gửi /nhận gói tin với phương thức send()/receive() Đóng socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần, không quay về bước 3. Một số chương trình ví dụ lập trình giao thức UDP Ví dụ 7.17: Chương trình minh hoạ sử dụng giao thức UDP để truyền dữ liệu //UDPEchoClient.java import java.net.*' import java.io.*; public class UDPEchoClient public final static int DEFAULT PORT = 7; public static void main(string[] args) String hostname = "localhost"; int port = DEFAULT_PORT; if (args.length > 0) hostname = args[0]; try InetAddress ia = InetAddress.getByName(hostname); Thread sender = new SenderThread(ia, DEFAULT PORT); sender.start( ); Thread receiver = new ReceiverThread(sender.getSocket( )); receiver.start( ); 206

207 catch (UnknownHostException ex) catch (SocketException ex) System.err.println (ex) ; // end main //UDPEchoServer.java import java.net.*; import java.io.*; public class extends UDPServer public final static int DEFAULT PORT = 7; public UDPEchoServer( ) throws SocketException super (DEFAULT_PORT) ; public void respond(datagram.packet packet) try DatagramiPacket outgoing = new DatagramPacket(packet.getData( ), packet.getlength( ), packet.getaddress( ), packet.getport( )); socket.send(outgoing); catch (IOException ex) System.err.println (ex) ; public static void main(string[] args) try UDPServer server = new UDPEchoServer( ); server.start( ); catch (SocketException ex) System.err.println(ex); 207

208 Ví dụ 7.18: Xây dựng chương trình Login từ xa dùng giao thức UDP Bài toán login từ xa dùng giao thức UDP đặt ra như sau: - Cơ sở dữ liệu được lưu trữ và quản lí trên server UDP, trong đó có bảng users chứa ít nhất hai cột: cột username và cột password. - Chương trình phía client UDP phải hiện giao diện đồ họa, trong đó có một ô text để nhập username, một ô text để nhập password, và một nút nhấn Login. - Khi nút Login được click, chương trình client sẽ gửi thông tin đăng nhập (username/password) trên form giao diện, và gửi sang server theo giao thức UDP - Tại phía server, mỗi khi nhận được thông tin đăng nhập gửi từ client, sẽ tiến hành kiểm tra trong cơ sở dữ liệu xem có tài khoản nào trùng với thông tin đăng nhập nhận được hay không.sau khi có kết quả kiểm tra (đăng nhập đúng, hoặc sai), server UDP sẽ gửi kết quả này về cho client tương ứng, theo đúng giao thức UDP. - Ở phía client, sau khi nhận được kết quả đăng nhập (đăng nhập đúng, hoặc sai) từ server, sẽ hiển thị thông báo tương ứng với kết quả nhận được: nếu đăng nhập đúng thì thông báo login thành công. Nếu đăng nhập sai thì thông báo là username/password không đúng. Yêu cầu kiến trúc hệ thống được thiết kế theo mô hình MVC Kiến trúc hệ thống theo mô hình MVC Sơ đồ lớp của phía client được thiết kế theo mô hình MVC bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: 208 Hình 7.4. Sơ đồ lớp phía client UDP

209 - Lớp User: là lớp tương ứng với thành phần model (M), bao gồm hai thuộc tính username và password, các hàm khởi tạo và các cặp getter/setter tương ứng với các thuộc tính. - Lớp ClientView: là lớp tương ứng với thành phần view (V), kế thừa từ lớp JFrame của Java, chứa các thuộc tính là các thành phần đồ họa bao gồm ô text nhập username, ô text nhập password, nút nhất Login. - Lớp ClientControl: là lớp tương ứng với thành phần control (C), chứa một lớp nội tại là LoginListener. Khi nút Login trên tầng view bị click thì nó sẽ chuyển tiếp sự kiện xuống lớp nội tại này để xử lí. - Tất cả các xử lí đều gọi từ phương thức actionperformed của lớp nội tại này, bao gồm: lấy thông tin trên form giao diện và gửi sang server theo giao thức UDP, nhận kết quả đăng nhập từ server về và yêu cầu form giao diện hiển thị. Điều này đảm bảo nguyên tắc control điều khiển các phần còn lại trong hệ thống, đúng theo nguyên tắc của mô hình MVC. Hình 7.5. Sơ đồ lớp phía server UDP Sơ đồ lớp của phía server được thiết kế theo mô hình MVC trong Hình 6.7, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: - Lớp User: là lớp thực thể, dùng chung thống nhất với lớp phía bên client. - Lớp ServerView: là lớp tương ứng với thành phần view (V), là lớp dùng hiển thị các thông báo và trạng thái hoạt động bên server UDP. - Lớp ServerControl: là lớp tương ứng với thành phần control (C), nó đảm nhiệm vai trò xử lí của server UDP, bao gồm: nhận thông tin đăng nhập từ phía các client, kiểm tra trong cơ sở dữ liệu xem các thng tin này đúng hay sai, sau đó gửi kết quả đăng nhập về cho client tương ứng. 209

210 Lớp phía client User.java package udp.client; import java.io.serializable; public class User implements Serializable private String username; private String password; public User() public User(String username, String password) this.username = username; this.password = password; public String getpassword() return password; public void setpassword(string password) this.password = password; public String getusername() return username; public void setusername(string username) this.username = username; Client View. java package udp.client; import java.awt.flowlayout; import j ava.awt.event.actionevent; import java.awt.event.actionlistener; import java.awt.event.windowadapter; import java.awt.event.windowevent; import javax.swing.*; public class ClientView extends JFrame implements ActionListener private JTextField txtusername; private JPasswordField txtpassword; private JButton btnlogin; public ClientView() 210

211 super("udp Login MVC"); txtusername = new JTextField(15); txtpassword = new JPasswordField(15); txtpassword.setechochar('*'); btnlogin = new JButton("Login"); JPanel content = new JPanel(); content.setlayout(new FlowLayout()); c ontent.add(new JLabel("Username:")); content.add(txtusername); content.add(new JLabel("Password:")); content.add(txtpassword); content.add(btnlogin); this.setcontentpane(content); this.pack(); this.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent e) System.exit(0); ); public void actionperformed(actionevent e) public User getuser() User model = new User(txtUsername.getText(), txtpassword.gettext()); return model; public void showmessage(string msg) JOptionPane.showMessageDialog(this, msg); public void addloginlistener(actionlistener log) btnlogin.addactionlistener(log); 211

212 ClientControl.java package udp.client; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.io.bytearrayinputstream; import java.io.bytearrayoutputstream; import java.io.objectinputstream; import java.io.objectoutputstream; import java.net.datagrampacket; import java.net.datagramsocket; import java.net.inetaddress; public class ClientControl private ClientView view; private int serverport = 5555; private int clientport = 6666; private String serverhost = "localhost"; private DatagramSocket myclient; public ClientControl(ClientView view) this.view = view; this.view.addloginlistener(new LoginListener()); class LoginListener implements ActionListener public void actionperformed(actionevent e) openconnection(); User user = view.getuser(); senddata(user); String result = receivedata(); if(result.equals("ok")) view.showmessage("login succesfully!"); else view.showmessage("invalid username and/or password!"); closeconnection(); 212

213 private void openconnection() try myclient = new DatagramSocket(clientPort); catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); private void closeconnection() try myclient.close(); catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); private void senddata(user user) try ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeobject(user); oos.flush(); InetAddress IPAddress = InetAddress.getByName(serverHost); byte[] senddata = baos.tobytearray(); DatagramPacket sendpacket = new DatagramPacket(sendData, senddata.length, IPAddress, serverport); myclient.send(sendpacket); catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); private String receivedata() String result = ""; try byte[] receivedata = new byte[1024]; DatagramPacket receivepacket =new DatagramPacket(receiveData, 213

214 receivedata.length); myclient.receive(receivepacket); ByteArrayInputStream bais = new ByteArrayInputStream(receiveData); ObjectInputStream ois = new ObjectInputStream(bais); result = (String)ois.readObject(); catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); return result; ClientRun.java package udp.client; public class ClientRun public static void main(string[] args) ClientView view = new ClientView(); ClientControl control = new ClientControl(view); view.setvisible(true); Các lớp phía server package udp.server; public class ServerView public ServerView() public void showmessage(string msg) System.out.println(msg); ServerControl.java package udp.server; import java.io.bytearrayinputstream; import j ava.io.bytearrayoutputstream; 214

215 import java.io.ioexception; import java.io.objectinputstream; import java.io.objectoutputstream; import java.net.datagrampacket; import java.net.datagramsocket; import java.net.inetaddress; import java.sql.connection; import java.sql.drivermanager; import java.sql.resultset; import java.sql.statement; import udp.client.user; public class ServerControl private ServerView view; private Connection con; private DatagramSocket myserver; private int serverport = 5555; private DatagramPacket receivepacket = null; public ServerControl(ServerView view) this.view = view; getdbconnection("usermanagement", "root", " "); openserver(serverport); view.showmessage("udp server is running..."); while(true) listenning(); private void getdbconnection(string dbname, String username, String password) String dburl = "jdbc:mysql://localhost:3306/" + dbname; String dbclass = "com.mysql.jdbc.driver"; try Class.forName(dbClass); con = DriverManager.getConnection (dburl, username, password); catch(exception e) view.showmessage(e.getstacktrace().tostring()); 215

216 private void openserver(int portnumber) try myserver = new DatagramSocket(portNumber); catch(ioexception e) view.showmessage(e.tostring()); private void listenning() User user = receivedata(); String result = "false"; if(checkuser(user)) result = "ok"; senddata(result); private void senddata(string result) try ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeobject(result); oos.flush(); InetAddress IPAddress = receivepacket.getaddress(); int clientport = receivepacket.getport(); byte[] senddata = baos.tobytearray(); DatagramPacket sendpacket = new DatagramPacket(sendData, senddata.length, IPAddress, clientport); myserver.send(sendpacket); catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); private User receivedata() User user = null; 216

217 try byte[] receivedata = new byte[1024]; receivepacket = new DatagramPacket(receiveData, receivedata.length); myserver.receive(receivepacket); ByteArrayInputStream bais = new ByteArrayInputStream(receiveData); ObjectInputStream ois = new ObjectInputStream(bais); user = (User)ois.readObjjctQ; catch (Exception ex) view.showmessage(ex.getstackt race().tostring()); return user; private boolean checkuser(user user) String query = "Select * FROM users WHERE username ='" + user.getusername()+ "' AND password ='" + user.getpassword() + "'"; try Statement stmt = con.createstatement(); ResultSet rs = stmt.executequery(query); if (rs.next()) return true; catch(exception e) view.showmessage(e.getstacktrace().tostring()); return false; ServerRun.java package udp.server; public class ServerRun public static void main(string[] args) 217

218 ServerView view = new ServerView(); ServerControl control = new ServerControl(view); Kết quả Login thành công: Login lỗi: 7.5. Lấy dữ liệu web URL và URI Sử dụng URLConnection để truy xuất dữ liệu 7.6. Gửi Gửi mail trong java sử dụng JavaMail. Đây là một nền tảng và giao thức độc lập được sử dụng để gửi, nhận và đọc mail thông qua ứng dụng Java. Sử dụng JavaMail API này để tạo ra những ứng dụng Java có tính năng hỗ trợ gửi cho người dùng. Ví dụ sau khi đăng ký thành công một tài khoản, ứng dụng sẽ gửi thông báo đên người dùng, Hình bên dưới là một minh hoạ về sử dụng JavaMail. 218

219 Hình 7.6. Minh hoạ sử dụng JavaMail Mô tả các giao thức mail khác nhau Hiện nay có 3 giao thức cơ bản được ứng dụng trong mail server giúp hệ thống có thể hoạt động trơn tru và đảm bảo khả năng bảo mật cao nhất, bao gồm: - SMTP Simple Mail Transfer Porotocol: SMTP được sử dụng khi gửi từ một ứng dụng như Postfix với một máy chủ hoặc khi được gửi từ một máy chủ khác. Giao thức này sử dụng cổng TCP POP3 Post Office Porotocol version 3: giao thức này được dùng để tải một từ một máy chủ . POP3 sử dụng cổng TCP IMAP Internet Message Access Protocol: Đây là giao thức thế hệ mới của POP. IMAP sử dụng cổng TCP 143 và đặt sự kiểm soát trên mail server. IMAP có thể hoạt động ở 3 chế độ: trực tuyến, ngoại tuyến và ngắt kết nối. Chế độ ngoại tuyến được thực hiện như sau: khi các đã được chuyển tới máy client, nó sẽ bị xóa khỏa mail server và sau đó hệ thống bị ngắt. Lúc này người dùng có thể đọc, trả lời và làm một số việc khác ở chế độ ngoại tuyến. Tuy nhiên nếu họ muốn gửi thư, họ phải kết nối lại. Như vậy ở trong chế độ này, thông điệp được lưu tạm ở client server giúp người dùng có thể sử dụng bình thường và ở lần kết nối kế tiếp nó sẽ được cập nhập trở lại vào mail server. 219 Hình 7.7. Các giao thức mail