1. Trang chủ
  2. » Công Nghệ Thông Tin

Bài giảng Ngôn ngữ lập trình Java: Phần 2 - TS. Vũ Hữu Tiến

65 25 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 65
Dung lượng 3,56 MB

Nội dung

Tiếp nội dung phần 1, Bài giảng Ngôn ngữ lập trình Java: Phần 2 cung cấp cho người học những kiến thức như: Xử lý nhập/xuất trong; xử lý ngoại lệ trong java; lập trình đa luồng; lập trình giao diện trong java. Mời các bạn cùng tham khảo!

BỘ THÔNG TIN VÀ TRUYỀN THÔNG HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THƠNG TS VŨ HỮU TIẾN ThS ĐỖ THỊ LIÊN BÀI GIẢNG NGƠN NGỮ LẬP TRÌNH JAVA Mã học phần: INT13108 (03 tín chỉ) Hà Nội, 11/2019 CHƯƠNG XỬ LÝ NHẬP/XUẤT TRONG 4.1 File luồng liệu Dữ liệu lưu trữ biến mảng tạm thời, bị biến cục bị phạm vi chương trình kết thúc Để lưu giữ liệu lâu dài, sau chương trình kết thúc, máy tính sử dụng tập tin (file) Máy tính lưu trữ file thiết bị lưu trữ thứ cấp đĩa cứng, USB, địa CD,… n Kết thúc file Hình Kích thước file n byte Java xem tệp luồng byte liên tiếp (Hình 4.1) Mỗi hệ điều hành cung cấp chế để xác định kết thúc tệp, chẳng hạn điểm đánh dấu cuối tệp số đếm tổng số byte tệp ghi lại cấu trúc file Một chương trình Java xử lý luồng byte đơn giản nhận liệu từ hệ điều hành đến cuối luồng chương trình dừng mà khơng cần để biết file luồng biểu diễn Các luồng liệu biểu diễn chuỗi nhị phân định dạng theo byte (byte based stream) chuỗi ký tự (character stream) Ví dụ, số lưu dạng nhị phân 0000.0101 Nếu số lưu dạng ký tự số nhị phân biểu diễn giá trị mã Unicode dùng để mô tả ký tự Cụ thể, ký tự bảng mã Unicode có mã 53 Vì vậy, chuỗi số nhị phân lưu vào file 0000.0000.0011.0101 Sự khác biệt hai cách lưu số cách thứ nhất, hiểu số nguyên đọc để tính tốn cịn cách thứ hai, hiểu ký tự sử dụng chuỗi Ví dụ ‘‘Tom is years old’’ Các file sử dụng luồng nhị phân gọi file nhị phân (binary file), file sử dụng luồng ký tự gọi file văn (text file) File văn đọc chương trình soạn thảo văn bản, file nhị phân đọc chương trình hiểu cấu trúc file Chương trình Java mở file cách tạo đối tượng, sau đối tượng kết hợp với luồng byte luồng ký tự Chương trình Java xử lý file cách sử dụng lớp gói Java.io Gói cung cấp lớp xử lý luồng liệu FileInputStream (dùng để ghi luồng byte vào file), FileOutputStream (dùng để đọc luồng byte từ file) FileWriter (dùng để ghi luồng ký tự vào file) FileReader (dùng để đọc luồng ký tự từ file) kế thừa từ lớp InputStream, OutputStream, Reader Writer tương ứng 67 Java cung cấp lớp dùng để xử lý liệu vào/ra đối tượng liệu Các liệu chất lưu dạng byte ký tự người lập trình đọc liệu dạng int, float,… String mà không cần quan tâm chúng chuyển sang dạng byte dạng ký tự Để xử lý liệu này, đối tượng lớp ObjectInputStream ObjecOutputStream dùng với lớp luồng byte FileInputStream FileOutputStream 4.2 Lớp File Đối tượng lớp java.io.File biểu diễn cho file thư mục mà không biểu diễn nội dung file Trong chương trình ta dùng đối tượng lớp để thay cho chuỗi biểu diễn tên file tên thư mục Hầu hết lớp sử dụng tham số file hàm khởi tạo FileWriter FileInputStream sử dụng đối tượng File để làm đối số - Tạo đối tượng File đại diện cho file : File f = new File(‘‘MyCode.txt’’) ; - Tạo thư mục : File dir = new File(‘‘Code’’) ; dir.mkdir() ; - Một số phương thức lớp File : Phương thức boolean canread() Mơ tả Trả giá trị True file đọc chương trình, giá trị False khơng đọc boolean canwrite() Trả giá trị True file ghi chương trình, giá trị False không ghi boolean exists() Trả giá trị True file thư mục tồn tại, giá trị False file thư mục không tồn String getName() Trả tên file thư mục biểu thị pathname trừu tượng String getParent() Trả đường dẫn thư mục chứa file String getPath() Trả đường dẫn file boolean isDirectory() Trả giá trị True đối trượng tên thư mục, giá trị False đối tượng tên thư mục boolean isFile() Trả giá trị True đối trượng tên file, giá trị False đối tượng tên file long lastModified() Trả thời điểm sửa file lần cuối long length() Trả số byte liệu file String[] list() Trả mảng chuỗi file thư mục thư mục Bảng Một số phương thức lớp file 68 Ví dụ : Yêu cầu người dùng nhập tên file thư mục in thông tin file thư mục 4.3 Kiến trúc luồng xuất liệu file 69 Hình Cấu trúc gia phá lớp xử lý vào/ra liệu Hình mô tả cấu trúc kế thừa lớp xử lý vào/ra liệu Các lớp dùng để xử lý liệu luồng byte thuộc lớp cha InputStream (đọc liệu từ file) OutputStream (ghi liệu file) Các lớp dùng xử lý liệu luồng văn thuộc lớp cha Reader (đọc liệu từ file văn bản) Writer (ghi liệu file văn bản) Các lớp cung cấp phương thức cho phép đọc/ghi liệu dạng ký tự cịn lớp cung cấp phương thức cho phép đọc/ghi tốc độ cao 4.3.1 Dữ liệu dạng byte 101 0 1 1 1 Program FileOutputStream File 101 0 1 1 1 File FileInputStream Program Hình Trao đổi liệu dạng byte Để trao đổi liệu với file ta phải tạo luồng kết nối chương trình file cách sử dụng lớp FileOutputStream/FileInputStream Ta luồng kết nối giống ‘‘con đường’’ để vận chuyển liệu Sau có ‘‘con đường’’, 70 ta phải sử dụng tới ‘‘phương tiện’’ để vận chuyển liệu Tùy theo loại liệu khác mà ta phải sử dụng đến ‘‘phương tiện’’ khác Cụ thể : - Nếu liệu luồng byte chứa liệu ta dùng lớp DataInputStream/DataOutputStream Nếu liệu luồng byte chứa đối tượng chuỗi hóa ta dùng lớp ObjectInputStream/ObjectOutputStream 4.3.2 Dữ liệu dạng văn 101 0 1 1 1 Program FileWriter File 101 0 1 1 1 File FileReader Program Hình 4 Trao đổi liệu dạng văn Tương tự liệu dạng byte, để tạo ‘‘con đường’’ kết nối chương trình file lưu liệu ta dùng lớp FileWriter/FileReader 4.4 Nếu ghi/đọc liệu ký tự rời rạc văn ta sử dụng trực tiếp phương thức write/read lớp Nếu ghi/đọc dịng văn ta dùng thêm lớp BufferedWriter/BufferedReader Ghi/đọc chuỗi ký tự tệp văn Quá trình ghi/đọc luồng ký tự hay luồng byte ra/vào tệp tuân theo bước sau : - Bước 1: Tạo đối tượng luồng liên kết với nguồn liệu file chứa liệu Bước : Đọc ghi liệu Bước : Đóng luồng Sử dụng lớp FileWriter để ghi liệu vào file văn sau : 71 Sử dụng lớp FileReader để đọc liệu từ file văn : Đọc ký tự : Đọc dòng : 72 4.5 Đọc/ghi liệu luồng byte 4.5.1 Đọc/ghi liệu dạng 73 4.5.2 Đọc/ghi liệu đối tượng Các đối tượng có trạng thái hành vi Hành vi tồn lớp, trạng thái tồn đối tượng cụ thể Trong nhiều trường hợp cần phải lưu lại 74 trạng thái đối tượng để đến lúc ta khơi phục lại Ví dụ ta chơi game, ta lưu lại trạng thái nhân vật Sau đó, ta quay lại chơi tiếp game trạng thái nhân vật lấy lại lúc trước lưu Trong Java cung cấp hai cách để lưu đối tượng: - Cách thứ lưu giá trị trạng thái vào file theo định dạng quy định Khi khôi phục lại trạng thái đối tượng, ta đọc giá trị gán tương ứng vào biến đối tượng Với cách này, ta dùng file dạng text với cú pháp quy định để lưu giá trị trạng thái chương trình khác ngồi Java đọc giá trị trạng thái Ví dụ : Với cách này, đọc liệu dễ mắc phải lỗi đọc nhầm trường dịng Khi đó, chương trình dễ bị lỗi trạng thái đối tượng không khơi phục lại ban đầu Vì vậy, cách dùng để ghi/đọc trạng thái đối tượng - Cách thứ hai ‘‘nén’’ đối tượng lại ‘‘giải nén’’ đối tượng cần sử dụng trở lại Với cách này, chương trình khác ngồi Java khó đọc nội dung file Cách gọi chuỗi hóa (serialization) đối tượng Ví dụ : Tuy nhiên khơng phải đối tượng chuỗi hóa Để đối tượng thuộc lớp chuỗi hóa được, ta phải cho lớp triển khai lớp giao diện Serializable Lớp Serializable khơng có phương thức để cài đè Mục đích lớp để khai báo lớp triển khai chuỗi hóa Nếu lớp chuỗi hóa lớp tự động chuỗi hóa mà khơng cần phải khai báo lại Ví dụ : ghi đối tượng file 75 // Constructor to setup GUI components and event handlers public AWTBorderLayoutDemo () { setLayout(new BorderLayout(3, 3)); // "super" Frame sets layout to BorderLayout, // horizontal and vertical gaps of pixels // The components are added to the specified zone btnNorth = new Button("NORTH"); add(btnNorth, BorderLayout.NORTH); btnSouth = new Button("SOUTH"); add(btnSouth, BorderLayout.SOUTH); btnCenter = new Button("CENTER"); add(btnCenter, BorderLayout.CENTER); btnEast = new Button("EAST"); add(btnEast, BorderLayout.EAST); btnWest = new Button("WEST"); add(btnWest, BorderLayout.WEST); setTitle("BorderLayout Demo"); // "super" Frame sets title size setSize(280, 150); // "super" Frame sets initial setVisible(true); // "super" Frame shows } // The entry main() method public static void main(String[] args) { new AWTBorderLayoutDemo(); // Let the constructor the job } } Kết quả: 7.4.3 GridLayout Manager Cách đặt chia cửa sổ container thành ô lưới Các thành phần đồ họa đặt vào vào ô lưới theo tọa độ Hàm khởi tạo GridLayout : GridLayout gl = GridLayout(4,4) ; // Tạo lưới GridLayout gồm 16 116 Ví dụ : import java.awt.*; import java.awt.event.*; // An AWT GUI program inherits the top-level container java.awt.Frame public class AWTPanelDemo extends Frame { private Button[] btnNumbers; // Array of 10 numeric Buttons private Button btnHash, btnStar; private TextField tfDisplay; // Constructor to setup GUI components and event handlers public AWTPanelDemo () { // Set up display panel Panel panelDisplay = new Panel(new FlowLayout()); tfDisplay = new TextField("0", 20); panelDisplay.add(tfDisplay); // Set up button panel Panel panelButtons = new Panel(new GridLayout(4, 3)); btnNumbers = new Button[10]; // Construct an array of 10 numeric Buttons btnNumbers[1] = new Button("1"); // Construct Button "1" panelButtons.add(btnNumbers[1]); // The Panel adds this Button btnNumbers[2] = new Button("2"); panelButtons.add(btnNumbers[2]); btnNumbers[3] = new Button("3"); panelButtons.add(btnNumbers[3]); btnNumbers[4] = new Button("4"); panelButtons.add(btnNumbers[4]); btnNumbers[5] = new Button("5"); panelButtons.add(btnNumbers[5]); btnNumbers[6] = new Button("6"); panelButtons.add(btnNumbers[6]); btnNumbers[7] = new Button("7"); panelButtons.add(btnNumbers[7]); btnNumbers[8] = new Button("8"); panelButtons.add(btnNumbers[8]); btnNumbers[9] = new Button("9"); panelButtons.add(btnNumbers[9]); // You should use a loop for the above statements!!! btnStar = new Button("*"); panelButtons.add(btnStar); btnNumbers[0] = new Button("0"); panelButtons.add(btnNumbers[0]); btnHash = new Button("#"); panelButtons.add(btnHash); 117 setLayout(new BorderLayout()); // "super" Frame sets to BorderLayout add(panelDisplay, BorderLayout.NORTH); add(panelButtons, BorderLayout.CENTER); setTitle("BorderLayout Demo"); // "super" Frame sets title setSize(200, 200); initial size setVisible(true); } // "super" Frame sets // "super" Frame shows // The entry main() method public static void main(String[] args) { new AWTPanelDemo(); // Let the constructor the job } } 7.5 Xử lý kiện 7.5.1 Khái niệm xử lý kiện Trong lập trình giao diện đồ họa, kiện (event) định nghĩa hành động thực người sử dụng Ví dụ người dùng ấn vào phím, kích chuột, nhấn lên nút, cuốn, Xử lý kiện (event handling) trình phát kiện thực nhiệm vụ mà u cầu Ví dụ người dùng nhấn vào nút Exit chương trình phải có nhiệm vụ phát việc khỏi chương trình chạy Vậy làm để phát kiện thực nhiệm vụ ? Câu trả lời phần mềm phải nằm vòng lặp để chờ kiện xảy để thực Tuy nhiên, người lập trình khơng phải viết vòng lặp mà Java hỗ trợ việc cho người lập trình Cụ thể, Java trạng thái lắng nghe kiện (event listening), kiện xảy ra, Java tự động gọi đến phương thức xử lý người lập trình viết Đây gọi mơ hình điều khiển theo kiện (event – drivent) Mơ hình mơ tả sau : 118 Hình Mơ hình xử lý kiện Mơ hình gồm thành phần : Nguồn tạo kiện (Source): thành phần giao diện mà kiện xuất Ví dụ button, textfield,… Source chịu trách nhiệm cung cấp thông tin kiện xảy tới xử lý Handler Khối xử lý kiện (Listener): Còn biết Event Handler Listener chịu trách nhiệm tạo phản hồi tới kiện Theo quan điểm Java, Listener đối tượng Khi kiện nhận, Listener xử lý kiện sau trả kết Các bước hoạt động mơ hình : Thành phần giao diện đăng ký khối xử lý kiện tương ứng để khối xử lý kiện xử lý kiện phù hợp với thành phần giao diện Về chất, khối xử lý kiện lớp giao diện cung cấp Java Trong lớp giao diện có phương thức để xử lý kiện tạo thành phần giao diện Ví dụ : Lớp xử lý kiện MouseListener có phương thức để xử lý kiện liên quan đến chuột : public interface MouseListener { // Xử lý kiện chuột ấn giữ public void mousePressed(MouseEvent evt); // Xử lý kiện chuột thả public void mouseReleased(MouseEvent evt); // Xử lý kiện chuột ấn thả public void mouseClicked(MouseEvent evt); // Xử lý kiện trỏ chuột đặt vào phạm vi thành phần giao diện 119 public void mouseEntered(MouseEvent evt); // Xử lý kiện trỏ chuột kéo khỏi phạm vi thành phần giao diện public void mouseExited(MouseEvent evt); } Thành phần kiện thay đổi trạng thái người dùng tác động Thành phần giao diện tạo đối tượng tương ứng với kiện xảy Thành phần giao diện gọi phương thức tương ứng lớp xử lý kiện để xử lý đối tượng vừa tạo truyền vào phương thức để xử lý 7.5.2 Các bước lập trình xử lý kiện Bước 1: Khai báo lớp xử lý kiện Lớp có nhiệm vụ implement lớp giao diện Listener public class DemoClass extends Frame implements ActionListener { } Bước 2: Gán đối tượng xử lý kiện lớp xử lý kiện khai báo cho components liên quan Component.addActionListener(instanceOf DemoClass); Bước 3: Triển khai phương thức xử lý kiện lớp giao diện Listener public void actionPerformed(ActionEvent e) { //code that reacts to the action } 7.5.3 Các lớp xử lý kiện Sự kiện Listener Interface Mô tả ActionEvent ActionListener Sự kiện xảy nút click, danh sách item double click menu lựa chọn AdjustmentEvent AdjustmentListener Sự kiện xảy scrollbar sử dụng ComponentEvent ComponentListener Sự kiện xảy component thay đổi kích thước, di chuyển, ẩn FocusEvent FocusListener Sự kiện xảy component chuột bàn phím lựa chọn 120 ItemEvent ItemListener Xảy menu item chọn, giải phóng checkbox list item click WindowEvent WindowListener Xảy cửa sổ activated, closed, opened quit TextEvent TextListener Xảy giá trị text field text area thay đổi MouseEvent MouseListener MouseMotionListener Xảy chuột di chuyển, click, giữ giải phóng KeyEvent KeyListener Xảy input nhận từ keyboard Bảng Một số lớp xử lý kiện Ví dụ : Sử dụng thành phần giao diện Choice 121 Kết : Ví dụ : Sử dụng thành phần giao diện Button 122 Kết : 7.6 Applet 7.6.1 Khái niệm Applet Applet chương trình Java chạy với hỗ trợ web browser Về chất, Applet giống Frame Frame dùng cho ứng dụng chạy độc lập Applet dùng cho ứng dụng chạy trên web Lớp Applet nằm gói java.applet Java Để tạo Applet, ta cần import hai gói sau: - Java.applet Java.awt 7.6.2 Cấu trúc Applet Mỗi Applet có phương thức mặc định chạy tự động init(), start(), stop() destroy() init – Hàm gọi trình khởi tạo applet Trong trình khởi tạo, đối tượng cụ thể Applet tạo Ngoài ra, phương thức sử dụng để nạp đối tượng đồ họa, khởi tạo biến tạo đối tượng 123 start- Khi hàm start gọi thực thực thi chương trình applet trình duyệt bạn Ngoài hàm dùng để khởi động lại applet sau tạm dừng stop – Hàm ngược lại với start, dừng hoạt động chương trình applet thực thi destroy – Hàm thực đóng hẳn chương trình applet Ngồi phương thức chương trình số phương thức khác repaint(), paint(), paintComponents(),… Phương thức repaint() dùng để cập nhật lại toàn hiển thị cửa sổ Applet Hai phương thức cịn lại dùng để vẽ hình ảnh, màu sắc, chữ, hình học,… lên Applet thơng q đối tượng Graphics Hình ảnh mơ tả chu trình hoạt động Applet cách Applet thực Hình Chu trình sống Applet - - Bước chạy applet trình “khởi tạo” Sau khởi tạo xong, applet “bắt đầu q trình làm việc” Bước lặp lại trước applet bị tạm dừng Ví dụ người dùng chuyển đến trang web sau quay trở lại trang chứa applet Sự khác biệt init() start() start() gọi nhiều lần init() gọi lần Phương thức stop() gọi người dùng rời trang web thu nhỏ trang web Phương thức destroy() gọi người dùng tắt trang web Phương thức giúp giải phòng nhớ số công việc nhằm “dọn dẹp” tài nguyên Sự khác Application Applet: 124 - - Để Application thực thi phải dùng trình thơng dịch java, applet chạy trình duyệt có hỗ trợ java sử dụng AppletViewer Quá trình thực thi Application hàm main() cịn Applet bắt đầu hàm init() Application sử dụng lệnh system.out.println() để hiển thị kết hình console, Applet sử dụng phương thức drawString() để đưa dòng chữ lên hình Vì Applet chạy web nên có số giới hạn hoạt động để chương trình Applet khơng làm ảnh hưởng đến hệ thống web người sử dụng khơng thể xóa file, lấy thơng tin hệ thống,… Cụ thể, Applet có số hạn chế sau: - Không thể đọc ghi file hệ thống file người dùng Chỉ giao tiếp hai website hai website có chương trình Applet Khơng thể chạy chương trình hệ thống người dùng Khơng thể tải chương trình lưu trữ hệ thống người dùng Ví dụ chương trình Applet Netbeans: Giả sử ta tạo lớp AppletExample kế thừa lớp Applet : Bước : Chọn New Project : Bước : Chọn Next -> nhập tên Project Bước : Tại cửa sổ bên trái, click phải chuột vào Project Applet Example chọn New -> Other -> New File 125 Bước : Chọn Next -> Nhập tên file : AppletExample Bước : Viết chương trình cho người dùng nhập tuổi vào TextField Nếu tuổi nhỏ 18 báo người dùng ‘‘Chưa đủ tuổi bầu cử’’ Ngược lại, chương trình thơng báo người dùng ‘‘Bạn đủ tuổi bầu cử’’ Trong chương trình ta thấy hai phương thức init() paint() cài đè Một ứng dụng frame chạy có hàm main, cịn applet chạy có hàm init, paint, start, stop,… đặc biệt phải kế thừa từ đối tượng Applet Khi Applet chạy tự động gọi tới hàm init() để khởi tạo có trước, sau gọi tới hàm paint() thực nội dung hàm Phương thức paint() sử dụng để hiển thị văn hình đồ họa lên hình Nó gọi trình duyệt Web trình applet viewer cần phải vẽ lên hình 126 7.7 Lập trình giao diện với Swing Java Swing lớp tảng Java (Java Foundation Classes - JFC) sử dụng để tạo ứng dụng dựa cửa sổ Nó phát triển dựa API AWT hoàn toàn viết java Tuy nhiên, không giống AWT, Java Swing cung cấp thành phần đồ họa nhẹ độc lập với tảng Gói javax.swing cung cấp lớp cho API swing java JButton, JTextField, JTextArea, JRadioButton, JCheckbox, JMothy, JColorChooser, v.v Cấu trúc gia phả Java Swing thể hình 7.6 7.7.1 Tạo thành phần giao diện với Java Swing Tương tự với AWT, tạo giao diện Java Swing cách viết lệnh trực tiếp Ngoài ra, ta dùng cách kéo thả thành phần giao diện vào cửa sổ Với cách này, chủ động xếp thành phần giao diện vào vị trí cách trực quan Hình Cây gia phả lớp SWING Ví dụ : import javax.swing.JFrame; public class Test extends JFrame { public Test() { setSize(300, 200); setTitle("Test"); 127 setDefaultCloseOperation(EXIT_ON_CLOSE); } } public static void main(String[] args) { Test test = new Test(); test.setVisible(true); } 7.7.2 Một số ví dụ với Swing Ví dụ : Tạo nút bấm Ví dụ : Tạo Bảng với Swing 128 BÀI TẬP CHƯƠNG Bài : Viết chương trình cho phép người dùng click chuột vào giao diện hiển thị tọa độ chuột người dùng click : Bài : Tạo giao diện hình vẽ : Bài : Tạo giao diện với chức hình vẽ : 129 TÀI LIỆU THAM KHẢO Kathy Sierra , Bert Bates Head First Java O’reilly Media, Inc, 2005 Bruce Eckel Thinking in Java President, MindView, Inc, 2006 Nguyễn Tiến, Ngô Quốc Việt, Phạm Nguyễn Tuấn Kỳ Giáo trình lý thuyết thực hành Java Nhà xuất thống kê,2000 Trần Đình Quế Lập trình hướng đối tượng Học Viện Cơng Nghệ Bưu Chính Viễn Thông 130 ... tới phần tử vượt q giới hạn mảng Nếu người lập trình khơng lường hết tình khơng viết đoạn mã để chương trình xử lý gặp lỗi chương trình dừng đột ngột Thơng thường, để xử lý tình này, người lập trình. .. System.out.println(str); } } 88 BÀI TẬP CHƯƠNG Bài 1: Nhập vào mảng N phần tử In phần tử mảng Dùng try/catch để bắt lỗi người dùng nhập vào sai số phần tử mảng Bài 2: Tạo lớp SoAmException để bắt... xảy trình chạy chương trình Lỗi cịn có tên RuntimeException Lỗi trình biên dịch bỏ qua biên dịch thực thi chương trình báo lỗi Ví dụ: 87 • 5.6 Error: Đây lỗi vật lý xảy trình chạy chương trình

Ngày đăng: 01/03/2022, 09:43

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN