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; Chương 2 trình bày những khái niệm căn bản về lập trình 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…; chương 4 giới thiệu lập trình giao diện bằng SWING; 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; chương 7 giới thiệu lập trình mạng bằng socket.
TỔNG QUAN VỀ LẬP 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ư từ Sun Microsystems đã bắt đầu phát triển một ngôn ngữ lập trình để điều khiển các thiết bị điện tử như tivi và máy giặt Ban đầu, họ dự định sử dụng C và C++, nhưng nhận thấy rằng các trình biên dịch C/C++ phụ thuộc vào từng loại CPU Do đó, nhóm đã quyết định xây dựng một ngôn ngữ mới, nhanh chóng, gọn gàng và hiệu quả, độc lập với thiết bị, và ngôn ngữ này được đặt tên là "Oak".
Năm 1995, ngôn ngữ Oak, tương tự như C++, đã được cải tiến để loại bỏ những tính năng nguy hiểm và có khả năng chạy trên nhiều nền tảng phần cứng khác nhau Cùng thời điểm đó, sự phát triển của World Wide Web đã thu hút sự chú ý của Sun, dẫn đến việc họ đầu tư vào việc cải tiến và phát triển ngôn ngữ này.
Ngôn ngữ lập trình Java được Sun Microsystems giới thiệu vào tháng 6 năm
Java, được ra mắt vào năm 1995, đã nhanh chóng khẳng định vị thế là ngôn ngữ lập trình ưa chuộng của các lập trình viên chuyên nghiệp Được phát triển dựa trên nền tảng của C và C++, Java sử dụng cú pháp tương tự, mang lại sự dễ dàng trong việc học và áp dụng cho nhiều ứng dụng khác nhau.
C++ là ngôn ngữ lập trình hướng đối tượng, trong khi Java kết hợp cả biên dịch và thông dịch Mã nguồn Java được biên dịch thành bytecode và sau đó được thực thi trên các nền tảng khác nhau nhờ trình thông dịch Mục tiêu của Java là cho phép lập trình viên viết mã một lần và chạy trên nhiều phần cứng khác nhau.
Hiện nay, Java là một ngôn ngữ lập trình phổ biến, được ứng dụng rộng rãi không chỉ trong việc phát triển phần mềm cho máy tính để bàn và web, mà còn trong việc tạo ra các trình điều khiển cho thiết bị di động và PDA.
Đặc trưng ngôn ngữ Java
Ngôn ngữ lập trình Java đơn giản hóa các đặc trưng phức tạp của C và C++ bằng cách loại bỏ thao tác con trỏ, không cho phép đa kế thừa mà thay vào đó sử dụng giao diện, và không hỗ trợ lệnh “goto” cũng như file header (.h) Ngoài ra, Java cũng loại bỏ các cấu trúc như “struct” và “union”, giúp lập trình viên dễ dàng hơn trong việc phát triển ứng dụng.
Hướng đối tượng trong Java tương tự như C++, nhưng Java là ngôn ngữ lập trình hướng đối tượng hoàn toàn Trong Java, mọi thứ đều liên quan đến các đối tượng đã được định nghĩa, bao gồm cả hàm chính của chương trình.
Trong Java, hàm main phải được đặt bên trong một lớp Hướng đối tượng trong Java không hỗ trợ tính đa kế thừa như C++, thay vào đó, Java sử dụng 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 trong Java có nghĩa là một chương trình, nếu chạy đúng trên một dòng máy nào đó, thì cũng sẽ hoạt động chính xác trên tất cả các dòng máy khác Ngược lại, chương trình chỉ hoạt động trên một số dòng máy cụ thể được xem là phụ thuộc vào phần cứng.
Tính độc lập với hệ điều hành trong Java có nghĩa là chương trình Java có thể chạy trên mọi hệ điều hành Ngược lại, những chương trình chỉ hoạt động trên một số hệ điều hành nhất định được gọi là phụ thuộc hệ điều hành.
Các chương trình Java có khả năng chạy trên hầu hết các hệ điều hành mà không cần thay đổi, nhờ vào khẩu hiệu 'viết một lần, chạy mọi nơi' Điều này là một ưu điểm vượt trội so với nhiều ngôn ngữ lập trình khác.
Java là ngôn ngữ lập trình chặt chẽ về kiểu dữ liệu, giúp giảm thiểu lỗi nhờ hạn chế ép kiểu tự động như trong C, C++ Ngôn ngữ này kiểm tra lỗi cả trong quá trình biên dịch và thông dịch, loại bỏ nhiều lỗi lập trình phổ biến Java không sử dụng con trỏ và các phép toán liên quan, đồng thời kiểm tra tất cả các truy cập đến mảng và chuỗi trong thời gian thực thi để đảm bảo không vượt quá giới hạn kích thước.
Trong lập trình truyền thống, lập trình viên phải tự quản lý bộ nhớ, bao gồm việc cấp phát và giải phóng Tuy nhiên, trong Java, quá trình này được tự động hóa nhờ dịch vụ thu gom rác (garbage collection), giúp lập trình viên không phải lo lắng về việc quản lý bộ nhớ Bên cạnh đó, cơ chế bẫy lỗi của Java cũng đơn giản hóa việc xử lý và hồi phục sau lỗi.
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 an toàn Mức đầu tiên là mức ngôn ngữ, nơi dữ liệu và phương thức được đóng gói trong lớp và chỉ có thể truy xuất thông qua các giao diện mà lớp cung cấp Mức thứ hai là mức trình biên dịch, trong đó trình biên dịch kiểm tra tính an toàn của mã trước khi thực hiện 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ư, hay còn gọi là mức class, đóng vai trò quan trọng trong việc kiểm soát quá trình nạp các lớp vào bộ nhớ Điều này giúp giám sát và phát hiện kịp thời các vi phạm giới hạn truy xuất trước khi các lớp được nạp vào hệ thống.
Java được phát triển để hỗ trợ các ứng dụng mạng thông qua gói Java.net, cung cấp các lớp mạng cần thiết Nó tích hợp công nghệ lập trình như RMI, CORBA và JavaBean, cho phép tái sử dụng các lớp đã được tạo ra và gọi các phương thức hoặc đối tượng từ xa.
Tính năng của Java cho phép lập trình viên viết các chương trình với nhiều đoạn mã lệnh chạy song song một cách dễ dàng Việc đồng bộ tài nguyên dùng chung trong Java cũng rất đơn giản, điều này tạo ra lợi thế lớn so với một số ngôn ngữ lập trình khác như C/C++ hay Pascal.
Java được phát triển như một ngôn ngữ động, phù hợp với các môi trường mở Các chương trình Java cung cấp thông tin cho các đối tượng trong thời gian thực, cho phép liên kết động mã một cách linh hoạt.
Các ứng dụng của Java
Ứng dụng thực thi qua dòng lệnh (Console) là ứng dụng nhập xuất văn bản tương tự như màn hình Console của MS-DOS Các ứng dụng Console thường được sử dụng để minh họa cú pháp ngôn ngữ, thuật toán và các chương trình không yêu cầu giao diện người dùng đồ họa.
Applet là một chương trình Java được thiết kế để chạy trên Internet thông qua các trình duyệt hỗ trợ Java như Internet Explorer và Netscape Nó được nhúng trực tiếp vào trang web, và khi trang được hiển thị trong trình duyệt, Applet sẽ được tải xuống và thực thi ngay trong môi trường của trình duyệt.
Java đã phát triển các chương trình ứng dụng với giao diện đồ họa trực quan thông qua thư viện AWT và JFC Trong đó, JFC (Swing) cung cấp nhiều tính năng phong phú và hỗ trợ mạnh mẽ hơn AWT, giúp lập trình viên dễ dàng tạo ra giao diện trực quan cho bất kỳ ứng dụng nào, bao gồm cả ứng dụng web.
Java cung cấp hỗ trợ mạnh mẽ cho phát triển ứng dụng Web thông qua công nghệ J2EE (Java 2 Enterprise Edition), cho phép tạo ra các ứng dụng Web hiệu quả tương đương với công nghệ NET của Microsoft Các công nghệ web hiện có của Java bao gồm Servlet và JSP, cùng với sự hỗ trợ từ lập trình Socket, Java Bean, RMI, CORBA và EJB, mang lại khả năng lập trình mạng đa dạng.
Java cung cấp hỗ trợ mạnh mẽ cho lập trình ứng dụng mạng thông qua các lớp thư viện socket, giúp đơn giản hóa việc kết nối và truyền dữ liệu Người lập trình có thể dễ dàng làm việc với nhiều giao thức như TCP/IP, UDP, HTTP, FTP, và Telnet.
Các ứng dụng điển hình bao gồm chương trình chat, chuyển dữ liệu, truyền tin tức, trao đổi trực tuyến và gửi nhận thư điện tử, cùng với ứng dụng cơ sở dữ liệu.
CSDL là yếu tố quan trọng trong hầu hết các ứng dụng, như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 Chẳng hạn, VB cung cấp nhiều cơ chế truy xuất CSDL tiện lợi như ODBC, ADO, ADO.NET và OLEDB, trong khi C/C++ lại thiếu những tính năng này.
Java cung cấp cơ chế truy xuất cơ sở dữ liệu mở thông qua JDBC (Java Database Connectivity), cho phép kết nối với nhiều loại cơ sở dữ liệu như Oracle, DB2, MySQL và SQL Server Với JDBC, việc truy cập dữ liệu trở nên dễ dàng hơn nhờ mô hình đa luồng, rất phù 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ớ là bước quan trọng trong quá trình "loading", nơi chương trình cần được nạp vào bộ nhớ trước khi thực thi "Loader" sẽ lấy các tệp có đuôi ".class" chứa mã Java bytecode và thực hiện việc nạp chúng vào bộ nhớ.
Trước khi trình thông dịch chuyển đổi mã Java bytecode thành mã máy để thực thi, mã bytecode cần được kiểm tra tính hợp lệ Dưới sự điều khiển của CPU và trình thông dịch, mã bytecode sẽ được chuyển đổi thành mã máy và thực hiện tại mỗi thời điểm.
Kiến trúc chương trình xây dựng trên Java
1.5.1 Kiến trúc chương trình Java
Tập tin mã nguồn Java cơ bản có cấu trúc như sau: đầu tiên là khai báo tên gói với cú pháp `package packageName;`, tiếp theo là khai báo thư viện cần sử dụng bằng `import java.awt.*;`, và cuối cùng là khai báo tên lớp với cú pháp `class className`.
/* Đâ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
Gói (package) là công cụ dùng để kết hợp các lớp trong chương trình thành một khối thống nhất, giúp lưu trữ hiệu quả các lớp tương tự hoặc thuộc cùng một module.
Cú pháp khai báo tên gói trong lập trình Java sử dụng từ khóa "package" theo định dạng: package ; Để đặt tên cho package, người dùng có thể áp dụng quy tắc tương tự như đặt tên thư mục trên ổ đĩa, bắt đầu từ tên có phạm vi lớn đến tên có phạm vi nhỏ, và cuối cùng là tên các gói chứa lớp Thông thường, quy trình đặt tên gói diễn ra theo thứ tự từ phạm vi lớn đến nhỏ.
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 lại thành các đơn vị nhỏ hơn giúp thao tác trên các khối này trở nên gọn gàng và hiệu quả hơn so với việc làm việc với một tập hợp lớn các lớp.
Để tránh xung đột trong việc đặt tên lớp, đặc biệt khi số lượng lớp trong chương trình lớn, bạn nên tổ chức chúng vào các package khác nhau Điều này giúp giảm thiểu việc phải đặt tên khác nhau cho các lớp và giữ cho mã nguồn được tổ chức tốt hơn.
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 trong cùng một gói ứng dụng cần được lưu trữ trong cùng một thư mục, với tên thư mục tương ứng là tên khối, theo cấu trúc khối của dự án.
Tên gói nên được viết bằng chữ thường để tránh nhầm lẫn với tên các tệp tin, vì tên gói sẽ tương ứng với tên thư mục trong ổ đĩa.
Khai báo thư viện là bước quan trọng để chỉ ra các thư viện đã được định nghĩa sẵn mà chương trình sẽ sử dụng Cú pháp để khai báo thư viện sử dụng từ khóa import, ví dụ: import ;
Java chuẩn cung cấp một số thư viện như sau:
Gói java.lang cung cấp các hàm hữu ích để thao tác với các kiểu dữ liệu cơ bản, xử lý lỗi và ngoại lệ, cũng như quản lý việc nhập và xuất dữ liệu từ 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
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
Khi tham khảo một số lớp trong thư viện, bạn chỉ nên chỉ định rõ lớp nào thay vì khai báo toàn bộ gói bằng ký hiệu “*”, vì việc tham khảo toàn bộ gói sẽ làm tăng kích thướ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
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
1.5.2 Chương trình Java đầu tiên
1.5.2.1 Công cụ soạn thảo mã nguồn Java Để viết mã nguồn Java chúng ta có thể sử dụng trình soạn thảo NotePad hoặc một số môi trường phát triển hỗ trợ ngôn ngữ Java như: Jbuilder của hãng Borland, JDeveloper của hãng Oracle, Visual J++ của Microsoft…
Trong khuôn khổ giáo trình này để hướng dẫn sinh viên thực hành chúng tôi dùng công cụ NetBeans IDE 7.0 được phát triển bởi Sun Microsystems
NetBean IDE là một công cụ phát triển mạnh mẽ dành cho lập trình viên, cho phép viết, biên dịch, gỡ lỗi và triển khai chương trình, chủ yếu hỗ trợ ngôn ngữ Java nhưng cũng có thể làm việc với nhiều ngôn ngữ khác Với giao diện thân thiện và hiệu quả, NetBean IDE là lựa chọn tối ưu cho việc lập trình Java Sản phẩm này hoàn toàn miễn phí và không có giới hạn sử dụng, đồng thời dễ dàng cài đặt trên nhiều hệ điều hành như Windows, Linux, Mac OS X và Solaris Bạn có thể tải NetBean IDE tại trang web 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:
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
Hình 1 2 Vị trí lưu project
Bước 2 Soạn thảo mã nguồn tại cửa sổ bên dưới:
Hình 1.3 Màn hình soạn thảo
Chương trình sau cho phép hiển thị thông điệp: "Hello World Application!" package helloworldapp;
* @param args the command line arguments
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, bạn có thể vào mục Run/Run Main Project hoặc nhấn nút Run Main Project () trên thanh công cụ, hoặc đơn giản là nhấn phím F6 Kết quả sẽ được hiển thị trên màn hình.
1.5.2.3 Phân tích chương trình đầu tiên
Tập tin HelloWorldApp.Java sẽ nằm trong gói helloworldapp
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 trong Java được bao quanh bởi hai ngoặc móc “{” và “}”, đánh dấu sự bắt đầu và kết thúc của một khối lệnh Phương thức chính, được định nghĩa là public static void main(String args[ ]), là nơi mà chương trình bắt đầu thực thi, và tất cả các ứng dụng Java đều cần có 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 phương thức main được gọi mà không cần phải tạo ra một thể hiện của lớp, do đó nó không phụ thuộc vào các thể hiện của lớp đã được tạo ra.
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
CẤU TRÚC LẬP TRÌNH CƠ BẢN TRONG JAVA
Các kiểu dữ liệu
Kiểu dữ liệu cơ bản
Kiểu dữ liệu Kích thước
Phạm vi biểu diễn Giá trị mặc định byte 8 bits -128 ÷127 0 short 16 bits - 32768 ÷ 32767 0 int 32 bits -2 31 ÷ 2 31 -1 0 long 64 bits -2 63 ÷ 2 63 -1 0l
Kiểu dữ liệu Kích thước
Phạm vi biểu diễn Giá trị float 32 bits -3.4e38÷3.4e38 0.0f double 64 bits -1.7976 E+308÷+
Nhận giá trị true hoặc false Giá trị mặc định là false
Kiểu dữ liệu Kích thước
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
Dữ liệu kiểu lớp giao diện do người dùng định nghĩa bao gồm các phương thức của giao diện Ngoài ra, việc ép kiểu cho phép chuyển đổi giữa các kiểu dữ liệu cơ sở một cách linh hoạt.
Hình 2 1 Chuyển đổi giữa các kiểu dữ liệu cơ sở
Trong Java có hai loại ép kiểu dữ liệu:
Mở rộng (widening) là quá trình chuyển đổi kiểu dữ liệu từ kích thước nhỏ hơn sang kích thước lớn hơn mà không làm mất thông tin, chẳng hạn như chuyển từ kiểu int sang long Quá trình này thường được thực hiện ngầm định bởi trình biên dịch.
Thu hẹp (narrowwing) là quá trình chuyển đổi kiểu dữ liệu từ kích thước lớn hơn sang kích thước nhỏ hơn, thường dẫn đến việc làm tròn số và có thể gây mất thông tin Việc chuyển đổi kiểu này không thể được thực hiện tự động bởi trình biên dịch, do đó người dùng cần thực hiện chuyển đổi một cách tường minh.
Qui tắc ép kiểu có dạng: ()
Trong đó: : kiểu dữ liệu cần ép sang
: là biểu thức cần ép kiểu cho giá trị trả về của biểu thức
Ví dụ 2.1: float fNum = 2.2; int iCount = (int) fNum
Biến
Biến là vùng nhớ dùng để lưu trữ các giá trị của chương trình
Tên biến phải bắt đầu bằng một chữ cái, một dấu gạch dưới hay dấu dollar
Tên biến có phân biệt chữ hoa chữ thường
Trong Java, biến có thể được khai báo ở bất kỳ đâu trong chương trình
Cú pháp khai báo biến: dataType varName; hoặc dataType varName = value;
Trong đó, dataType là kiểu dữ liệu của biến, varName là tên biến, value là giá trị khởi tạo ban đầu cho biến varName
Phạm vi hoạt động của biến
Một biến có phạm vi hoạt động trong toàn bộ khối lệnh mà nó được khai báo, với khối lệnh được bắt đầu bằng dấu “{” và kết thúc bằng dấu “}” Các kiểu dữ liệu phổ biến bao gồm: byte, short, int, long, float, và double.
Nếu một biến được khai báo trong một phương thức mà không nằm trong bất kỳ khối lệnh nào, thì phạm vi hoạt động của biến đó chỉ giới hạn trong phương thức tương ứng Biến này có thể được sử dụng trong tất cả các khối lệnh của phương thức đó.
Nếu một biến được khai báo trong một lớp mà không nằm trong bất kỳ phương thức nào, biến đó sẽ có phạm vi hoạt động toàn bộ lớp Điều này có nghĩa là biến này có thể được sử dụng trong tất cả các phương thức của lớp tương ứng.
Toán tử
Toán tử số học yêu cầu các toán hạng phải là số hoặc ký tự, không chấp nhận toán hạng kiểu boolean Dưới đây là danh sách một số kiểu toán tử số học.
+ Trả về giá trị tổng hai toán hạng
- Trả về kết quả của phép trừ
* Nhân Trả về giá trị là tích hai toán hạng
/ Chia.Trả về giá trị là thương của phép chia
% Phép lấy modul Giá trị trả về là phần dư của phép chia
++ Tăng giá trị của biến lên 1 Ví dụ a++ tương đương với a = a + 1 Giảm giá trị của biến 1 đơn vị Ví dụ a tương đương với a = a - 1
-= 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
&& 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
Cú pháp: ? : ;
biểu thức 1: Biểu thức logic Trả trả về giá trị True hoặc False
biểu thức 2: Là giá trị trả về nếu xác định là True
biểu thức 3: Là giá trị trả về nếu xác định là False
Toán tử gán (=) cho phép gán giá trị cho một biến và có thể gán đồng thời nhiều giá trị cho nhiều biến Ví dụ, trong đoạn lệnh "int x = 20; int p, q, r, s; p=q=r=s=x;", giá trị của biến x được gán cho các biến p, q, r và s trong cùng một dòng lệnh.
Dòng lệnh cuối cùng thực hiện từ phải qua trái, bắt đầu bằng việc gán giá trị của biến x cho 's' Tiếp theo, giá trị của 's' được gán cho 'r' và quy trình này tiếp tục diễn ra.
Strings và StringBuider
Class String trong Java rất quan trọng, vì nó là một lớp không thể thay đổi (immutable) và là final, nghĩa là không cho phép bất kỳ lớp nào thừa kế từ nó Mọi thay đổi đối với một đối tượng String đều dẫn đến việc tạo ra một đối tượng String mới.
Trong Java, String là một lớp đặc biệt, được sử dụng phổ biến trong các chương trình, do đó cần có hiệu suất và tính linh hoạt cao Điều này lý giải tại sao String vừa mang tính chất đối tượng vừa có 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
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 lưu trữ trong một bể chung, cho phép hai string literal có nội dung giống nhau chia sẻ cùng một vùng bộ nhớ trên stack, từ đó tiết kiệm bộ nhớ hiệu quả.
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");
Các đối tượng String được lưu trữ trên Heap, dẫn đến việc quản lý bộ nhớ phức tạp và tiêu tốn không gian lưu trữ Hai đối tượng String có nội dung giống nhau sẽ được lưu trữ ở hai vùng bộ nhớ khác nhau trên Heap.
Các phương thức của String
Phương thức `charAt(int index)` trả về ký tự tại vị trí chỉ số được chỉ định trong chuỗi, trong khi phương thức `compareTo(Object o)` dùng để so sánh một chuỗi với một đối tượng khác.
Int compareTo (String anotherString) So sánh hai chuỗi theo từ điển
(Phân biệt chữ hoa chữ thường) int compareToIgnoreCase (String str) So sánh hai chuỗi theo từ điển
(Không phân biệt chữ hoa chữ thường) String concat (String str) Nối chuỗi boolean contentEquals (StringBuffer sb)
Hàm trả về true nếu chuỗi này tương ứng với chuỗi ký tự được quy định bởi StringBuffer Phương thức static String copyValueOf(char[] data) trả về một chuỗi đại diện cho chuỗi ký tự trong mảng đã cho Ngoài ra, phương thức static String copyValueOf(char[] data, int offset, int count) cũng được sử dụng để trả về chuỗi từ mảng ký tự với vị trí bắt đầu và số lượng ký tự nhất định.
Chuỗi trả về đại diện cho chuỗi ký tự trong mảng quy định Phương thức boolean endsWith (String suffix) kiểm tra xem chuỗi này có kết thúc với hậu tố được chỉ định hay không Phương thức boolean equals (Object anObject) dùng để so sánh chuỗi với một đối tượng khác.
So sánh với một String khác, không phân biệt chữ hoa chữ thường byte[] getBytes ( )
Mã hóa chuỗi thành một mảng byte bằng cách sử dụng bảng mã mặc định của nền tảng và lưu trữ kết quả vào một mảng byte mới.
Encode this string into a byte array using a predefined character table, and store the results in a new byte array The method used is void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin).
Sao chép các ký tự từ chuỗi nguồn vào mảng ký tự đích Hàm int indexOf(int ch) trả về chỉ số đầu tiên của ký tự cụ thể trong chuỗi Hàm int indexOf(int ch, int fromIndex) cho phép tìm kiếm ký tự bắt đầu từ chỉ số nhất định.
Hàm `indexOf` trong chuỗi trả về chỉ số xuất hiện đầu tiên của ký tự hoặc chuỗi được chỉ định, bắt đầu tìm kiếm từ chỉ số cụ thể đến cuối chuỗi Cú pháp là `int indexOf(String str)` để tìm ký tự đầu tiên và `int indexOf(String str, int fromIndex)` để tìm từ chỉ số đã cho.
Hàm `int indexOf(int ch)` trả về chỉ số của sự xuất hiện đầu tiên của ký tự trong chuỗi, bắt đầu từ chỉ số xác định Trong khi đó, hàm `int lastIndexOf(int ch)` trả về chỉ số của sự xuất hiện cuối cùng của ký tự cụ thể trong chuỗi Nếu cần tìm từ một vị trí cụ thể, bạn có thể sử dụng hàm `int lastIndexOf(int ch, int fromIndex)`.
Hàm `lastIndexOf(String str)` trả về chỉ số của lần xuất hiện cuối cùng của ký tự được chỉ định trong chuỗi, bắt đầu tìm kiếm từ chỉ số xác định Hàm `length()` cung cấp độ dài của chuỗi Hàm `matches(String regex)` kiểm tra xem chuỗi có khớp với biểu thức chính quy được chỉ định hay không Cuối cùng, hàm `regionMatches(int offset, String other, int ooffset, int len)` xác định xem có một phần nào đó của chuỗi giống với chuỗi khác hay không.
String replace (char oldChar, char newChar)
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
String replaceAll (String regex, String replacement)
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
String replaceFirst (String regex, String 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 thành các chuỗi con tại những vị trí khớp với biểu thức chính quy đã cho Sử dụng phương thức boolean startsWith(String prefix) để kiểm tra xem chuỗi có bắt đầu với tiền tố quy định hay không.
CharSequence subSequence (int beginIndex, int endIndex)
Trả về một chuỗi ký tự mới là một dãy con của dãy này
String substring (int beginIndex, int endIndex)
Trả về một chuỗi ký tự mới, là dãy con của chuỗi ban đầu từ chỉ số bắt đầu đến chỉ số kết thúc Phương thức char[] toCharArray() chuyển đổi 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
String toString ( ) Trả về String này
String toUpperCase ( ) Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương mặc định (default locale)
String toUpperCase (Locale locale) Chuyển tất cả các ký tự của chuỗi này sang chữ hoa, sử dụng miền địa phương (locale) cho trước
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
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
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) );
System.out.println("Please Enter Your Name:"); try
System.out.println("Hello " + name +"!");
Tham số đầu vào của BufferReader có thể là InputStreamReader hoặc FileReader (Dùng để đọc file)
Một số phương thức của lớp BufferReader:
- read() : đọc một kí tự
- readLine() : đọc một dòng text b Sử dụng JOptionPane
JOptionPane là một lớp thừa kế từ lớp JComponent Khi biên dịch chương trình sẽ hiện lên một dialog box cho phép nhập dữ liệu
Ví dụ 2.3: import javax.swing.JOptionPane; public class InputFromKeyboardJOptionPane
{ public static void main(String[] args)
String name = ""; name=JOptionPane.showInputDialog("Please enter your name");
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:
Các phương thức mô tả bao gồm: `public String next()` trả về nội dung trước khoảng trắng; `public String nextLine()` trả về 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; và `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());
Phương thức nextLine() sẽ trả về kết quả nội dung của một hàng, ví dụ:
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());
To calculate the sum of two numbers entered from the keyboard, we utilize the Scanner class in Java First, we import the necessary Scanner package and define our main class Inside the main method, we create a Scanner object to read user input We then prompt the user to enter two numbers, read these values, and compute their total Finally, we display the result of the addition.
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();
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
2.6.1 Lệnh, khối lệnh trong Java
Giống ngôn ngữ C, các câu lệnh trong Java kết thúc bằng dấu chấm phẩy (;)
Một khối lệnh là đoạn mã chương trình bao gồm từ hai lệnh trở lên, 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ọn (}) Khối lệnh có thể chứa nhiều lệnh hoặc các khối lệnh khác bên trong.
} // kết thúc khối lệnh 2 lệnh 1.1 lệnh 1.2
// Các lệnh thuộc khối lệnh 3
Java đưa ra hai cấu trúc lệnh if…else như sau:
là Biểu thức boolean như toán tử so sánh
, là câu lệnh đơn hoặc khối lệnh trong Java;
Ví dụ 2.5: Chương trình kiểm tra xem ngày hiện tại có phải chủ nhật hay không: import java.util.Date; public class TestIf { public static void main( String args[ ] ){
Date today = new Date(); if( today.getDay() == 0 )
System.out.println(“Hom nay la chu nhat\n”); else
System.out.println(“Hom nay khong la chu nhat\n" );
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ả
{ case ‘value1’: action 1 statement; break; case ‘value2’: action 2 statement; break; case ‘valueN’: actionN statement; break; default: default_action statement;
Biểu thức trong Java là một thành phần chứa giá trị xác định và phải có kiểu dữ liệu là char, byte, short hoặc int Nếu biểu thức có kiểu khác với các kiểu này, Java sẽ 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)
The code snippet provided demonstrates a switch-case structure that outputs the name of the day based on a numeric input Each case corresponds to a specific day of the week, with case 0 for Sunday, case 1 for Monday, case 2 for Tuesday, case 3 for Wednesday, case 4 for Thursday, case 5 for Friday, and case 6 for Saturday This structure allows for efficient day identification based on integer values.
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 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< )
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< là False
Kết quả in ra: Tong bang 100
Cú pháp: do{ action statements;
Vòng lặp do-while thực hiện khối lệnh khi điều kiện là True, giống như vòng lặp while, nhưng đảm bảo rằng khối lệnh sẽ được thực thi í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;
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< là False
Cú pháp: for(initialization statements; condition; increment 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< ; 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< là False
Trong cấu trúc lặp, việc điều khiển luồng thực hiện có thể được thực hiện thông qua các lệnh break và continue Lệnh break sẽ kết thúc quá trình lặp mà không thực hiện các câu lệnh còn lại, trong khi lệnh continue sẽ dừng thực thi phần còn lại của vòng lặp và chuyển điều khiển về điểm bắt đầu để 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
} 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
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
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));
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)
Hàm tạo này được sử dụng để chuyển biểu diễn Chuỗi thập phân của BigInteger thành BigInteger
BigInteger (Chuỗi val, int radix)
Hàm tạo này được sử dụng để chuyển biểu diễn Chuỗi của BigInteger trong cơ số được chỉ định thành BigInteger
Một số phương thức thường dùng:
BigInteger add (BigInteger val)- Phương thức này trả về một BigInteger có giá trị là (this + val)
Int compareTo(BigInteger val)- so sánhBigInteger với BigInteger xác định float floatValue() Chuyển BigInteger thành kiểu float
BigInteger max(BigInteger val) Trả về giá trị lớn nhất của BigInteger và val BigInteger min(BigInteger val) Trả về giá trị nhỏ nhất của BigInteger và val
Mảng
Mảng là tập hợp nhiều phần tử có cùng tên, cùng kiểu dữ liệu và mỗi phần tử trong mảng được truy xuất thông qua chỉ số trong mảng
Khai báo []; hoặc [] ;
Ví dụ 2.11: int arrInt[]; // đây là mảng các số nguyên hoặc int[ ] arrInt;
Để cấp phát bộ nhớ cho mảng trong Java, chúng ta sử dụng từ khóa "new", vì tất cả trong Java đều được thực hiện thông qua các đối tượng Ví dụ, để cấp phát vùng nhớ cho một mảng số nguyên có tối đa 100 phần tử, ta thực hiện như sau:
Truy nhập các phần tử trong mảng
Trong Java, chỉ số mảng bắt đầu từ 0, nghĩa là phần tử đầu tiên có chỉ số 0 và phần tử thứ n có chỉ số n-1 Để truy xuất các phần tử của mảng, bạn sử dụng chỉ số của chúng nằm 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
Trong Java, chuỗi được xử lý khác với các ngôn ngữ lập trình như C, nơi chuỗi được coi là mảng ký tự Java cung cấp lớp String để làm việc với các đối tượng chuỗi và thực hiện các thao tác liên quan đến loại dữ liệu này.
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: public class Test { public static void main(String args[]){ int [] numbers = {10, 20, 30, 40, 50}; for(int x : numbers ){
String [] names ={"James", "Larry", "Tom", "Lacy"}; for( String name : names ) {
2.8.2 Khởi tạo mảng và mảng nặc danh
Chúng ta có thể khởi tạo giá trị ban đầu cho các phần tử của mảng khi khai báo
Ví dụ về khai báo mảng trong lập trình: mảng số nguyên gồm 4 phần tử được định nghĩa là `int arrInt[] = {1, 2, 3, 4};`, mảng ký tự với 3 phần tử là `char arrChar[] = {'a', 'b', 'c'};`, và mảng chuỗi kiểu String gồm 3 phần tử được khai báo là `String arrStrng[] = {"ABC", "DEF", "GHI"};`.
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]);
Có thể sao chép một mảng vào một mảng khác, nhưng cả hai mảng phải tham chiếu đến cùng một địa chỉ Ví dụ, khi gán `int[] luckyNumbers = smallPrimes;` và thay đổi giá trị `luckyNumbers[5] = 12;`, thì `smallPrimes[5]` cũng sẽ nhận giá trị 12.
2.8.4 Các tham số dòng lệnh
Trong hàm main của Java, tham số string[] args cho phép nhận giá trị đầu vào dưới dạng một mảng các xâu ký tự, được truyền qua dòng lệnh.
Ví dụ 2.16: public class Message
{ public static void main(String[] args)
System.out.print("Hello,"); else if (args[0].equals("-g"))
// print the other command-line arguments for (int i = 1; i < args.length; i++)
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"
Chương trình cho kết quả
2.8.5 Sắp xếp mảng Để sắp xếp một mảng số ta dùng phương thức sort trong class Array int[ ] a= new int [10000];
Array.sort(a);// sẽ sắp xếp mảng a
Cú pháp khai báo mảng hai chiều:
[Kiểu_dữ_liệu] Tên_mảng[][]; Hoặc [Kiểu_dữ_liệu][][] Tên_mảng;
Cấp phát bộ nhớ cho mảng
Trong Java có 2 cách cấp phát bộ nhớ như sau:
[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];
[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ử trong mảng 2 chiều được truy cập bằng tên mảng kết hợp với chỉ số dòng và chỉ số cột tương ứng Cũng giống như mảng 1 chiều, trong mảng 2 chiều có m dòng và n cột, chỉ số dòng sẽ bắt đầu từ 0 đến m - 1, và chỉ số cột sẽ từ 0 đến 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
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++) {
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 phone35469L; d Fload pi=3.14159; e Double p14.159e-2;
In Java, the valid declarations for the main() method include: a) static void main(String arg[]) { /* */ }, and e) final static public void main(String[] arg) { /* */ } However, options b) public static int main(String arg[]) { /* */ }, c) public stactic void main(String arg[]) { /* */ }, and d) public stactic void main(String[] arg) { /* */ } are incorrect due to issues with return type and syntax errors.
Câu 4 Tìm câu trả lời đúng cho các câu hỏi sau: a) Trong cấu trúc if-else đơn (1 if và 1 else), ít nhất một khối lệnh (của if hoặc của else) sẽ được thực hiện Đúng hay sai? b) Trong cấu trúc lệnh switch-case, nếu không sử dụng “default”, có ít nhất một khối lệnh sẽ được thực hiện Đúng hay sai? c) Trong cấu trúc lệnh switch-case, khi có “default”, ít nhất một khối lệnh sẽ được thực hiện Đúng hay sai? d) Trong cấu trúc lệnh while, khối lệnh sẽ được thực hiện ít nhất một lần ngay cả khi điều kiện là False Đúng hay sai? e) Trong cấu trúc lệnh do-while, khối lệnh sẽ được thực hiện ít nhất một lần ngay cả khi điều kiện là False Đúng hay sai? f) Trong cấu trúc lệnh for, khối lệnh sẽ được thực hiện ít nhất một lần ngay cả khi điều kiện là 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
Public VOID main(String arg){ double fahreb.5; double celsius(fahre);
System.uot.println(fahre +’F=’+celsius+’c’);
Đoạn chương trình trên thực hiện vòng lặp do-while, trong đó biến i được khởi tạo bằng 0 và biến sum bằng 0 Vòng lặp sẽ tiếp tục cho đến khi biến i đạt giá trị nhất định Kết quả cuối cùng là tổng các giá trị của i từ 0 đến n-1, trong đó n là số lần lặp.
Đoạn chương trình trên thực hiện vòng lặp 5 lần và kết quả in ra là tổng của các số từ 5 đến 9, cụ thể là 35.
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;
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;
Khi dịch và chạy chương trình, giá trị của biến k được gán là 9, và mảng a sẽ có giá trị a[0] = 9 và a[1] = 6 Kết quả in ra sẽ là "i 9 6", trong đó i chưa được định nghĩa, dẫn đến lỗi biên dịch.
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));
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);
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]++;};
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ố
8545604 có tổng các chữ số là: 8+5+4+5+6+0+4= 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
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
7 Hiển thị số n thành tích các thừa số nguyên tố
Bài 11 Viết chương trình nhập vào vào mảng A có n phần tử, các phần tử là những số nguyên lớn hơn 0 và nhỏ hơn 100 được nhập vào từ bàn phím Thực hiện các chức năng sau: a) Tìm phần tử lớn nhất và lớn thứ 2 trong mảng cùng chỉ số của các số đó b) Sắp xếp mảng theo thứ tự giảm dần c) Nhập một số nguyên x và chèn x vào mảng A sao cho vẫn đảm bảo tính sắp xếp giảm dần và số phần tử của mảng vẫn không đổi
Bài 12 Nhập một dãy n số nguyên, tìm khoảng cách ngắn nhất giữa hai số liền kề nhau
Ví dụ: input 4 8 6 1 2 9 4, out put: Khoảng cách ngắn nhất là 1, giữa hai số 1 và 2
ĐỐI TƯỢNG, LỚP, KẾ THỪA, GIAO DIỆN
Đối tượng, lớp, lớp trừu tượng
Lớp được coi là khuôn mẫu cho đối tượng, bao gồm các thuộc tính và phương thức ảnh hưởng đến các thuộc tính đó.
Lớp SinhVien bao gồm các thuộc tính như MãSV, điểm, và hạnh kiểm, cùng với các phương thức học tập, thực hành và giải trí Mỗi đối tượng trong lớp này là một thể hiện của lớp, với các dữ liệu và hành vi được định nghĩa rõ ràng.
Mỗi sinh viên cụ thể là một thể hiện (hay đối tượng) của lớp SinhVien
3.1.2 Khai báo lớp class
Trong đó: class: là từ khóa của Java
ClassName là tên của lớp, trong khi field_1 và field_2 là các thuộc tính, tức là các biến hoặc thành phần dữ liệu của lớp Constructor là phương thức dùng để xây dựng và khởi tạo đối tượng của lớp Các phương thức như method_1 và method_2 thể hiện các thao tác xử lý và 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
// không chứa thuộc tính, phương thức
In this example, we create a Pencil class that includes properties such as color, length, and diameter, along with a unique identifier The class features a method called setColor that allows users to change the pencil's color The default color is set to red, while the length and diameter are defined as integer and float types, respectively Additionally, a static long variable, nextID, is used to generate unique IDs for each pencil instance.
3.1.3 Thuộc tính của lớp class
// khai báo những thuộc tính của lớp
field1;
- 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
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
- public: Có thể được truy cập từ bất kỳ một lớp nào
Ví dụ 3.4: public class Pencil { public String color = “red”; public int length; public float diameter; private float price; public static long nextID = 0; public void setPrice (float newPrice) { price = newPrice;
} public class CreatePencil { public static void main (String args[]){ Pencil p1 = new Pencil(); p1.price = 0.5f; // price has private access in Pencil
Các bổ nghĩa loại thuộc tính
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)
Khi truy cập thuộc tính từ bên ngoài lớp khai báo, cần phải sử dụng tên lớp trước thuộc tính, ví dụ: System.out.println(Pencil.nextID), hoặc thông qua một đối tượng liên quan đến lớp đó.
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 p2 = new Pencil(); Pencil.nextID++;
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 cần phải là hằng số, chúng ta có thể khởi tạo giá trị cho mọi biến nếu có quyền Nếu không được khởi tạo, giá trị mặc định sẽ phụ thuộc vào loại thuộc tính của biến đó.
()
: Để xác định quyền truy xuất của các đối tượng khác đối với các phương thức của lớp người ta thường dùng các tiền tố sau:
- Các chỉ định truy xuất của phương thức (cùng ý nghĩa với thuộc tính): public, protected, private
- Các bổ nghĩa loại phương thức: static, final, abstract, synchronized, native, volatile
: có thể là kiểu void, kiểu cơ sở hay một lớp
: đặt theo qui ước giống tên biến
: 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( );
- Sử dụng toán tử (.): reference.method(arguments)
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 đó
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
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 giữa hai danh sách đối số bằng cách so sánh số lượng và kiểu dữ liệu của các đối số trong từng danh sách, đồng thời chú ý đến thứ tự của chúng.
3.1.5 Chỉ định truy xuất lớp
Một lớp cũng có thể có các chỉ định truy xuất đi trước tên lớp:
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
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
Lớp không thể được thừa kế
Tạo đối tượng
In Example 3.9, we create a class named Student, which includes private fields for a long idNum, a String name initialized to "empty," and a String address Additionally, the class features a static long variable nextID set to 0, which will be used for generating unique student IDs Subsequently, we instantiate an object of the Student class based on this definition.
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 ();
Là một phương thức đặc biệt trong lớp, hàm khởi tạo được gọi tự động khi một thể hiện (đối tượng) của lớp được tạo ra, cho phép gán các giá trị mặc định cho các thuộc tính của đối tượng.
- 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 trong Java không định nghĩa constructor, hệ thống sẽ tự động cung cấp một constructor mặc định Các thuộc tính của lớp này sẽ được khởi tạo với các giá trị mặc định: kiểu số sẽ là 0, kiểu logic sẽ là false, và các đối tượng sẽ được khởi tạo là 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;
56 private String name= “empty”; private String address;
// bien static chia se giua cac doi tuong cua lop Student private static long nextID = 0;
// 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:
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
Student std = new Student(“Hai”, null);
Student std1 = new Student(“Hau”, “Hung Yen”);
Trong trường hợp này, chúng ta tạo ra hai đối tượng Student thông qua constructor với tham số đầu vào Đối tượng đầu tiên được khởi tạo với các giá trị: idNum = 0, name = “Hai”, address = null, và nextID = 1 Đối tượng thứ hai được khởi tạo với idNum = 1 (giá trị nextID hiện tại), name = “Hau”, address = “Hung Yen”, và nextID = 2 (giá trị nextID sau khi tăng).
- 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
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
Tính kế thừa trong Java là kỹ thuật cho phép một đối tượng nhận thuộc tính và hành vi từ đối tượng cha, giúp tái sử dụng các phương thức và thuộc tính của lớp cha Bằng cách này, lập trình viên có thể tạo ra các lớp mới dựa trên các lớp đã có, đồng thời bổ sung thêm các phương thức và thuộc tính mới Tính kế thừa thể hiện mối quan hệ IS-A, hay còn 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 là quá trình tạo ra một lớp mới từ một lớp đã tồn tại, trong đó tất cả các thuộc tính và phương thức của lớp cũ sẽ được chuyển giao cho lớp mới Lớp cũ được gọi là lớp cha, trong khi lớp mới được gọi là lớp con.
Khai báo lớp kế thừa được thực hiện bởi từ khoá extends:
extends
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;
System.out.println( name + “ is ” + age + “ years old!”);
// 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();
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;
System.out.println("Tenxe: " + tenxe + "hangxe: " + hangxe);
The class "oto" extends the "xe" class, incorporating attributes such as "giaban" (price) and "tocdo" (speed) The constructor of the "oto" class initializes these attributes while also calling the superclass constructor with the parameters "tenxe" (car name) and "hangxe" (brand).
} public class chitietxe { public static void main(String[] args) { oto xeoto = new oto("HCS","SNC",232,454); xeoto.Show();
In this example, the program illustrates the declaration of the overloaded Show() method in the car class (oto), without utilizing the Show() method from the vehicle class (xe) The car class extends the vehicle class, featuring attributes such as price (giaban) and speed (tocdo) The constructor of the car class initializes these attributes along with the vehicle's name and brand, ensuring a clear and distinct implementation of the Show() method.
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
Java không hỗ trợ kế thừa trực tiếp từ nhiều lớp cha để tránh sự phức tạp của tính chất đa kế thừa như trong C++ Thay vào đó, Java cho phép cài đặt nhiều giao diện (Interface), giúp lập trình viên có thể thừa hưởng các thuộc tính và phương thức từ các giao diện này.
Cú pháp khai báo một giao diện như sau:
[Tính chất] interface [extends ]{
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 bao gồm các giao diện cha đã được định nghĩa để kế thừa, được phân cách bởi dấu phẩy Phần trong ngoặc vuông “[ ]” là tùy 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
[Tính chất] ([]) [throws
Tính chất của thuộc tính và phương thức trong giao diện luôn được định nghĩa là public Nếu không được khai báo rõ ràng, giá trị mặc định sẽ là public Đối với thuộc tính, tính chất cần phải được khai báo là hằng (final) và tĩnh (static).
Kiểu giá trị trả về: có thể là các kiểu cơ bản của Java, cũng có thể là kiểu do người dùng tự định nghĩa (kiểu đối tượng)
Tên phương thức: tuân thủ theo quy tắc đặt tên phương thức của lớp
Các tham số: nếu có thì mỗi tham số được xác định bằng một cặp
Các tham số được phân cách nhau bởi dấu phẩy
Các ngoại lệ: nếu có thì mỗi ngoại lệ được phân cách nhau bởi dấu phẩy
Các phương thức của giao diện chỉ được định nghĩa dưới dạng mẫu mà không có các cài đặt chi tiết Điều này được thể hiện qua việc có dấu chấm phẩy ngay sau khai báo mà không có phần cài đặt nào trong dấu ngoặc.
“{}”) 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 đó
Các thuộc tính trong giao diện luôn được định nghĩa là hằng (final), tĩnh (static) và công khai (public), vì vậy cần phải gán giá trị khởi đầu ngay tại thời điểm 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{
Giao diện Product được khai báo với một thuộc tính lưu nhãn hiệu của nhà sản xuất sản phẩm và một phương thức để truy xuất giá bán của sản phẩm.
{ public static final String MARK = “Adidas”; public float getCost();
Giao diện chỉ có thể được khai báo thông qua các phương thức mẫu và thuộc tính hằng, do đó, để sử dụng giao diện, cần có một lớp cài đặt giao diện đó Việc khai báo lớp cài đặt giao diện được thực hiện bằng cách sử dụng từ khóa "implement".
class implements {
Tính chất và tên lớp được sử dụng như trong khai báo lớp thông thường
Một lớp trong lập trình có khả năng cài đặt nhiều giao diện, với các giao diện được phân cách bởi dấu phẩy Để đảm bảo hoạt động đúng, lớp đó cần phải cài đặt đầy đủ tất cả các phương thức của từng giao diện mà nó sử dụng.
Một phương thức được khai báo trong giao diện cần phải được cài đặt cụ thể trong lớp triển khai giao diện, và không được phép khai báo chồng Điều này có nghĩa là số lượng tham số của phương thức trong giao diện phải được giữ nguyên khi thực hiện cài đặt trong lớp.
Chương trình này minh họa cách cài đặt lớp giày (Shoe) bằng cách sử dụng giao diện Product, trong đó các thuộc tính và phương thức đã được khai báo trước đó.
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()
// Phương thức truy nhập nhãn hiệu sản phẩm public String getMark()
// Phương thức main public static void main(String args[])
System.out.println(“This shoe is ” + myShoe.getMark() +“ having a cost of
Phương thức getMark() trả về nhãn hiệu sản phẩm, thuộc tính đã được khai báo trong giao diện Trong khi đó, phương thức getCost() là cài đặt riêng của lớp Shoe cho phương thức đã được định nghĩa trong giao diện Product, với giá trị trả về là 10 cho lớp Shoe.
Lớp trừu tượng
Lớp trừu tượng là một loại lớp đặc biệt trong lập trình, nơi các phương thức chỉ được định nghĩa dưới dạng khuôn mẫu mà không có cài đặt cụ thể Các lớp con kế thừa từ lớp trừu tượng này sẽ thực hiện việc cài đặt chi tiết cho những phương thức đó.
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í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ớp trừu tượng có khả năng kế thừa từ một lớp khác, tuy nhiên, lớp cha cũng cần phải là một lớp trừu tượng Ví dụ, chương trình dưới đây khai báo lớp trừu tượng có tên là động vật (Animal) một cách tổng quát: 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 trong lớp trừu tượng cần phải được khai báo là trừu tượng Đồng thời, các phương thức của lớp này chỉ được định nghĩa dưới dạng khuôn mẫu mà không có phần chi tiết.
Cú pháp khai báo phương thức của lớp trừu tượng:
[public] abstract ([]) [throws ];
Tính chất của thuộc tính hoặc phương thức trong lớp trừu tượng luôn được thiết lập là public Nếu không có khai báo rõ ràng, giá trị mặc định cũng sẽ 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ó, được xác định bằng cặp và được phân cách bằng 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
Phương thức trừu tượng không thể được khai báo là private hoặc static, vì chúng chỉ có thể được nạp chồng trong các lớp kế thừa Nếu một phương thức là private, nó sẽ không thể nạp chồng, trong khi nếu là static, nó sẽ 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 định nghĩa hai phương thức trừu tượng trong lớp Animal Phương thức getName() có nhiệm vụ trả về tên loài động vật dưới dạng chuỗi (String), trong khi phương thức getFeet() cung cấp số chân của loài động vật đó dưới dạng số nguyên (int).
Sử dụng lớp trừu tượng
Lớp trừu tượng hoạt động thông qua các lớp dẫn xuất của nó, vì chỉ các lớp dẫn xuất mới có khả năng cài đặt cụ thể các phương thức được khai báo trong lớp trừu tượng.
Lớp Bird kế thừa từ lớp Animal và cài đặt hai phương thức getName() và getFeet() Phương thức getName() trả về tên loài là “Bird”, trong khi phương thức getFeet() trả về số chân của loài chim là 2.
// Trả về tên loài chim public String getName()
// Trả về số chân của loài chim public int getFeet()
Chúng ta xây dựng lớp mèo (Cat) kế thừa từ lớp động vật (Animal) Lớp mèo cài đặt hai phương thức từ lớp động vật: phương thức getName() trả về tên loài là "Cat" và phương thức getFeet() trả về số chân của mèo là 4 public class Cat extends Animal.
// Trả về tên loài mèo public String getName()
// Trả về số chân của loài mèo public int getFeet()
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[])
System.out.println(“The ” + myBird.getName() + “ has ”+ myBird.getFeet() +
“ feets”); Cat myCat = new Cat();
System.out.println(“The ” + myCat.getName() +“ has ”+ myCat.getFeet() + “ feets”); }}
Lớp Object
Trong Java, tất cả các lớp đều kế thừa từ lớp Object, có nghĩa là lớp Object là lớp cha của mọi lớp khác Điều này cho phép sử dụng biến kiểu Object để tham chiếu đến các đối tượng thuộc bất kỳ kiểu nào.
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
Employee [ ] staff = new Employee[ ]; obj= staff; obj= new int [10];
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
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;
// Kiểm tra các trường có các giá trị giống nhau
72 return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay);
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+ "]";
Giao diện
Interface trong Java là một bản thiết kế cho lớp, bao gồm các phương thức trừu tượng Đây là kỹ thuật giúp đạt được tính trừu tượng hoàn toàn và hỗ trợ đa kế thừa Interface cũng thể hiện mối quan hệ IS-A, nhưng không thể được khởi tạo như lớp trừu tượng.
Một interface trong Java là tập hợp các phương thức trừu tượng mà không phải là một lớp Khi một class triển khai một interface, nó sẽ kế thừa các phương thức abstract từ interface đó Trong khi lớp mô tả các thuộc tính và hành vi của một đối tượng, interface lại định nghĩa các hành vi mà các class sẽ thực hiện.
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
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[]){
3.6.2 Mục đích sử dụng giao diện
Trong một số trường hợp, việc sử dụng Interface là cần thiết để các nhóm phát triển khác nhau có thể thống nhất về một “hợp đồng”, từ đó xác định cách thức tương tác của phần mềm với nhau.
Mỗi nhóm nên có cách viết code theo cách riêng mà không cần biết cách các nhóm khác làm thế nào
Các interface trong java có thể được sử dụng để xác định các contracts
Interface không thuộc bất kỳ lớp nào cho dù chúng làm việc kết hợp với các lớp khác
Java không cho phép đa kế thừa trong nhưng interface hỗ trợ điều đó
Trong Java, một class có thể kế thừa từ chỉ một class nhưng nó có thể implement nhiều interfaces
3.6.3 So sánh giao diện và lớp trừu tượng
Cả lớp trừu tượng và Interface đều phục vụ cho việc đạt được tính trừu tượng trong lập trình, cho phép khai báo các phương thức trừu tượng mà không thể khởi tạo Tuy nhiên, giữa chúng tồn tại những điểm khác biệt quan trọng.
Lớp trừu tượng có thể có các phương thức abstract và non-abstract
Interface chỉ có thể có phương thức abstract
Lớp trừu tượng không hỗ trợ đa kế thừa Interface hỗ trợ đa kế thừa
Lớp trừu tượng có thể có các biến final, non- final, static và non-static
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
Interface không thể có phương thức static, main hoặc constructor
Từ khóa abstract được sử dụng để khai báo lớp trừu tượng
Từ khóa interface được sử dụng để khai báo Interface
Lớp trừu tượng có thể cung cấp trình triển khai của Interface
Interface không cung cấp trình triển khai cụ thể của lớp abstract
Ví dụ: public abstract class Shape{ public abstract void draw(); }
Ví dụ: public interface Drawable{ void draw(); }
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)
Lớp Outer định nghĩa bên ngoài, trong khi 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: lớp lồng tĩnh (static) và lớp nội (no-static).
Lớp nội được liên kết với lớp bao bọc và có khả năng truy cập các phương thức và thuộc tính, bao gồm cả những thành phần được khai báo là private Để tạo một đối tượng lớp nội, trước tiên cần tạo lớp ngoài, sau đó sử dụng cú pháp thích hợp để khởi tạo đối tượng lớp nội.
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
Lớp lồng static (tĩnh) được kết nối với đối tượng trong lớp bao bọc, nhưng không cho phép truy xuất các thuộc tính và phương thức của đối tượng đó Để truy cập lớp lồng static, bạn cần sử dụng dấu chấm kèm theo tên lớp.
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 dễ đọc và dễ bảo trì cho code
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.Inner in = obj.new Inner(); in.msg();
Lớp nội nặc danh (anonymous inner class) là một loại lớp không có tên, thường được sử dụng để ghi đè phương thức của lớp hoặc interface Có hai cách để tạo ra lớp nội nặc danh trong Java.
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");
A local inner class is defined within a method, allowing it to access the method's variables To invoke the methods of this inner class, an instance of the class must be created within the containing method For example, in the `localInner1` class, there is a private instance variable `data` initialized to 30, and a method `display()` that includes a local inner class named `Local`, which contains a method `msg()`.
77 public static void main(String args[]) { localInner1 obj = new localInner1(); obj.display();
Xử lý ngoại lệ
Exception là lỗi đặc biệt xuất hiện trong quá trình thực thi chương trình, gây ra các trạng thái không bình thường Nếu không được xử lý, các trạng thái này có thể dẫn đến việc chương trình kết thúc đột ngột, chẳng hạn như việc chia cho 0 Ngôn ngữ Java cung cấp cơ chế xử lý ngoại lệ hiệu quả, giúp hạn chế tối đa nguy cơ hệ thống bị hỏng hay ngắt đột ngột, làm cho Java trở thành một ngôn ngữ lập trình mạnh mẽ.
3.8.2 Mục đích của việc xử lý ngoại lệ
Một chương trình cần có cơ chế xử lý ngoại lệ để tránh bị ngắt khi xảy ra lỗi Nếu không, tài nguyên mà hệ thống đã cấp sẽ không được giải phóng, dẫn đến lãng phí Do đó, việc thu hồi tất cả các nguồn tài nguyên là rất quan trọng để bảo đảm hiệu quả sử dụng.
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
Khi một ngoại lệ xảy ra, một đối tượng tương ứng sẽ được tạo ra và truyền cho phương thức nơi ngoại lệ đó xảy ra Đối tượng này chứa thông tin chi tiết về ngoại lệ Ví dụ, trong Java, lớp public class Exception extends Throwable được sử dụng để xử lý các ngoại lệ.
Lớp 'Throwable' là lớp cha của tất cả các ngoại lệ trong Java, cho phép nhận và xử lý thông tin ngoại lệ một cách hiệu quả.
3.8.5 Mô hình xử lý ngoại lệ
Mô hình xử lý ngoại lệ trong Java, được gọi là ‘catch and throw’, cho phép chặn các ngoại lệ khi chúng xảy ra và chuyển hướng chương trình đến khối xử lý ngoại lệ Lập trình viên cần phải xử lý các ngoại lệ khác nhau phát sinh trong chương trình, đảm bảo rằng chúng được xử lý đúng cách hoặc thoát khỏi chương trình khi cần thiết.
Dưới đây là cấu trúc của mô hình xử lý ngoại lệ: try
// đoạn mã có khả năng gây ra ngoại lệ
// 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
// 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
// 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
// khối lệnh nay luôn được thực hiện cho dù ngoại lệ có xảy ra hay không }
3.8.5.1 Các ưu điểm của mô hình ‘catch và throw’
Mô hình ‘catch và throw’ có hai ưu điểm:
Người lập trình chỉ phải xử lý ngoại lệ khi cần thiết Không cần phải thực hiện tại mọi mức
Thông báo lỗi có thể được hiện ra khi tiến hành xử lý ngoại lệ
3.8.5.2 Các khối ‘try’ và ‘catch’
Khối 'try-catch' được áp dụng để thực hiện mô hình 'catch và throw' trong xử lý ngoại lệ Một hoặc nhiều khối 'catch' có thể được đặt sau khối 'try', nhằm bắt các ngoại lệ có thể xảy ra trong khối '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();
Đối tượng 'e' thuộc lớp 'Exception' cho phép chúng ta in ra các chi tiết liên quan đến 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 ảnh dưới đây minh họa kết quả của phương thức này.
Để xử lý ngoại lệ trong lập trình, chúng ta cần xác định kiểu ngoại lệ cụ thể bằng cách sử dụng khối try-catch, ví dụ: catch(Exception e) Khi không rõ kiểu ngoại lệ, lớp 'Exception' có thể được sử dụng để bắt ngoại lệ đó.
3.8.6 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
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
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 khối 'try' lồng nhau, khối 'try' bên trong sẽ được thực thi trước Mọi ngoại lệ phát sinh trong khối 'try' này sẽ được xử lý bởi các khối 'catch' theo sau Nếu không tìm thấy khối 'catch' phù hợp, các khối 'catch' của khối 'try' bên ngoài sẽ được xem xét Nếu vẫn không có khối 'catch' nào, Java Runtime Environment sẽ chịu trách nhiệm 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(“Cannot Divide by zero”);
Kết xuất của chương trình:
Trong chương trình này, việc chia cho 0 dẫn đến một ngoại lệ không hợp lệ Ngoại lệ này được bắt giữ trong khối catch, nơi ta xác định loại ngoại lệ có thể xảy ra Đối tượng 'a' của ArithmeticException được sử dụng để in chi tiết về ngoại lệ Nếu thay thế lệnh 'System.out.println' trong khối catch bằng 'System.out.println(a.getMessage())', chương trình sẽ cho kết quả khác.
Khi sử dụng các khối 'try' mà không kèm theo khối 'catch', chương trình sẽ biên dịch thành công nhưng sẽ gặp lỗi và ngắt khi thực thi do ngoại lệ không được xử lý.
Khối 'finally' thực hiện việc thu dọn tài nguyên khi có ngoại lệ xảy ra và có thể kết hợp với khối 'try' Trong khối 'finally', các câu lệnh thu hồi tài nguyên hoặc thông báo sẽ được thực hiện.
Đó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 try{ doSomethingThatMightThrowAnException();
Phương thức ‘cleanup()’ sẽ được gọi khi ‘doSomething That Might Throw AnException()’ phát sinh ngoại lệ, đồng thời cũng được thực hiện ngay cả khi không có ngoại lệ xảy ra, tiếp tục thực hiện các lệnh trong khối ‘finally’.
Khối 'finally' là tùy chọn và không bắt buộc, thường được đặt sau khối 'catch' cuối cùng Chương trình sẽ thực thi câu lệnh đầu tiên trong 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
FinallyDemo(String args[]){ try{ name=new String(“Aptech Limited”);
System.out.println(“Division Result is” + no1/no2);
System.out.println(“Cannot Divide by zero”);
}finally{ 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:
3.8.8 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 từ khóa 'throw', với toán hạng là một đối tượng thuộc lớp kế thừa từ 'Throwable' Ví dụ, trong đoạn lệnh sau, nếu biến 'flag' nhỏ hơn 0, sẽ ném ra một ngoại lệ do người dùng định nghĩa: 'throw new MyException();'.
Giao diện Collection
Cấu trúc tập hợp cho phép nhóm các đối tượng lại để xử lý như một đơn vị Các đối tượng có thể được lưu trữ, tìm kiếm và thao tác như các phần tử của tuyển tập, trong đó các phần tử không đồng nhất.
Cấu trúc list là một tập hợp các phần tử được sắp xếp theo thứ tự và cho phép lặp lại các phần tử giống nhau Ngoài các hàm kế thừa từ Collection, List còn cung cấp thêm nhiều hàm bổ sung hữu ích.
- 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
ArrayList là một lớp kiểu mảng động trong Java, cho phép thay đổi kích thước linh hoạt, và là cấu trúc hiệu quả nhất để cài đặt danh sách List Với tính năng và sự linh hoạt vượt trội, ArrayList được nhiều lập trình viên ưa chuộng hơn so với mảng truyền thống, vốn bị giới hạn về kích thước Khi các phần tử trong mảng truyền thống bị xóa, bộ nhớ không được thu hồi, trong khi kích thước của ArrayList có thể tăng hoặc giảm tùy theo nhu cầu sử dụng.
Có 2 kiểu khởi tạo ArrayList, đó là non-generic và generic
ArrayList list = new ArrayList(); // non-generic - kiểu cũ
ArrayList list = new ArrayList(); // 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) được sử dụng để thêm phần tử vào cuối danh sách, trong khi phương thức void add(int index, Object element) cho phép chèn phần tử tại vị trí chỉ định trong danh sách.
Get (int i) Trả về đối tượng tại vị trí i trong danh sách
Remove (int index) Xóa đối tượng tại vị trí có chỉ số index
The `boolean contains(Object o)` method returns true if the list contains the specified object The `isEmpty()` method returns true if the list is empty The `object get(int index)` method retrieves the object at the specified index in the ArrayList Additionally, the `int size()` method returns the number of elements present in the 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*/
ArrayList obj = new ArrayList();
/*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);
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
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 list = new LinkedList(); // 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)` cho phép thêm phần tử vào cuối danh sách, trong khi `void add(int index, Object element)` dùng để chèn phần tử tại vị trí chỉ định trong danh sách Để chèn phần tử vào đầu danh sách, sử dụng `void addFirst(Object o)`, và để thêm vào cuối danh sách, dùng `void addLast(Object o)` Phương thức `int size()` trả về số lượng phần tử trong danh sách, còn `boolean contains(Object o)` kiểm tra xem danh sách có chứa phần tử chỉ định hay không Cuối cùng, `boolean remove(Object o)` được sử dụng để xóa phần tử đầu tiên được chỉ định trong danh sách.
Object getFirst( ) Được sử dụng để trả về phần tử đầu tiên trong một danh sách
Phương thức `getLast()` được sử dụng để lấy phần tử cuối cùng trong danh sách, trong khi phương thức `indexOf(Object o)` trả về chỉ mục của phần tử được chỉ định nếu nó xuất hiện lần đầu tiên trong danh sách, hoặc -1 nếu danh sách không chứa phần tử đó.
Phương thức `lastIndexOf(Object o)` được sử dụng để tìm chỉ mục của lần xuất hiện cuối cùng của một phần tử trong danh sách Nếu phần tử không tồn tại trong danh sách, phương thức sẽ trả về giá trị -1.
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
Phương thức get(Object key) cho phép bạn truy xuất giá trị tương ứng với key đã chỉ định Nếu key có ánh xạ trong cấu trúc dữ liệu, giá trị sẽ được trả về; ngược lại, nếu không có ánh xạ nào, phương thức sẽ trả về null.
- 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 này cung cấp giao diện Map và được xây dựng trong java.lang, cho phép người dùng tạo ra các ánh xạ mới với kích thước tùy ý, có thể là rỗng hoặc đầ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(" Cac phan tu cua Map");
Các phần tử của Map
Chương trình Java dưới đây minh họa các phương thức của lớp HashMap Để sử dụng lớp này, cần import gói java.util.* Trong phương thức main, bạn có thể thấy cách thức hoạt động và các tính năng của HashMap được trình bày rõ ràng.
In a Java HashMap, various elements can be added using the put method, such as "Zara" with a value of 3434.34, "Mahnaz" with 123.22, "Ayan" with 1378.00, "Daisy" with 99.22, and "Qadir" with a negative value of -19.08.
// Lay mot tap hop cac entry
// 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());
// Gui 1000 vao trong tai khoan cua Zara double balance = ((Double)hm.get("Zara")).doubleValue(); hm.put("Zara", new Double(balance + 1000));
System.out.println("Balance hien tai cua Zara la: " + hm.get("Zara"));
Kết quả chạy chương trình:
Collection Framework trong Java là một cấu trúc thống nhất cho việc biểu diễn và thao tác với các tập hợp dữ liệu Nó cung cấp cho lập trình viên quyền truy cập vào các cấu trúc dữ liệu đã được đóng gói sẵn và các thuật toán để thao tác trên chúng Các lớp và giao diện của Collection Framework được tổ chức 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
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
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 Mô tả
Collection 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
Set 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
SortedSet kế thừa từ Set, cung cấp cấu trúc tập hợp được sắp xếp mà không cho phép các phần tử lặp lại Trong SortedSet, các phần tử không được sắp xếp theo thứ tự cụ thể.
List 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
Map 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ị
SortedMap Kế thừa từ Map để cài đặt các ánh xạ khóa theo thứ tự
Trên đỉnh của cấu trúc phân cấp, chúng ta có interface Collection, nơi cung cấp các phương thức như size và isEmpty để cung cấp thông tin về số lượng phần tử trong collection.
- 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
Ví dụ 3.33: public interface Iterator {
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)
Thread
3.10.1 Thread là gì, Tạo Thread
Thread là một chuỗi các điều khiển trong một tiến trình hoặc ứng dụng Trong Java, người lập trình có thể phát triển các chương trình đa luồng, trong đó mỗi luồng được gán một nhiệm vụ cụ thể và thực hiện đồ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(){
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(){
3.10.2 Các trạng thái của Thread
Một thread trải qua nhiều giai đoạn khác nhau trong vòng đời của nó Quá trình này bắt đầu từ khi thread được sinh ra, tiếp theo là giai đoạn khởi động, sau đó là giai đoạn chạy, và cuối cùng là giai đoạn hủy Hình ảnh minh họa dưới đây thể hiện rõ ràng vòng đời đầy đủ của một thread.
Hình 3.3 Các trạng thái của Thread
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
Sau khi một thread mới được khởi tạo, nó sẽ chuyển sang trạng thái runnable, tức là thread đó đang sẵn sàng để thực hiện tác vụ của mình Trong trạng thái runnable, thread được coi là đang thực hiện nhiệm vụ.
Trong lập trình đa luồng, một thread có thể rơi vào trạng thái chờ (waiting) khi nó đang đợi một thread khác thực hiện một tác vụ nào đó Chỉ khi thread khác gửi tín hiệu, thread đang chờ mới có thể chuyển về trạng thái runnable và tiếp tục thực thi.
Timed waiting là trạng thái mà một thread đang ở trạng thái runnable có thể chuyển sang trong một khoảng thời gian nhất định Khi thời gian chờ kết thúc hoặc sự kiện mà thread đang chờ xảy ra, thread sẽ trở lại trạng thái runnable.
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
3.10.3 Các phương thức của lớp Thread
- Đâ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
- 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
- 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
- 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ó
- Đâ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
- 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
- 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
Java cung cấp các lớp điều khiển để quản lý chương trình đa luồng, cho phép người dùng phát triển các chương trình với các giai đoạn như tạm ngưng, tiếp tục hoặc dừng hoàn toàn Để quản lý và điều khiển luồng, người dùng có thể sử dụng các phương thức phù hợp.
Phương thức ngưng luồng thi hành không an toàn, do đó, nên gán giá trị null vào biến Thread để dừng luồng thay vì sử dụng phương thức stop().
Trong Java 2, phương thức tạm ngừng luồng ít được sử dụng vì không giải phóng tài nguyên, dẫn đến nguy cơ deadlock Thay vào đó, nên sử dụng phương thức wait() để quản lý luồng hiệu quả hơn.
Tiếp tục vận hành luồng nếu nó đang bị ngưng, trong khi nếu luồng đang thi hành thì phương thức này sẽ bị bỏ qua Phương thức này thường được sử dụng kết hợp với phương thức suspend() Tuy nhiên, từ Java 2 trở đi, cả phương thức này và phương thức suspend() không còn được khuyến nghị sử dụng, do đó nên dùng phương thức notify() để quản lý luồng hiệu quả hơn.
The public static void sleep(long millis) method in Java pauses the execution of the current thread for a specified duration in milliseconds It is important to note that sleep() is a static method and can throw an InterruptedException if the thread is interrupted while sleeping.
public void destroy() Hủy luồng đang thi hành
Trong Java có hai loại luồng:
- Luồng người sử dụng : Do người dùng tạo ra
Luồng chạy ngầm (daemon) trong máy ảo Java cung cấp dịch vụ cho các luồng khác và chỉ còn hoạt động khi tiến trình chính thoát Một ví dụ điển hình của luồng deamon là luồng "garbage collection", chịu trách nhiệm thu lượm tài nguyên không còn sử dụng.
Luồng dọn rác chỉ hoạt động khi hệ thống không có tác vụ nào đang chạy, và nó có quyền ưu tiên thấp Lớp luồng cung cấp hai phương thức để tương tác với luồng daemon.
- public void setDaemon(boolean on)
Đa luồng và tương tranh
Đ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, giúp quản lý nhiều tác vụ tương tranh dựa trên độ ưu tiên Hệ thống xử lý đa luồng trong Java được xây dựng dựa trên class Thread và interface Runnable trong package java.lang Việc sử dụng đa luồng không chỉ tăng cường hiệu suất xử lý của các hệ thống đơn mà còn giảm thiểu 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 {
} public static void main(String args[]) {
98 t1.setName(“Thread2”); t2.setName(“Thread3”); t1.start(); t2.start();
System.out.println(“Number of threads running: ” + Thread activeCount());
Ví dụ 3.36: Tạo 2 luồng public class Thread1 extends Thread {
@Override public void run() { while(true) { name = Thread.currentThread().getName(); for (int i=1;i