Tìm hiểu SOLID – 5 nguyên tắc thiết kế hướng đối tượng bằng Java

Ngôn ngữ lập trình Java

Java được biết đến là ngôn ngữ lập trình bậc cao, hướng đối tượng và giúp bảo mật mạnh mẽ, và còn được định nghĩa là một Platform. Java được phát triển bởi Sun Microsystems, do James Gosling khởi xướng và ra mắt năm 1995. Java vẫn được sử dụng rất nhiều trong các dự án công nghệ mới. Tên ban đầu của Java là OAK, sau đó được Sun Microsystem đổi tên vào năm 1995 và tập trung phát triển các sản phẩm theo trend www (world wide web). Năm 2009, Java được mua lại bởi Oracle.

Các nguyên tắc thiết kế hướng đối tượng – SOLID

image 18 - quochung.cyou PTIT

SOLID nghĩa là gì

Nguyên tắc SOLID là một phương pháp tiếp cận hướng đối tượng trong thiết kế cấu trúc phần mềm được sử dụng trong Java. Robert C. Martin là người đã đưa ra ý tưởng này (còn được biết đến với tên Uncle Bob). Năm nguyên tắc này đã làm thay đổi toàn bộ lĩnh vực lập trình hướng đối tượng, cũng như cách các phần mềm được viết ra. Nguyên tắc SOLID cũng đảm bảo rằng phần mềm có tính chất mô-đun (dễ tái sử dụng và dùng trong nhiều vị trí), dễ hiểu, dễ gỡ lỗi và dễ refactor (tái cấu trúc, cập nhật).

  • S: Single responsibility principle – Nguyên tắc một chức năng
  • O: Open-closed principle – Nguyên tắc đóng và mở
  • L: Liskov substitution principle – Nguyên tắc thay thế
  • I: Interface segregation principle – Nguyên tắc chia nhỏ
  • D: Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc

Lí do nên áp dụng các nguyên tắc SOLID

  • Clean: Nguyên tắc SOLID giúp code clean, dễ nhìn hơn và chuẩn hoá format của code để nhiều người hiểu hơn.
  • Dễ bảo trì: Code dễ bảo trì, sửa lỗi hơn.
  • Tính co giãn: Dễ dàng refactor, tái cấu trúc lại code. Dễ dàng phát triển các tính năng mới sau này
  • Tối ưu: Giảm bớt các code dư thừa.
  • Kiểm nghiệm: Viết unit test dễ hơn
  • Dễ đọc: Giúp code đọc dễ hiểu hơn
  • Độc lập: Code hoạt động độc lập, ít phụ thuộc các phần khác, giảm thiểu lỗi
  • Tái sử dụng: Code được chia nhỏ và độc lập như các module, dễ dàng sử dụng lại.

SOLID trong Java

image 12 - quochung.cyou PTIT

Nguyên tắc 1: Single responsibility principle – Nguyên tắc một chức năng

image 13 - quochung.cyou PTIT

Nguyên tắc này được phát biểu như sau:

Một class chỉ nên giữ 1 trách nhiệm duy nhất, chỉ có thể sửa đổi class với 1 lý do duy nhất.

A class should have one and only one reason to change, meaning that a class should have only one job.

Theo nguyên lí này, mỗi class chỉ nên có một vai trò duy nhất. Tức là bạn có thể để 1 class có rất nhiều chức năng, làm đủ thứ, với nhiều method khác nhau. Tuy nhiên việc nhét toàn bộ chức năng vào 1 class khiến code khó bảo trì, khó hiểu hơn, và xử lí một phần nhỏ của cả một class chứa rất nhiều chức năng này có thể làm lỗi toàn bộ các chức năng khác.

Hãy thử xem một class sau:

image 14 - quochung.cyou PTIT

Việc cho toàn bộ các phương thức gộp vào 1 class NguoiChoi như này đã vi phạm quy tắc, thực hiện rất nhiều thay đổi chỉ trong 1 class như lấy dữ liệu từ database, chuyển sang json để trả về, di chuyển nhân vật, …. Sau này khi nâng cấp thêm chức năng, class này ngày càng phình to ra. Khiến cho việc bảo trì, nâng cấp, test, …. trở lên khó khăn hơn sau này.

Thay vì vậy, ta có thể chuyển thành như sau

image 15 - quochung.cyou PTIT
image 16 - quochung.cyou PTIT
image 17 - quochung.cyou PTIT
luong srp - quochung.cyou PTIT
  • Lúc này mỗi class sẽ độc lập hơn và các luồng hoạt động cũng sẽ rõ ràng hơn, khi có lỗi xảy ra hay cần nâng cấp chức năng, bạn có thể dễ dàng sửa đổi vào các class trong 1 luồng chứ không phải thay đổi hay thêm mọi thứ vào 1 class và khiến chúng phình to hơn nữa.
  • Tuy số lượng class nhiều hơn những việc sửa chữa sẽ đơn giản hơn, dễ dàng tái sử dụng hơn, class ngắn hơn nên cũng ít bug hơn.
  • Một số ví dụ về nguyên tắc SRP cần xem xét có thể cần được tách riêng bao gồm: Persistence, Validation, Notification, Error Handling, Logging, Class Instantiation, Formatting, Parsing, Mapping, …

Nguyên tắc 2: Open-closed principle – Nguyên tắc đóng và mở

Nguyên tắc này được phát biểu như sau:

Có thể thoải mái mở rộng 1 class, nhưng không được sửa đổi bên trong class đó.

Objects or entities should be open for extension, but closed for modification.

image 19 - quochung.cyou PTIT

Nghe qua thấy nguyên lý có sự mâu thuẫn do thường chúng ta thấy rằng dễ mở rộng là phải dễ thay đổi, đằng nay dễ mở rộng nhưng không cho thay đổi. Thực sự theo nguyên lý này, chúng ta không được thay đổi hiện trạng của các lớp có sẵn, nếu muốn thêm tính năng mới, thì hãy mở rộng class cũ bằng cách kế thừa để xây dựng class mới. Làm như vậy sẽ tránh được các tình huống làm hỏng tính ổn định của chương trình đang có.

Theo nguyên tắc này, sau khi thiết kế một class với một số chức năng nhất định, cần đảm bảo các chức năng này hoạt động trơn tru trong tương lai, tránh sửa đổi thêm sau này. Như vậy, class luôn “đóng – closed” cho các sửa đổi vào các chức năng đã được thiết kế trước, nhưng lại phải “mở – open” để mở rộng tính năng hơn, để mở thì có 1 số cách phổ biến như:

  • Tạo ra một class kế thừa
  • Viết lại chức năng hàm đó từ class cha
  • Nâng cấp chức năng/hàm của class cha ở class con

Lấy ví dụ như sau:

image 21 - quochung.cyou PTIT

Với cách thiết kế như trên, khi ta có các class con kế thừa từ class cha NguoiChoi, và cần kiểm tra xem class con có hệ là gì, hay ví dụ ta cần tạo thêm nhiều class con khác tương tự, ta lại phải thêm nhiều if else vào class gốc. Thay vào đó, ta nên thiết kế như sau:

image 22 - quochung.cyou PTIT

Lúc này, khi cần nâng cấp thêm nhiều hệ mới cho hệ thống, ta chỉ cần tạo các class con và sử dụng chức năng của class chính, không cần thực hiện trực tiếp vào class chính nữa.

  • Lợi ích của nguyên lý này là đôi khi chúng ta cần sử dụng các class từ các nguồn thư viện thứ 3, hoặc từ chính các thư viện có sẵn trong Java. Chúng ta có thể dễ dàng extend và tạo các class con mới kế thừa từ class cha để phục vụ cho một mục đích, chức năng mới của dự án, mà không cần quá lo lắng về class cha sẽ bị lỗi do ta đã không sửa đổi chúng.
  • Tuy nhiên, việc kế thừa class cha có thể dẫn tới việc chức năng các class con lại quá khác nhau và không có chung ý nghĩa, nên chú ý vào ý nghĩa của các chức năng, tránh tạo ra quá nhiều class dẫn xuất. Mặc dù những sửa đổi nhỏ trong class thường không ảnh hưởng, chúng ta cũng cần phải test cẩn thận. Và đó là lý do chính tại sao chúng ta cần phải viết test case cho các chức năng, để có thể nhận thấy hành vi không mong muốn xảy ra trong code.
  • Lúc này, ta có thể sử dụng interface như các bản thiết kế cha để có thể làm các chức năng mở rộng sau này, việc sử dụng interface cũng giúp code đạt thêm tính “Loose coupling” – “liên kết lỏng” hơn và tránh sự phụ thuộc quá chặt chẽ vào các class.

Nguyên tắc 3: Liskov substitution principle – Nguyên tắc thay thế

Barbara Liskov đã đưa ra nguyên tắc Liskov Substitution Principle (LSP) này. Nguyên tắc này cho rằng: trong kế thừa, các class con, class kế thừa phải luôn có thể thay thế được class cha. Tức là, nếu class A kế thừa từ class B, thì mình luôn có thể sử dụng class A thay cho class B mà các chức năng không bị thay đổi.

image 23 - quochung.cyou PTIT

Lấy ví dụ về hình vuông và hình chữ nhật

image 28 - quochung.cyou PTIT
image 25 - quochung.cyou PTIT

Như trong toán học được dạy ở các cấp dưới, ta hay được nghe là “hình vuông cũng là hình chữ nhật”, Nhìn ví dụ trên ta thấy mọi tính toán đều rất hợp lý. Do hình vuông có 2 cạnh bằng nhau, mỗi khi set độ dài 1 cạnh thì ta set luôn độ dài của cạnh còn lại bằng cách viết đè phương thức set chiều cao và set chiều rộng.

Tuy nhiên, class HinhVuong sau khi kế thừa class HinhChuNhat đã làm thay đổi các đặc tính vốn có của HinhChuNhat, dẫn đến vi phạm LSP. Thử với một ví dụ như sau

image 26 - quochung.cyou PTIT

Rõ ràng, lúc này ta khai báo một object class HinhChuNhat theo HinhVuong, set chiều cao và chiều rộng, nhưng do ta đã ghi đè hàm set chiều cao chiều rộng nên chiều cao chiều rộng bị cập nhật thành 10, và tính diện tích ra là 10×10 = 100, rõ ràng không đúng vì hình chữ nhật đúng ra diện tích là 5×10 = 50, hay có thể nói là: Class HinhVuong không thể dùng thay thế cho class HinhChuNhat

Những vi phạm về nguyên lý LSP

  • Các lớp dẫn xuất có các phương thức ghi đè phương thức của lớp cha nhưng với chức năng hoàn toàn khác.
  • Các lớp dẫn xuất có phương thức ghi đè phương thức của lớp cha là một phương thức rỗng.
  • Các phương thức bắt buộc kế thừa từ lớp cha ở lớp dẫn xuất nhưng không được sử dụng.
  • Phát sinh ngoại lệ trong phương thức của lớp dẫn xuất.
image 27 - quochung.cyou PTIT

Đây là nguyên lý… dễ bị vi phạm nhất, nguyên nhân chủ yếu là do sự thiếu kinh nghiệm khi thiết kế class. Thuông thường, design các class dựa theo đời thật: hình vuông là hình chữ nhật, file nào cũng là file. Tuy nhiên, không thể bê nguyên văn mối quan hệ này vào code. Hãy nhớ 1 điều:

  • Trong thực tế, A là B (hình vuông là hình chữ nhật) không có nghĩa là class A nên kế thừa class B. Chỉ cho class A kế thừa class B khi class A thay thế được cho class B.
  • Nguyên lý này ẩn giấu trong hầu hết mọi đoạn code, giúp cho code linh hoạt và ổn định mà ta không hề hay biết. Ví dụ như trong Java, ta có thể chạy hàm foreach với List, ArrayList, LinkedList bởi vì chúng cùng kế thừa interface Iterable. Các class List, ArrayList, … đã được thiết kế đúng LSP, chúng có thể thay thế cho Iterable mà không làm hỏng tính đúng đắn của chương trình.

Theo đó, để sửa vấn đề hình vuông – hình chữ nhật trên, ta nên để chúng cùng kế thừa một class Shape như sau

image 29 - quochung.cyou PTIT

Lúc này việc set các chiều cao và chiều rộng thì chỉ class con mới có, và không vi phạm nguyên tắc LSP.

Việc thiết kế áp dụng theo nguyên tắc LSP giúp chúng ta giảm bớt quá lạm dụng việc kế thừa trong class. Ý nghĩa của các chức năng không được thay đổi để có thể sử dụng ở nhiều phạm vi khác nhau hơn.

Nguyên tắc 4: Interface segregation principle – Nguyên tắc chia nhỏ

Nguyên tắc này được phát biểu như sau:

image 31 - quochung.cyou PTIT

Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.

Many client-specific interfaces are better than one general-purpose interface.

Theo nguyên tắc ISP, một class con khi implement các interface thì không nên bị bắt buộc implement các phương thức mà mình không sử dụng bao giờ. Theo cách hiểu trên, nguyên tắc này sẽ ưu tiên việc chia nhỏ các interface ra thành các phương thức sử dụng cho các mục đích đặc thù hơn, tránh sử dụng cả một interface lớn.

Hay nói một cách khác, khi ta implement một interface trong 1 class, mà có 1 vài phương thức mình cứ phải kế thừa dù cũng không cần thiết không phải một cách hay. Ngắn gọn là: Không một client nào nên bị bắt buộc phải kế thừa một phương thức nào đó mà nó không dùng

Hãy tưởng tượng chúng ta có 1 interface lớn, khoảng 100 methods. Việc implements sẽ khá cực khổ, ngoài ra còn có thể dư thừa vì 1 class không cần dùng hết 100 method. Khi tách interface ra thành nhiều interface nhỏ, gồm các method liên quan tới nhau, việc implement và quản lý sẽ dễ hơn.

image 33 - quochung.cyou PTIT
image 30 - quochung.cyou PTIT

Như vậy, việc class Bike implement interface Vehicle khiến class Bike phải viết lại cả hàm openDoor() mở cửa, dù xe đạp thì không có cửa, và thường thì ta sẽ phải để nó rỗng. Như vậy khiến code bị dư thừa và nếu mở rộng ra thì các class con ngày càng bị phình to, khó bảo trì hơn

image 32 - quochung.cyou PTIT
  • Việc áp dụng nguyên tắc trên giúp code dễ đọc và dễ quản lí bảo trì hơn. Giảm bớt code dư thừa và chỉ phải viết các phương thức cần thiết.

Nguyên tắc 5: Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc

Lấy ví dụ:

image 35 - quochung.cyou PTIT

Như ta thấy, các thiết bị như tai nghe dùng cổng 3.5mm, điện thoại Android dùng dây sạc TypeC, điện thoại IOS dùng dây sạc Lightning, cổng cắm của Camera cũng khác, …

Vậy khi ta làm dây sạc, dây kết nối cho các máy này. Ta có chọn trước là nó sẽ dùng cho thiết bị nào không? Rõ ràng là không, các dây sạc này (các module cấp thấp) không quy định là nó dùng cho module cấp cao nào, mà chính các thiết bị như máy ảnh, điện thoại, tai nghe mới quy định nó dùng module cấp thấp nào.

Nguyên tắc này được phát biểu như sau:

  1. Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction.
  2. Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại. ( Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)
  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.
Ví dụ, ta đang thiết kế một hệ thống thanh toán bằng ngân hàng như sau:
image 39 - quochung.cyou PTIT

Ví dụ lúc này, ta cần thêm phương thức thanh toán bằng tiền mặt, cần có thêm một số tham số thì sao?
image 40 - quochung.cyou PTIT
image 41 - quochung.cyou PTIT

Ta thấy lúc này ta lại phải sửa đổi ở module cấp cao (highlevel module) là CuaHang, thêm if else để xử lí phương thức mới, mà sau này, nếu thêm các phương thức mới nữa, ta lại cứ phải thêm rất nhiều if else vào cái class lớn này.

Nó đã vi phạm 2 nguyên tắc là nguyên tắc (Nguyên tắc đảo ngược phụ thuộc) vì một class level cao hơn lại khai báo các chi tiết của class level bé hơn, hơn nữa lại còn sai về (Nguyên tắc một chức năng) vì đã khai báo nhiều chức năng hơn trong class lớn. Đúng ra class lớn chỉ nên thực hiện thanh toán thôi, còn cụ thể thanh toán thế nào thì phải là class khác.

Áp dụng nguyên tắc Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc
image 42 - quochung.cyou PTIT
image 43 - quochung.cyou PTIT

Lúc này các module cấp cao high-level là CuaHang đã có một interface ở giữa là PhuongThucThanhToan với các chức năng của các lớp cấp thấp (low-level module) như ThanhToanTienMat, ThanhToanNganHang. Ta đã đảo ngược sự phụ thuộc.

Như vậy, khi ta muốn thêm các lớp thanh toán khác như PayPal, Thẻ tín dụng, ví điện tử,…. Ta chỉ cần viết thêm các class con khác kế thừa từ interface PhuongThucThanhToan, chứ không cần động gì vào các code cũ này nữa.

image 44 - quochung.cyou PTIT
  • Code có thể tái sử dụng
  • Code dễ dàng quản lí hơn
  • Chia nhỏ các phần giúp việc test đơn giản hơn
  • Giảm bớt việc lỗi khi động vào các class cao hơn.

Tài liệu tham khảo:

  • https://gpcoder.com/4200-cac-nguyen-ly-thiet-ke-huong-doi-tuong/
  • https://www.interviewbit.com/blog/solid-principles-java/
  • https://stg-tud.github.io/sedc/Lecture/ws13-14/3.3-LSP.html#mode=document
  • https://www.codeproject.com/Articles/538536/A-curry-of-Dependency-Inversion-Principle-DIP-Inve

[Android] P-Connect Social

https://github.com/quochung-cyou/P-Connect

Author

  • quochungcyou

Description

  • P-Connect is an Android app written in Java, developed by quochungcyou, made during ProGApp competition in one month. It provides a comprehensive experience with its user-friendly UI/UX, smooth animations, and top performance. With fast loading times and an elegant design, P-Connect offers a plethora of features such as the latest headlines, chat functions, and even a small dating app. Additionally, the code is clean and organized with an MVC structure to ensure maximum efficiency.

Prerequisites

  • To run this software, you need an editor with java support, java – version 11 or higher,android-studio
  • Install java 11 using the commands: sudo apt update sudo apt install openjdk-11-jre-headless
  • Install android-studio sudo snap install android-studio --classic

Setup

  • Clone the project
  • Open in android-studio

Technologies Used

  • Java (version 11)
  • Android
  • Gradle (7.1)

Demo image

1 - quochung.cyou PTIT
2 - quochung.cyou PTIT
3 - quochung.cyou PTIT
4 - quochung.cyou PTIT
5 - quochung.cyou PTIT
6 - quochung.cyou PTIT
7 - quochung.cyou PTIT

License Information

  • This software is licensed under MIT License.
  • Read More on the license.
MIT License

Copyright (c) 2023 QuocHung

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Tìm hiểu về Java Swing

  1. Java Swing
  1. Định Nghĩa
  • Swing trong Java là một bộ công cụ Giao diện Người dùng Đồ họa (GUI) bao gồm các thành phần GUI. Swing cung cấp một bộ widget và gói phong phú để tạo ra các thành phần GUI tinh vi cho các ứng dụng Java. Swing là một phần của Java Foundation Classes (JFC), là một API để lập trình Java GUI cung cấp GUI.
  • Thư viện Java Swing được xây dựng dựa trên Bộ công cụ tiện ích con trừu tượng Java ( AWT ), một bộ công cụ GUI phụ thuộc vào nền tảng cũ hơn. Bạn có thể sử dụng các thành phần lập trình GUI đơn giản của Java như nút, hộp văn bản, v.v., từ thư viện và không phải tạo các thành phần từ đầu. Tuy nhiên, nó lại khác với AWT ở chỗ bộ công cụ này thuộc loại nền tảng độc lập, bao gồm các thành phần nhẹ và phức tạp hơn AWT.
  • Gói javax.swing cung cấp các lớp cho java swing API như JButton, JTextField, JTextArea, JRadioButton, JCheckbox, JMenu, JColorChooser, etc..
  1. Sử dụng
  • Java Swing được dùng để hỗ trợ tạo giao diện đồ hoạ người dùng (với Java).
  • Bộ công cụ này cung cấp các bộ điều khiển nâng cao như thanh trượt, colorpicker, Tree, TabbedPane và bảng điều khiển,..
  • Swing có những đặc điểm:
    • Độc lập với thiết bị.
    • Có thể tuỳ chỉnh, mở rộng.
    • Khá nhẹ.
    • Có thể cấu hình.

Các lớp và phân tầng Java Swing

EuTjh3a 8PoWdTXJVM4T2SEgYdJ1TqZVyiul2rMB 43nq 2PRiquBGzFlJd7sY yrMfU QK4oV37CjX16dP8xoG4Vu80hbre9sEWncuHmQU 7ji6LV ciPzBeM8PCz07txDiiaj6 D6 - quochung.cyou PTIT
  1. Các phương thức thường gặp ở lớp Component
Phương thứcMô tả
public void add(Component c)thêm một thành phần vào thành phần khác.
public void setSize(int width, int height)thiết lập kích thước của thành phần.
public void setLayout(LayoutManager m)thiết lập trình quản lý bố cục (layout) cho thành phần.
public void setVisible(boolean b)thiết lập khả năng hiển thị của thành phần. Nó theo mặc định là false (ẩn)
  1. Một số thành phần cơ bản
  1. JButton – Nút bấm
  • Nó được dùng để tạo ra một nút (button) có tên.
  • Việc sử dụng ActionListener sẽ dẫn đến một số hành động khi nút được nhấn.
  • Nó kế thừa lớp AbstractButton và độc lập với nền tảng.
I3sqx7ZizTt9ffnn0TVuacA2U1IMvRKhl ElXMr7fufQX6mu5fvImqgoTcK yGrHhvabR VBjDAL33B9qfE5r3ljqII676mn4RggTzYc0yRp - quochung.cyou PTIT
aKLIgPeLYlZzwA7WHFkLFOAO0uevX9mTYj237Ekjv18xPM QXIT0GBjt1FG9kBwQMQ ZO30H O2xehKKJvdX7rrNxuAxZV1ivVLkMU6uZ8o - quochung.cyou PTIT
  1. JTextField – Ô nhập
  • Nó kế thừa lớp JTextComponent và dùng để cho phép chỉnh sửa dòng đơn.
lQifr0kdtWBN8HY8S2cEUUWgKSOgr2 UyjfM6 rwmB6FThsklWGYS4xTBlmj kF9Vo3qXzl3 - quochung.cyou PTIT
UQScS xfvadwIljc0eawqsj3K5ME8upslkJRu7bXWWr6r5oWNSsh65XdgiRfulyu7jcIEWvNTrmLGzuHb1Q6V1qQf Q5fDshLF - quochung.cyou PTIT
  1. JScrollBar – Thanh cuộn
stM3 A6A04IhjAcpgFUJ37DPtzgFBRwHa gRpGtsKdVWt0b pHRF8ROR PdUsGUH ch0P2jyd91vR5eLBLgKEshklJG9dQge h3mgLGHIQBRn9wamYJF5cyI7mEimfqKjbTPiUdfEkNC - quochung.cyou PTIT
6zUcf5EZ0 kVtwUwUtHhMkTJRJMTDT8YQ7suA Ll6NDfHGOF0w4KcSjm4 - quochung.cyou PTIT
  1. JPanel – Thành phần chứa
  • Kế thừa lớp JComponent, cung cấp không gian cho một ứng dụng (có thể đính kèm bất kỳ thành phần nào khác).
QDeyI0HLhpiJQ0e87sttI34thu3F9DQH06 B2139HWnYjyGcMNx7XphboGbigmj8VAzkpRfOVsfV3QinM3oHmoBPlvPQzrayNw3AHzll6OnCFkHf67DaGl5uufA0 Q4IM8HWT0qlDZ5a - quochung.cyou PTIT
N b7R WACr JXwBGSqHd5KQOM8 s FCWysliktl1riOaEAiEBalFrlYYnkA7PEi r ZEL XL7M6 ff8f dNR6bLLDWu4PIVtVbmA8 TxLcRzFHUv K1iSfbmpw7PJFmhWwctCQuDQz5u - quochung.cyou PTIT
  • JMenu – Tạo menu
- quochung.cyou PTIT
Ac 89GgO1kGNni6rmV8Xw s7i2z5EPpIT34gEaELgDEnF0M5zolv7Xw 2hm9KWnS3q2 Ie1mm2Hm GNA3I4SSZGt61ikpCevEPLk8QRgO8VW At5DcRbL4wpoH1zpdYPkPRSvHorje - quochung.cyou PTIT
  • JList – Chứa danh sách
0pZpCe72qd7ylCDwqw5AW8OF37 p89nB0SoN74C9V6G6j02tsNvSJMqoKn4e6kaBApOU26u4 Zs1XpLoRpP I3v0yjUopIfBVCJRvdI4XwKN 0kdPsMB8qh7Wars3kWKy - quochung.cyou PTIT
BAnymKZ8nExl3pbZu5nN5V3odcCt frK0wqzoUd0 - quochung.cyou PTIT
  • JLabel – Vùng tiêu đề, chứa văn bản
- quochung.cyou PTIT
RA9XjSro1CdjqFw50fwWm3lDiS6BGp7e7UFOhF1kdhZRdTUFYbnZmpSC 4PnAjbKMr ki82BQzPkCoPQ1m M0QCuy96HEZbCpMZZZj72Au5x pqKwpaSEzJn6YYS35JLPgp8zON3HYlU - quochung.cyou PTIT
  • JComboBox – Menu lựa chọn
atRjeVfpNmYxmpwoEDcw4EJQP336OZtTeBWOqH8Mbf2Ic9IcR2IPC4wsd9H4AHEJgxsQjdDuKcCtqJ7wifjd7l7RxxZ2QVda1NE - quochung.cyou PTIT
kS lTUfDTWwP8WsDZtOoqdHzwv zrdLiu4SFBT TWoaIjCEoRrdXcegQbysqru1lAWNd7hnTxXYrAkPSmXMzCyXE5BDav XiJw1z3vVUGK4Lm zKSmd HHBGRmg5Cg0u 0AY GUqyeI3 - quochung.cyou PTIT

Tính đa hình – Polymorphism trong C++ OOP

Đa hình

C++ Polymorphism with Example

Tính đa hình (Polymorphism) là một trong bốn tính chất cơ bản của OOP. Trong lập trình, thuật ngữ này dùng để ám chỉ đến việc một biến, một hàm hoặc một phương thức có thể tồn tại ở nhiều dạng khác nhau. Đa hình là hiện tượng mà các đối tượng thuộc các class khác nhau có thể biểu diễn cùng một thông điệp theo các cách khác nhau.

Ví dụ:

Một người A có thể là một học sinh, đồng thời là thành viên câu lạc bộ, đồng thời là nhân viên bán thời gian quán café.

Một hành động “làm việc” nếu cho học sinh sẽ là học tập, cho kế toán sẽ là “tính toán”, …

Lập trình viên có thể tận dụng Tính đa hình trong OOP vào những trường hợp sau:

  • Khi các lớp con cần dùng phương thức của lớp cha để bổ sung cho một phương thức khác.
  • Khi một lớp cần có nhiều phương thức cùng tên nhưng khác tham số (parameter).

Tính đa hình trong OOP gồm có 2 loại: 

  • Runtime Polymorphism (Đa hình thời gian chạy) 
  • Compile Time Polymorphism (Đa hình thời gian biên dịch) 

Theo đó , tính đa hình cho phép một phương thức thực thi những hành vi khác nhau theo hai hướng: sử dụng phương thức ghi đè (method overriding) hoặc phương thức nạp chồng (method overloading).

  • Đa hình thời gian chạy là trường hợp một đối tượng bị ràng buộc với chức năng của chúng ngay tại thời gian chương trình đang chạy. Đa hình thời gian chạy sử dụng phương thức ghi đè.
  • Đa hình thời gian biên dịch là trường hợp một đối tượng bị ràng buộc với chức năng của chúng ngay tại thời gian chương trình đang biên dịch. Đa hình thời gian biên dịch sử dụng phương thức nạp chồng.

Phương thức ảo, từ khóa Virtual

Một số tài liệu có viết công dụng của Virtual Function như sau:

“Virtual Function là để khai báo một function ở class cha (base class) mà sau đó các class kế thừa (derived class) có thể override function đó”

Các hàm ảo sẽ cho phép chúng ta tạo một danh sách các con trỏ lớp cơ sở và các phương thức của bất kỳ lớp dẫn xuất nào mà không cần biết loại đối tượng của lớp dẫn xuất.

Khi khai báo hàm ảo với từ khóa virtual nghĩa là hàm này sẽ được gọi theo loại đối tượng được trỏ (hoặc tham chiếu), chứ không phải theo loại của con trỏ (hoặc tham chiếu). Và điều này dẫn đến kết quả khác nhau:

  • Nếu không khai báo hàm ảo virtual trình biên dịch sẽ gọi hàm tại lớp cở sở base (lớp cha)
  • Nếu dùng hàm ảo virtual trình biên dịch sẽ gọi hàm tại lớp dẫn xuất derived (lớp kế thừa)

Phương thức thuần ảo, lớp ảo

Hàm thuần ảo (pure virtual function) được sử dụng khi:

  • Không cần sử dụng hàm này trong lớp cơ sở, chỉ phục vụ cho lớp dẫn xuất
  • Lớp dẫn xuất bắt buộc phải định nghĩa lại hàm thuần ảo (Nạp chồng)

Ví dụ: Chúng ta có một lớp cơ sở là Shape. Các lớp dẫn xuất của lớp Shape là Triangle, Square và Circle. Chúng ta muốn tính diện tích của các hình này.

Chúng ta có thể tạo ra một hàm thuần ảo có tên là calculateArea() trong lớp Shape. Các lớp Triangle, Square và Circle phải định nghĩa lại hàm calculateArea() với công thức tính diện tích khác nhau cho mỗi hình.

5aqTqiCz9G4Xm6Uaz UnhFT3FFBkS90tg1hwvHqeYduCSDl5bOpxqiTLxwqxx4EbE2vveyk tfw2kZGOLfDwlJA8SUFw3a8rOZHJ6SbM7jUjoubXhah94az MJ4IizDzt4zAHqYB7VNstPKvRs 6pw - quochung.cyou PTIT

Lớp ảo

Có một vấn đề khi một lớp cơ sở được kế thừa bởi nhiều lớp dẫn xuất. Ví dụ, xét tình huống các lớp kế thừa theo sơ đồ như sau:

https://media.geeksforgeeks.org/wp-content/uploads/20190312133527/Need-of-Containership-in-C.jpg

Lớp B và Lớp C kế thừa từ lớp A. Chúng trở thành A (B) và A (C). Lúc này, nếu ta lại cho D kế thừa từ B và C, ta vô tình tạo ra tới 2 con đường kế thừa về 1 điểm đích.

  • D kế thừa thông qua B kế thừa A
  • D kế thừa thông qua C kế thừa A

Điều này sẽ khiến trình biên dịch mơ hồ và không biết nên đi theo con đường nào và tạo ra lỗi.

Thay vì kế thừa thông thường, ta sẽ định nghĩa B và C là các lớp ảo, việc sử dụng lớp ảo sẽ tiết kiệm không gian và tránh việc trùng lặp kế thừa.

  • Nếu kế thừa thông thường, khi tạo class D kế thừa đồng thời B và C, ta thực chất tạo ra 2 bản sao class A
  • Nếu cho class B và class C thành virtual, thì khi tạo class C chúng ta sẽ dùng chung 1 bản class A.
YilbwpP RhhZw3Zu6GAQyVLIUIhvGX 6KaIg13I2EQG8CpgaorXvQFB0eY GEOS7Bz W4hU1hwcttV1CufHhY8kzKzAMhEUsGUXD5YX8KoAYNEEcx1LaO - quochung.cyou PTIT

Diagram UML và C++ OOP

Diagram UML + Practicing

Hệ thống UML Class Diagram (Biểu đồ Class) là một cách dùng hình ảnh để khởi tạo và biểu diễn một hệ thống hướng đối tượng.

Tìm hiểu về Diagram UML: Access Modifier, Composition, Aggregation, Cardinality.

Access Modifier:

Trong C++ chúng ta có 3 nhãn phạm vi (access modifier) thì uml sẽ kí hiệu như sau

Dấu + : public

Dấu – : private

Dấu # : protected

Composition, Aggregation

Wevt9VIWixwXXqZChL595AM2tmBIItZfBe8pguSnvZep4R8d - quochung.cyou PTIT

Ví dụ:

  • Người đang tiếp xúc với pet, pet cũng tiếp xúc với người (Sự kết hợp – association)
  • Cái đuôi là một thành phần của con chó hoặc con mèo (aggregation composition – Thành phần)
  • Mèo là một kiểu con của pet (Kế thừa)

Aggregation là kiểu khi mà các lớp con được độc lập và không phụ thuộc vào lớp cha. Khi lớp cha bị xóa thì lớp con vẫn tồn tại.

Composition bị phụ thuộc vào lớp cha.

Ví dụ:

hkxJP7s9WMyUHH7feURQTXTOEhBlGB9rvilfx3i5 wM1V147HRCaA7KWv4L5k0TVCQ2u01P0ujuByHCj pWMoP L15HQiSNLrfshipIX2NvBU A74Hev1Tv 5cN8CaCJV3kpAUKIE7TjgHQG0Zdjqw - quochung.cyou PTIT

Class ô tô và Class bánh xe. Class bánh xe vẫn là một sự tồn tại “có ý nghĩa” kể cả không có Class  ô tô, class bánh xe vẫn có thể dùng cho class xe đạp, class xe máy, …

=> Aggregation là kiểu khi mà các lớp con được độc lập và không phụ thuộc vào lớp cha. Khi lớp cha bị xóa thì lớp con vẫn tồn tại.

OrRJ8oN57LWRsOqjwK6LO5K915Y6olyTiFXoOXz8XqHdxi L9m SoisWF0wcGm2vecjOcT9tEvBQIeRUvUbL6SERIdzO91Kf9A25Jmy5VIUUmNIa6b1qazQHTiYnSg7 6E EbgSIRUT2TNM3XYJTHA - quochung.cyou PTIT

Class người và class tay, chân, đầu. Nếu thiếu class người thì gần như những class còn lại đều tồn tại “không có ý nghĩa”, vì chúng không thể sử dụng vào đâu khác cả.

Cardinality

Sử dụng để thể hiện quan hệ về số lượng giữa các đối tượng được tạo từ các class trong class diagram

0…1: 0 hoặc 1

n : Bắt buộc có n

0…* : 0 hoặc nhiều

1…* : 1 hoặc nhiều

m…n: có tối thiểu là m và tối đa là n

B3hsekvGyamXUNgY 5v9biPnYAZKVqtKylsQQobRPQ7385gB SvuQp4u3qsdOppomEHptEBfQ2ykwt2Qfhxn0frNOsyQ U3rSS1WoEGKs8KX5yaGC9vsyCWjJ18srjYuF 11sWQ0qfB - quochung.cyou PTIT

-> VD ảnh: Công ty chỉ có một, còn nhân viên công ty thì là 1 hoặc * (nhiều – không giới hạn)

Tìm hiểu về lập trình hướng đối tượng (OOP) trong C++

  1. Khái niệm

Ở những chương trình code bình thường, code của chúng ta như một vector vậy, chúng chỉ là một dãy các dòng lệnh sẽ được chạy tuần tự từ đầu đến cuối. Ngoại lệ là các hàm và dữ liệu sẽ được tách khỏi vector đó và có thể gọi chúng giữa vector để tạo nên kết quả. 

Vậy OOP – Lập trình hướng đối tượng là gì? Nếu nhìn vào cuộc sống thực tế, tất cả thứ bạn thấy xung quanh đều là các đối tượng (Object) , object sẽ luôn có 2 thành phần chính

  • Các đặc tính của chúng. (VD: cân nặng, chiều cao, kích cỡ, hình dáng, màu sắc, …)
  • Các “hành động” chúng có thể bị tương tác hoặc có thể tương tác (Quyển sách bị “mở”, người “đi”, …) (Hàm)

Lập trình hướng đối tượng cho chúng ta khả năng tạo ra các đối tượng liên kết chặt chẽ với nhau và hoạt động như những khối hộp có thể sử dụng lại

VD: Bạn cần tạo một con game thế giới mở, người chơi có thể đi lại trong thế giới.

VD bạn cần tạo ra một ngôi nhà, ngôi nhà được cấu thành từ : bức tường, vật gia dụng, sơn, …. Bạn không thể mỗi nơi lại gọi hết các bức tường, vật gia dụng, sơn, … khác nhau ra, mà chúng ta sẽ có một Lớp – Ngôi nhà chứa tất cả đặc tính chúng ta cần như màu sắc, kích cỡ, và các thuộc tính này sẽ khác nhau mỗi nơi, để có thể sử dụng lại

  1. Class và Object
QnfLA2cHVptrlxRXBNmlIRvhDPfa9g3LJ7Icf52JGEbiWeNTssv5re3wrUvRYHv128YolvasaDupKZIpM2dawUJRL4BYZhjRDOPfsm8W6o K8mCChFTmvwVXFAkAW9kM3wbZ00WKSs44kmxx Z2MQA - quochung.cyou PTIT

Class là một lớp chứa các thuộc tính, chức năng. và Object – đối tượng là một vật thể được tạo ra từ một lớp Class đó.

struct NgayThang { int nam {}; int thang {}; int ngay {}; };

Đây là một lớp ngày tháng có 3 thuộc tính : ngày tháng năm, các lớp bây giờ sẽ là những thời gian khác nhau trong năm

NgayThang tet = 1/1/2022

NgayThang valentine = 4/2/2022

Con trỏ this?

YfOfxemnNCJM U7b Yge4QXCQUUUCabvTcImwuSqgTkCWfCyJLuGjJIhPkvAO48o9371OKjEEfuJ2Hrf 2EhREa5riQ8tLi1bYv5CM2pn2SKkMz8qlUiLHMbM6pKc7G8f3tP5rjSKqSElqP VnQmgw - quochung.cyou PTIT

This là một con trỏ cho phép chúng ta truy cập các thành phần, đặc tính của class

Ví dụ ta đang xây dựng một mạng xã hội với Class, và có một lớp NguoiDung

struct NguoiDung { int taikhoan {}; int matkhau {};};

Ta không thể cho phép người sử dụng khác in ra mật khẩu, tài khoản của một người dùng bất kì

cout << NguoiDung.matkhau;

=> Không cho phép truy cập các phần tử bí mật.

Nhưng ta vẫn cần cho phép người dùng có thể lấy ra mật khẩu khi đăng nhập, đăng xuất, quên mật khẩu, … nhưng mà lại không thể truy cập các phần tử bí mật.

NguoiDung endcontest;

cout << endcontest.matkhau; -> Không được

endcontest.dangnhap(); 

// nhưng 

void NguoiDung::dangnhap() const {

string matkhau;

cin >> matkhau;

if (matkhau == this.matkhau) cout << “dang nhap”;

  }

Ta đã ứng dụng con trỏ this để gọi ra mật khẩu, trong khi không thể truy cập trực tiếp.

  1. Các đặc tính của OOP
zFX7J35Q1qtmDc7ENbJQ sljjPTf OdxuVcBYz txJxSapajdZTKxd zjMqbk eSITIhTkXkPzTddedDEJYjwOqY3L7MzAC0EzYVR K pu5gs 6WB6kFp VY6 pUqJTN6e1SHgy9nSpfSlG B f 3w - quochung.cyou PTIT

Tính đóng gói (Encapsulation):

(Điều này giống ví dụ về this bên trên)

uDfzeGc8RtonzgEmfpW8wSiPti960aXK8MqMW20 JdUpkgY8bd6wKWH5pAZvstXzAO0MSw5eI88n0f4Tmp4I0oCcRL1LuVw1zcFkZRgp0erj9uCbactkaeHUdgT5F zugvRBjYkJomiB9iVW8O0CFw - quochung.cyou PTIT

Là cách để che dấu những tính chất xử lý bên trong của đối tượng, những đối tượng khác không thể tác động trực tiếp làm thay đổi trạng thái  chỉ có thể tác động thông qua các method public của đối tượng đó. 

xem cách thể hiện bằng code dưới đây : class hinhchunhat

WDgadreoPMjdoiA fJXqTXumUn n4THY3sxy9bO9OLDk02OUQeVzS R3F5ok9fgfIzO91h13jtpKWutcI Twqy o0oi4q tTjHlTOGK8ZZ4vzPr8FTthuHkPOi1Z3blw2lOzd iEyIqt - quochung.cyou PTIT
  • height và width ở đây chính là các tính chất (properties) của đối  tượng class hinhchunhat
  • tinhdientich() là method được public nhằm mục đích tương tác với các đối tượng khác. Tạo một class Program với method static để run, xem cách tương tác và thay đổi tính chất  của đối tượng thông qua các method public như nào:

Ta thấy ta sẽ không thể truy cập các thuộc tính height, width. Nhưng vẫn có thể sử dụng các hàm/method để truy cập chúng. 

Tính kế thừa (Inheritance):

Là kỹ thuật cho phép kế thừa lại những tính năng mà một đối tượng khác đã có, giúp tránh việc code lặp dư thừa mà chỉ xử lý công việc tương tự.

  • Kế thừa một cấp (Single level Inheritance): Với một class cha và một class con
Fk5F2xvm2TUt9lsJVVZawNnHWfP1nBPCS2XP5df9VDBlNUJ5VPt8N9z0CABzluYOHGCjczlJyq0KDQ7CE89dqLMZZfFdtrPHYYV2W7 55zGaF38cfyDwX2o72xjXZbxYVk2U - quochung.cyou PTIT

Ví dụ ta có một class đồ điện tử, chúng có các thuộc tính như: số vi mạch, lượng điện sử dụng, hiệu điện thế, cường độ dòng điện ,…. Các thiết bị như điện thoại, tai nghe, … vẫn dùng chúng, nhưng lại có thêm một số thuộc tính riêng khác, nên thay vì phải tạo lại tất cả thuộc tính đó cho từng class điện thoại, tai nghe mới. Ta sẽ gọi Điện thoạilớp kế thừa của đồ điện tử, và thừa hưởng mọi thuộc tính, chức năng của đồ điện tử.

Tính đa hình (Polymorphism):

Là một đối tượng thuộc các lớp khác nhau có thể hiểu cùng một thông điệp theo cách khác nhau.

Ví dụ đa hình trong thực tế: Mình có 2 con vật: chó, mèo hai con vật này khi nhận được mệnh lệnh  là “hãy kêu” thì chó kêu “gâu gâu”,  mèo kêu “meo meo”.

Ví dụ trên cả 2 con vật đều hiểu chung một thông điệp “hãy kêu” và thực hiện theo cách riêng của chúng.

Tính trừu tượng (Abstraction):

Abstraction (Tính trừu tượng) là một khả năng mà chương trình có thể bỏ qua sự phức tạp bằng cách tập trung vào cốt lõi của thông tin cần xử lý.

Điều đó có nghĩa, bạn có thể xử lý một đối tượng bằng cách gọi tên một phương thức và thu về kết quả xử lý, mà không cần biết làm cách nào đối tượng đó được các thao tác trong class.

Ví dụ: bạn có thể nấu cơm bằng nồi cơm điện bằng cách rất đơn giản là ấn công tắc nấu, mà không cần biết là bên trong cái nồi cơm điện đó đã làm thế nào mà gạo có thể nấu thành cơm.

  1.  Nhãn phạm vi

Nhãn public

  • Từ khóa public được dùng để tạo các thành phần “public – công khai” (Cho dữ liệu hoặc hàm) 
  • Các thành phần được tạo public có thể truy cập từ bất cứ chỗ nào trong phần mềm

Nhãn private

  • Từ khóa public được dùng để tạo các thành phần “private – bí mật” (Cho dữ liệu hoặc hàm) 
  • Các phần tử private chỉ có thể truy cập trong class hoặc class friend

Nhãn protected

  • Từ khóa public được dùng để tạo các thành phần “protected – bảo mật” (Cho dữ liệu hoặc hàm) 
  • Các thành phần protected chỉ có thể truy cập trong class và các class kế thừa của nó.

Tổng quát

KiểuTrong chính classClass kế thừaBên ngoài
publicYesYesYes
privateYesNoNo
protectedYesYesNo
  1. Hàm khởi tạo và hàm hủy

Hàm khởi tạo là một hàm đặc biệt trong lớp. Hàm này được gọi tự động khi một đối tượng được tạo ra. Hàm khởi tạo sẽ khởi tạo giá trị cho các thuộc tính của đối tượng. Trong C++, một hàm khởi tạo có đặc điểm sau:

− Tên hàm khởi tạo trùng với tên của lớp.

− Hàm khởi tạo không có kiểu dữ liệu trả về (kể cả void).

− Hàm khởi tạo có thể là public, private hoặc protected. Hàm private có thể truy cập từ lớp friend, protected có thể gọi từ lớp kế thừa.

− Hàm khởi tạo có thể có đối số hoặc không có đối số.

− Trong một lớp có thể có nhiều hàm khởi tạo (cùng tên nhưng khác đối số).

XQUeJ5tuUCmdfPiZuaa7l4 kfr99 uKNPT fcYAS1yyZGs1gHLFohu7YaTZ22NRxhP 3HEHMdZ7yetmE 5LLmmjoAcKb aLhgyBZiijchDiwwwpzziaN fz8j RbMV2t 94 Jw32 52ouEgRmLYGQw - quochung.cyou PTIT

 Hàm khởi tạo mặc định (default constructor)

Hàm khởi tạo mà không có đối số nào thì được gọi là hàm khởi tạo mặc định. Hàm này thường dùng gán giá trị mặc định cho các thuộc tính trong lớp.

Trong ví dụ bên dưới, hàm Circle() là một hàm khởi tạo mặc định và tự đặt r = 1 nếu chưa cài r.

- quochung.cyou PTIT

Lưu ý: Nếu lớp không có hàm khởi tạo nào, trình biên dịch sẽ cung cấp một hàm khởi tạo mặc định không đối số và không code trong nó.

Nếu trong lớp đã có ít nhất một hàm khởi tạo thì hàm khởi tạo mặc định do trình biên dịch cung cấp sẽ không còn nữa. Khi đó, bạn phải sử dụng các hàm khởi tạo được định nghĩa trong lớp nếu không sẽ gây ra lỗi.

Vnaot vBBOcs CkvdX 6a17fiR4RmGEtkjP vqjE8Fzan7uMRcFliLBLZTgZhutiDq5VfvdLoNHP88 rLNpZVcoAaqCp XeDTyGozJle4RsNoQ6NWDIDAIitR44mnftAo1H8HCYqstLm2i TF54qIw - quochung.cyou PTIT

Hàm huỷ sẽ tự động được gọi trước khi giải phóng một đối tượng để giải phóng vùng nhớ trước khi đối tượng được hủy bỏ. Hàm hủy có các đặc điểm sau:

− Mỗi lớp chỉ có một hàm hủy.

− Hàm hủy không có kiểu, không có giá trị trả về và không có đối số.

− Tên hàm huỷ cùng tên với tên lớp và có một dấu ngã (~) ngay trước tên.

w29IEuvUbhgSWYiwv0XNRZsUs5pnhlO8U7a9Jm2SbNGotBSRFJxcd1Pc9FBdNfLLNsLUR3Enp6l1ZT4n1jO ho AnqGW5wklXX6SsGS6WfU6uZabfCYByMwO4dUffsvujTYosAV22R97ZuAV35y8ew - quochung.cyou PTIT
  1. Getter và SetterbZtWeYVc93RF9jxumWG2W UscRZzz x90pjaS88rewCNSpIz6Xh7JDliDnvisncvatxhKOzUEt5yGQ - quochung.cyou PTIT

Là các hàm dùng để set (đặt) hoặc get (lấy) dữ liệu

  1. Static

Static trong C++ là một toán tử có tác dụng chỉ định một biến hoặc hàm thành viên trong class tồn tại ở dạng tĩnh.

Dạng tĩnh ở đây có ý nghĩa, vùng bộ nhớ dùng để lưu trữ các dữ liệu này sẽ là bất biến, không thay đổi.

Bằng việc sử dụng static trong class C++, chúng ta sẽ có được biến static và hàm static, là các thành viên được lưu tại các vùng bộ nhớ bất biến. Vì ở dạng tĩnh nên chúng sẽ tồn tại duy nhất trong class. Chúng có thể sử dụng kể cả không có đối tượng trong class

O9FVSTRGZXdoSY11q IDpeTUYejM2BoQsIYHvQflsDxzyt0W8flmXIe7VzRm4OHcMGWrCA9kp4uN3p7PJ 1vGRDONJ068lqnrrALyw AeitPeMb0mXDKa q Y cXAFN6xE 6eCAQggCQ - quochung.cyou PTIT

6.1. Biến static

Để tạo biến static trong class C++, chúng ta thêm toán tử static vào đằng trước tên biến khi khai báo biến trong class như sau. Lưu ý là access modifier của biến static phải ở dạng public để cho phép truy cập nó từ ngoài phạm vi class.

Ưu điểm lớn nhất của biến static đó chính là chúng ta có thể sử dụng trực tiếp nó mà không cần phải tạo ra đối tượng (instance) từ class. Tuy nhiên do không tạo ra instance dẫn đến việc giá trị ban đầu của biến static cũng không được khởi tạo, nên khi dùng biến static mà không tạo ra đối tượng, chúng ta cần phải khởi tạo giá trị cho nó ở bên ngoài phạm vi class.

Ví dụ:

VjdwUs zBdsnMUzInd5E ohUzrsk5z3qzjrCxDuK3iw2rQHX 1llE1pjPiSvIr8BWe9O4gBlSIeu58kIvJdK9CXQRTDG yWlsQSm6W9J866Z 0ndY5chkjXRo7OeBpgcyEGQ CFHQJ13bbYsHwz0Q - quochung.cyou PTIT

Giống như ví dụ trên, do sNum là biến static nên chúng ta có thể trực tiếp sử dụng giá trị của nó mà không cần phải tạo ra đối tượng từ class. Tuy nhiên biến num thì chỉ là biến thành viên bình thường nên chúng ta không thể sử dụng trực tiếp nó vì lỗi sẽ xảy ra.

6.2. Hàm static

Để tạo hàm static trong class C++, chúng ta thêm toán tử static vào đằng trước tên hàm khi khai báo hàm trong class như sau. Lưu ý là access modifier của hàm static phải ở dạng public để cho phép truy cập nó từ ngoài phạm vi class.

Lưu ý: đối với các hàm static thì các biến sử dụng bên trong phải là biến static

Ví dụ:

9LO5wMGm97Qb291ve6J AvXkWn8VTs l yllM5W - quochung.cyou PTIT
  1. Kỹ thuật chia tách file trong C++

Headers file

Khi các chương trình phát triển ngày càng lớn (và sử dụng nhiều file hơn), Việc khai báo các hàm bạn muốn sử dụng được định nghĩa trong cùng một file sẽ trở nên ngày càng tẻ nhạt và làm cho file đó trở lên nhiều dòng code, khó kiểm soát. Sẽ tốt hơn nếu bạn có thể đặt tất cả các khai báo của mình ở một nơi và sau đó định nghĩa chúng khi bạn cần nó.

Viết một file header

Ví dụ chúng ta có hai tệp, add.cpp và main.cpp, trông giống như thế này:

LNlR7gRsreZomKD6JOtH8Unnc9isFedY8yTE0S3Bu6HwYVX6x7N2E r2uZ6e3FBIzDwctFkyk4VGkdEeFH1Nwd hGD7Jq7yvAF6tbrvNhAYX1 PP GOTc0sGhuiQllSrpL7CIb4W8tvwjvd4wb64yw - quochung.cyou PTIT

Trong ví dụ này, chúng ta đã sử dụng một khai báo để trình biên dịch sẽ biết định danh add là gì khi biên dịch main.cpp. Chúng ta có thể thấy việc thêm thủ công cho mọi hàm bạn muốn sử dụng trong một file khác khá nhiều và phức tạp.

Hãy viết một file header để giảm bớt gánh nặng này. Viết một file header rất dễ dàng, vì các file header chỉ bao gồm hai phần:

Các tiền chỉ thị trong file header.

Nội dung thực tế của file header, phải là khai báo cho tất cả các định danh mà chúng ta muốn các file khác có thể nhìn thấy.

Sử dụng hậu tố .h khi đặt tên file header

Các file header thường được ghép nối với các file code, với file header cung cấp các khai báo cho file code tương ứng. Vì file header của chúng ta sẽ chứa một khai báo cho các hàm được định nghĩa trong add.cpp, nên chúng ta sẽ gọi file header mới là add.h.

Nếu một file header được ghép nối với một file code (ví dụ: add.h với add.cpp), cả hai sẽ có cùng tên (add).

File add.h

doyig TXI0hIRsuVPmObj7bZanklmCVvX2JlvwD - quochung.cyou PTIT

Để sử dụng file header này trong main.cpp, chúng ta phải #include nó (sử dụng dấu ngoặc kép, không phải dấu ngoặc nhọn).

main.cpp

ArtV1BocMhzCaB KwzB2ZjkXZIcCRAWFwK9WMiHbJ3dLd CDJTnN5nXTlctRy3Ny8yyh3oNFcqVOlsx2F5cDQhnZnh64Cx 4NSHdjEFTzVLZEjmiITMWm4m8WA1hTbd2FffRTS1PsxSTxRlVu O5CQ - quochung.cyou PTIT

Quy tắc đặt tên biến trong OOP C++

Luôn sử dụng tiếng Anh để đặt tên hàm và biến.

Ví dụ:

/* Bad */

const hoTen = “Trạng Tí”

const banBe = [“Sửu Ẹo”, “Dần Béo”, “Cả Mẹo”]

/* Good */

const fullName = “Trạng Tí”

const friends = [“Sửu Ẹo”, “Dần Béo”, “Cả Mẹo”]

Quy ước đặt tên

Nếu team của bạn chọn quy ước đặt tên là camelCase, hãy sử dụng camelCase cho toàn bộ dự án, nếu bạn qua một team khác chuộng snake_case hơn, hãy tuân thủ nghiêm ngặt. Cho dù là quy ước nào thì điều quan trọng nhất chính là tính nhất quán.

/* Bad */

const page_count = 5

const isUser = true

/* Good */

const pageCount = 5

const isUser = true

Nguyên tắc S-I-D

Short (ngắn gọn): tên không được dài, không phải mất thời gian để gõ và nhớ.

Intuitive (tự nhiên): tên khi đọc lên phải cho cảm giác xuôi tai, gần gũi với văn nói.

Descriptive (súc tích): tên phải mô tả được ý nghĩa, tác dụng của nó, bằng cách hiệu quả nhất.

/* Bad */

const totalNumberOfPublishedArticles = 10 // tên quá dài

const a = 5 // “a” không mô tả được số 5 để làm gì

const isDisplayable = a > 5 // “isDisplayable” nghe không tự nhiên lắm

/* Good */

const totalArticles = 10

const postCount = 5

const shouldDisplay = postCount > 5

Tính kế thừa – Inheritance [C++ OOP]

Kế thừa là gì?

  • Tính kế thừa (Inheritance) là một trong bốn tính chất cơ bản của Lập trình hướng đối tượng (OOP).
  • Kế thừa có thể được định nghĩa là quá trình mà một lớp (class) có được các thuộc tính của một lớp khác. Các thuộc tính đó có thể là một phương thức (method) hoặc một trường (field) nào đó. Có nghĩa là nếu lớp A kế thừa lớp B thì lớp A sẽ có những thuộc tính và phương thức của lớp B.
  • Lớp được thừa hưởng những thuộc tính và phương thức từ lớp khác được gọi là dẫn xuất (Derived Class) hay lớp Con (Subclass) và các lớp bị lớp khác kế thừa được gọi là lớp cơ sở (Base Class) hoặc lớp cha (Parent Class).

Cú pháp kế thừa

6Em9crRKpncLAstWY2Z2bQZfwt ERW0Sck jghXSFbpcKJxvyDbN94we3Yf8c0kLHreuGstaVmMTX3cptUJnE1vGYRS3wA - quochung.cyou PTIT

Đơn kế thừa, đa kế thừa

Đơn kế thừa

  • Đơn kế thừa (single inheritance) là một class con kế thừa duy nhất từ một class cha. Ví dụ như class sinh viên chỉ kế thừa duy nhất từ class con người.

Khai báo:

w8VoTdRWgs3vjKMRb973p jC ZkXSiEhdOBSacDB8MS9VCMKTRoQgb 3w4ggLcaWfAiUFewo8I5kMtt3kAA DoUTHSqXjtDpWRgWTIBhLLew bVryXWGIh8iEhsiF0IDApB U WLVE - quochung.cyou PTIT

Tính chất dẫn xuất trong đơn kế thừa

Lớp dẫn xuấtLớp dẫn xuấtLớp dẫn xuất
Lớp cơ sởPublicPrivateProtected
PrivateKhông kế thừaKhông kế thừaKhông kế thừa
ProtectedProtectedPrivateProtected
PublicPublicPrivateProtected
  • public: Nếu kế thừa ở dạng này, sau khi kế thừa, tất cả các thành viên dạng public lớp cha sẽ public ở lớp con, dạng protected ở lớp cha vẫn sẽ là protected ở lớp con.
  • protected: Nếu dùng protected thì sau khi kế thừa, tất cả các thành viên dạng public lớp cha sẽ trở thành protected tại lớp con.
  • private: Trường hợp ta sử dụng private, thì sau khi kế thừa, tất cả các thành viên dạng public và protected ở lớp cha sẽ thành private tại lớp con.

Đa kế thừa

  • Đa kế thừa (multiple inheritance) nghĩa là thay vì một class dẫn xuất kế thừa từ một class cơ sở, class dẫn xuất này có thể có kế thừa từ nhiều class cơ sở khác nhau. Ví dụ như con cái thì sẽ kế thừa đặc điểm từ cả bố và mẹ của chúng.

Khai báo:

p7jnwzKx8OTXleC1sNzeAfsA41V19Q4Lo GOHqrzDGN9kbth2XZyeMQDyhWrSkaqO zTLzh0Wc NFEcKg HWhMqr9jsI r9geXyEOjP0FnYhHZ MDYPXaXGsST7UytG7oU1b8W5yNlXV - quochung.cyou PTIT
  • Khi thừa kế từ nhiều class cơ sở, bạn thấy sẽ có một vấn đề phát sinh chính là khi lớp cơ sở có phương thức trùng tên, khi gọi phương thức sẽ xảy ra sự mơ hồ. Sự mơ hồ nghĩa là trình biên dịch không biết bạn đang gọi phương thức từ class dẫn xuất nào. Để giải quyết vấn đề này, ta có thể sử dụng toán tử phạm vi :: để chỉ rõ phương thức ở class nào. Ví dụ:
6LY VpYtPOiVZa5X62k5a21IgZF0z RbLRqRYW nD4H7nHvSPDvVieoemMZtnmVkKhKqv7dNyMf5AZOXhysn - quochung.cyou PTIT

Function Overloading & Operator Overloading

  • Ngôn ngữ C++ cho phép xác định nhiều hơn một định nghĩa cho một tên hàm hoặc một toán tử trong cùng phạm vi, được gọi tương ứng là Nạp chồng hàm và Nạp chồng toán tử.
  • Một khai báo nạp chồng là một khai báo mà đã được khai báo cùng tên như một khai báo được khai báo trước đó trong cùng phạm vi, ngoại trừ rằng: cả hai khai báo có các tham số khác nhau và định nghĩa khác nhau

Nạp chồng hàm

  • Kỹ thuật này cho phép sử dụng cùng một tên gọi cho các hàm “giống nhau” (có cùng mục đích). Nhưng khác nhau về kiểu dữ liệu tham số hoặc số lượng tham số.
  • Ví dụ: Nạp chồng hàm cho phép ta khai báo và định nghĩa các hàm sau với cùng một tên gọi.
tYUerjOrMqnd4LwQawL7weM2TMV1 efVoirK6BCawbTGbNbrDFFX7u0WcxYT NcQCnn0G1VHHXn7CuZU4P8ykm3WL TBGd ESMMmlmN61dEKqbpE6GbmU2VNrl4ZJ kM7o3IlRn ToZ2 - quochung.cyou PTIT

Nạp chồng toán tử

  • .Nạp chồng toán tử (overload operator) là bạn định nghĩa lại toán tử đã có trên kiểu dữ liệu người dùng tự định nghĩa để dể dàng thể hiện các câu lệnh trong chương trình.
  • Ví dụ như bạn định nghĩa phép cộng cho kiểu dữ liệu phân số thì sẽ thực hiện cộng hai phân số rồi trả về một phân số mới. So với việc thực hiện gọi hàm, việc overload toán tử sẽ làm cho câu lệnh ngắn gọn, dễ hiểu hơn.
XjTsX IbJE9IzgP5NGytA763YIqjQffZljq53jqA znKHiJloMouE1UsO4nfhSHOnWc3q2MIyzYoWHBVkgZCavfVoSp Aofx6bbHdlzfSTA1VGuM0M8V2ig6X89ArKN zBd5My8BmKpw - quochung.cyou PTIT

Cú pháp:

=> Như đã giới thiệu, bản chất việc dùng toán tử là lời gọi hàm, do đó chúng ta overload toán tử cũng giống overload hàm, vậy chúng ta sẽ overload hàm nào? Chúng ta sẽ overload hàm có tên là operator @, với @ là toán tử cần overload (+, -, *, /…), kiểu trả về của hàm chính là lớp đó.

Nạp chồng toán tử 2 ngôi:

BQ1NHTILv 0aXq0Ha9tsVX88OBxh70XHLdYkO3sBFS8w7FRJ 0oD4jFagxDUI1336PbZF88rPajtgOGMYanSItSKRFtOz1ghoLz9mtCaB2YzwO1GROk7qh4D8 Nbj2SunNMe oQZDIDD - quochung.cyou PTIT

Function Overriding

  • Trong kế thừa, khi lớp con khai báo phương thức có tên trùng với phương thức ở lớp cha thì phương thức của lớp cha sẽ bị thay thế bởi phương thức ở lớp con.
- quochung.cyou PTIT

Hàm Friend

  • Hàm bạn của một lớp không phải là hàm thành viên nên nó không phụ thuộc vào lớp và có thể định nghĩa ở trong hoặc ngoài lớp. 
  • Hàm bạn có thể truy cập trực tiếp các thành viên private và một lớp có thể có nhiều hàm bạn.
  • Cú pháp khai báo hàm bạn đơn giản là thêm từ khóa friend trước hàm bình thường ta vẫn sử dụng.

Collection và Collections trong Java

COLLECTION & COLLECTIONS

Collection

Collection trong java là một root interface trong hệ thống cấp bậc Collection. Java Collection cung cấp nhiều interface (Set, List, Queue, Deque,…) và các lớp (ArrayList, Vector, LinkedList, PriorityQueue, HashSet, LinkedHashSet, TreeSet,…)

- quochung.cyou PTIT

Collections

Collections trong java là một khuôn khổ cung cấp một kiến trúc để lưu trữ và thao tác tới nhóm các đối tượng. Tất cả các hoạt động mà bạn thực hiện trên một dữ liệu như tìm kiếm, phân loại, chèn, xóa,… có thể được thực hiện bởi Java Collections.

Hệ thống cấp bậc Collection

Gói java.util chứa tất cả các lớp và interface của Collection

NxEiS NhpDt2aCqAp qDQG0p29nHkoqtQieaej5V4WwooYzSmeDjJzkgHwTXDzYqt52o6bvg3rZo0KL4pUT9Dt4kULTdN4X - quochung.cyou PTIT

Các phương thức

Iterable interface chứa dữ liệu thành viên Iterator interface.

Giao tiếp Iterator cung cấp phương tiện để lặp đi lặp lại các thành phần từ đầu đến cuối của một collection.

  • Ngoài ra, ta còn có thể làm việc với Collection thông qua Iterator Interface.
Phương thứcMô tả
public boolean hasNext()Nó trả về true nếu iterator còn phần tử kế tiếp phần tử đang duyệt.
public object next()Nó trả về phần tử hiện tại và di chuyển con trỏ trỏ tới phần tử tiếp theo.
public void remove()Nó loại bỏ phần tử cuối được trả về bởi Iterator. Nó hiếm khi được sử dụng.
  • Một số method của Interface Collection. Các method này sẽ được các Interface con kế thừa lại:
Phương thứcMô tả
boolean add(Object element)Được sử dụng để chèn một phần tử vào collection.
boolean addAll(Collection c)Được sử dụng để chèn các phần tử collection được chỉ định vào collection gọi phương thức này.
boolean remove(Object element)Được sử dụng để xóa phần tử từ collection.
boolean removeAll(Collection c)Được sử dụng để xóa tất cả các phần tử của collection được chỉ định từ collection gọi phương thức này.
boolean retainAll(Collection c)Được sử dụng để xóa tất cả các thành phần từ collection gọi phương thức này ngoại trừ collection được chỉ định.
int size()Trả lại tổng số các phần tử trong collection.
void clear()Loại bỏ tổng số của phần tử khỏi collection.
boolean contains(Object element)Được sử dụng để tìm kiếm phần tử.
boolean containsAll(Collection c)Được sử dụng để tìm kiếm collection được chỉ định trong collection.
Iterator iterator()Trả về một iterator.
Object[] toArray()Chuyển đổi collection thành mảng (array).
boolean isEmpty()Kiểm tra nếu collection trống.
boolean equals(Object element)So sanh 2 collection.
int hashCode()Trả về số hashcode của collection.
  • Collections chỉ chứa static method. Một số method của collections:
Phương thứcMô tả
void sort()sắp xếp collections
min(), max()trả về phần tử nhỏ nhất /lớn nhất trong Collection
int binarySearch()tìm kiếm nhị phân một giá trị
void reverse()đảo ngược collections
void copy()Sao chép tất cả các phần tử từ một danh sách này sang một danh sách khác.
int frequency()Trả về số lượng các phần tử có trong bộ sưu tập được chỉ định bằng đối tượng được chỉ định.
int indexOfSubList()Trả về vị trí bắt đầu của lần xuất hiện đầu tiên của danh sách mục tiêu được chỉ định trong danh sách nguồn quy định, hoặc -1 nếu không tìm thấy đối tượng chỉ định.

CÁC COLLECTION THƯỜNG GẶP

List

List là một interface trong java. Nó chứa các phương thức để chèn và xóa các phần tử dựa trên chỉ số index.

Khai báo của interface List trong Java:

5neMDTW8gs2P6eXH6p091esF7GsoSWfQ2nGklyLNTQa L3zMRzi57gksf3ZP4ZVupWuENZzXYPr8iefiiKvQMO0rtd4fm JwWvNuuIhL4ejupOD20JRZFnWcuP7nfCSwRQO DLMBj7p5 - quochung.cyou PTIT
  • Các method của List thường dùng:
Phương thứcMô tả
void add(int index, Object element)Nó được sử dụng để chèn các phần tử vào list tại chỉ số index.
boolean addAll(int index, Collection c)Nó được sử dụng để chèn tất cả các yếu tố của c vào danh sách tại chỉ số index.
object get(int index)Nó được sử dụng để trả về đối tượng được lưu trữ tại chỉ số index trong list.
object set(int index, Object element)Nó được sử dụng để gán phần tử cho vị trí được chỉ định index trong list.
object remove(int index)Nó được sử dụng để xóa các phần tử tại vị trí có chỉ số index và trả về phần tử đã xóa.
ListIterator listIterator()Nó được sử dụng để trả về một Iterator mà bắt đầu từ phần tử đầu tiên của list.
ListIterator listIterator(int index)Nó được sử dụng để trả về một Iterator mà phần tử bắt đầu từ chỉ số index chỉ định.

ArrayList

Lớp ArrayList trong java là một lớp kế thừa lớp AbstractList và triển khai của List Interface nên nó sẽ có một vài đặc điểm và phương thức tương đồng với List.

ArrayList được sử dụng như một mảng động để lưu trữ các phần tử.

Những đặc điểm của ArrayList:

  • Lớp ArrayList trong java có thể chứa các phần tử trùng lặp.
  • Lớp ArrayList duy trì thứ tự của phần tử được thêm vào.
  • Lớp ArrayList là không đồng bộ (non-synchronized).
  • Lớp ArrayList cho phép truy cập ngẫu nhiên vì nó lưu dữ liệu theo chỉ mục.
  • Lớp ArrayList trong java, thao tác chậm vì cần nhiều sự dịch chuyển nếu bất kỳ phần tử nào bị xoá khỏi danh sách.

Khởi tạo ArrayList:

- quochung.cyou PTIT
  • Các method của ArrayList thường dùng:
Phương thứcMô tả
boolean add(Object o)Nó được sử dụng để nối thêm phần tử được chỉ định vào cuối một danh sách.
void add(int index, Object element)Nó được sử dụng để chèn phần tử element tại vị trí index vào danh sách.
boolean addAll(Collection c)Nó được sử dụng để nối tất cả các phần tử trong collection c vào cuối của danh sách, theo thứ tự chúng được trả về bởi bộ lặp iterator.
boolean addAll(int index, Collection c)Nó được sử dụng để chèn tất cả các phần tử trong collection c vào danh sách, bắt đầu từ vị trí index.
void retainAll(Collection c)Nó được sử dụng để xóa những phần tử không thuộc collection c ra khỏi danh sách.
void removeAll(Collection c)Nó được sử dụng để xóa những phần tử thuộc collection c ra khỏi danh sách.
int indexOf(Object o)Nó được sử dụng để trả về chỉ mục trong danh sách với sự xuất hiện đầu tiên của phần tử được chỉ định, hoặc -1 nếu danh sách không chứa phần tử này.
int lastIndexOf(Object o)Nó được sử dụng để trả về chỉ mục trong danh sách với sự xuất hiện cuối cùng của phần tử được chỉ định, hoặc -1 nếu danh sách không chứa phần tử này.
Object[] toArray()Nó được sử dụng để trả về một mảng chứa tất cả các phần tử trong danh sách này theo đúng thứ tự.
Object[] toArray(Object[] a)Nó được sử dụng để trả về một mảng chứa tất cả các phần tử trong danh sách này theo đúng thứ tự.
Object clone()Nó được sử dụng để trả về một bản sao của ArrayList.
void clear()Nó được sử dụng để xóa tất cả các phần tử từ danh sách này.
void trimToSize()Nó được sử dụng để cắt dung lượng của thể hiện ArrayList này là kích thước danh sách hiện tại.
boolean contains(element)Kết quả trả về là true nếu tìm thấy element trong danh sách, ngược lại trả về false.
  • Như tìm hiểu bên trên, ta thấy ArrayList và Vector gần giống nhau. Tuy nhiên, ta có thể so sánh một vài sự khác biệt như sau:
ArrayListVector
ArrayList là non-synchronized.Vector là synchronized.
ArrayList là nhanh hơn vì nó là non-synchronized.Vector là chậm hơn vì nó là synchronized. Tức là, trong môi trường đa luồng, các thread giữ nó ở trong trạng thái runnable hoặc non-runnable cho đến khi thread hiện tại giải phóng đối tượng đó.
ArrayList được duyệt bởi Iterator.Vector được duyệt bởi Enumeration và Iterator.

Map

Trong java, map được sử dụng để lưu trữ và truy xuất dữ liệu theo cặp key và value. Mỗi cặp key và value được gọi là mục nhập (entry). Map trong java chỉ chứa các giá trị key duy nhất. Map rất hữu ích nếu bạn phải tìm kiếm, cập nhật hoặc xóa các phần tử trên dựa vào các key.

Các phương thức hữu ích của Map interface

Phương thứcMô tả
Object put(Object key, Object value)Nó được sử dụng để chèn một mục nhập trong map hiện tại.
void putAll(Map map)Nó được sử dụng để chèn map chỉ định vào map hiện tại.
Object remove(Object key)Nó được sử dụng để xóa một mục nhập của key được chỉ định.
Object get(Object key)Nó được sử dụng để trả lại giá trị cho khoá được chỉ định.
boolean containsKey(Object key)Nó được sử dụng để tìm kiếm key được chỉ định từ map hiện tại.
Set keySet()Nó được sử dụng để trả đối tượng Set có chứa tất cả các keys.
Set entrySet()Nó được sử dụng để trả lại đối tượng Set có chứa tất cả các keys và values.

Map.Entry Interface

Entry là một interface con của Map. Vì vậy, chúng ta được truy cập nó bằng tên Map.Entry. Nó cung cấp các phương pháp để truy xuất các key và value.

Các phương thức của Map.Entry interface

Phương thứcMô tả
Object getKey()Nó được dùng để lấy key.
Object getValue()Nó được sử dụng để lấy value.

Phân biệt List và ArrayList

l3YItbjXQXR8GyrhYUZxXwrnTHVTIsgLFaOXblTC0rMSBNS ZDBrEcOro1kCT0uB8Q o 6ksM3OguGn1g8L7RlROphHGpAFwi7Ud5sUIWS3zK odQh konRWvb JBKEKJVRZv2QqLs9m - quochung.cyou PTIT
  • List là một interface –  ArrayList là một class thuộc framework Collection
  • List kế thừa từ Collection – ArrayList kế thừa AbstractArrayList và implement List
  • List lưu các phần tử theo thứ tự vị trí, ArrayList như một mảng động có thể thêm xoá phần tử

Sắp xếp trong Java

Sắp xếp bằng cách tạo hàm compare.to sử dụng giao diện Comparable để sắp xếp

XpxUcDwgV3pbMLRG4GxdnGT0YTZLY 7JDFAhyafARqXcz EWyrVgZsqugh 3yZG IrgLtCzVv6RI4pGVxX jmf7x - quochung.cyou PTIT

Sắp xếp bằng cách sử dụng Comparator để sắp xếp tiện dụng hơn

R61xA9a2WnpDW53cJ3RltUzPcccRmmqUEiREl5ixNBz5V0SRZ1kj e9DC FIur0x 8WH8sc0GFT MA DdRJKRIAiswoNQbO3QLVjUjX - quochung.cyou PTIT

[site_reviews_form]

Tìm hiểu về Java cơ bản, Regex và 4 tính chất OOP trong Java

Java cơ bản

Cú pháp cơ bản

Khi nói về một chương trình Java, do đây là một ngôn ngữ được tạo ra với thiên hướng “Hướng đối tượng” rất lớn, nên chương trình Java có thể tóm tắt bằng việc một tập hợp các vật thể – object tương tác với nhau bằng các hàm, thông số, …

  • Object – Vật thể: Chứa thông số và các hàm. VD Object Máy tính có thông số CPU, Ram, dung lượng máy, .. có các hàm tương tác như: lướt web, bật máy, …
  • Class – Lớp: Là một bản gốc chứa các thông số và các hàm mà vật thể được tạo lên
  • Method – Hàm: Là các hàm tương tác với 1 object

Trước nhất, một cấu trúc chương trình cơ bản trong Java sẽ trông như sau:

- quochung.cyou PTIT

Các lưu ý về cú pháp trong Java:

  • Quan trọng viết hoa: Tức là Hellohello sẽ khác nhau trong Java
  • Tên file: Tên một file thường trùng tên với class trong file đó
  • Hàm Main(): Trong mỗi class luôn cần có method main(), sẽ là thứ đầu tiên được chạy của hàm đó

Nhập xuất

Để nhập vào các dữ liệu từ input phía người dùng trong Java, chúng ta sẽ sử dụng lớp Scanner có sẵn trong Java để nhập vào dữ liệu.

Đầu tiên cần khai báo và import thư viện Scanner

d4ShbqBEYkxwtDP5qg2IiV8EPOjuo7Dp8qOyZPP1YYn3wdFHbznbhQmFESwmsMA1NYU j9OcXiIt5uv5Jv3r93mIhCoPLpnRwpTDWRSH5pA8uLT - quochung.cyou PTIT

Khởi tạo một object Scanner trong chương trình để đảm nhận việc lấy dữ liệu

- quochung.cyou PTIT

Ở đây ta đã lấy dữ liệu cho một biến int – tương tự như C, là một kiểu dữ liệu lưu số nguyên bằng hàm nextInt().

Cú pháp:

(data-type) (variable-name) = scan.next…();

Ta cần thay trong dấu “…” bằng phương thức ta muốn:

Phương thứcMô tả
nextByte()Đọc một số nguyên kiểu byte
nextShort()Đọc một số nguyên kiểu short
nextInt()Đọc một số nguyên kiểu int
nextLong()Đọc một số nguyên kiểu long
nextFloat()Đọc một số kiểu float
nextDouble()Đọc một số kiểu double
next()Đọc một string kết thức trước một ký tự trắng
nextLine()Đọc một line of text (kết thúc bằng phím Enter)

Ví dụ: Nhập một string.

XCwDQMbooym1U2GYI2OLYDPYVMR5z9 Zzof2 7ETeGrsF CBe2CXC2caLa2aqll9VhN2CQXV4BC5v2w4ommFQ2XrHk13pdjBl6qQh7Vtfgot4u9qECfqvdqupmh5HcdEq9YFsjMYbbgSnIdgufpS Q - quochung.cyou PTIT
  • Trong Java cũng gặp tình trạng bị trôi lệnh khi nhập 1 string ngay sau khi nhập 1 số. Cách sử lý là dùng thêm 1 lần nhập scan.nextLine() trước khi nhập string.
1GzSTAKxxTKzrpiG pwya5V5mbPYOtMPk7MImqjauAg C2HZFfTO475AzOdwQ2swl7TJFPoQwAVL2VQWRo6GB7rSQHzzz UkRlx RdhvVCZQO3OVh FaV178nUHwDg7k0QfKYo4qMIS86sNIfcj5Nw - quochung.cyou PTIT

Để in ra dữ liệu trong Java

  • Sử dụng System.out.println();
    • Xuất kết quả ra màn hình đồng thời con trỏ chuột nhảy xuống dòng tiếp theo
  • Sử dụng System.out.print();
    • Xuất kết quả ra màn hình nhưng con trỏ chuột không xuống dòng.
  • Sử dụng System.out.printf();
    • Xuất ra màn hình kết quả đồng thời có thể định dạng được kết quả đó nhờ vào các đối số thích hợp. (đặc tả)
    • Giống trong C và CPP.

Ví dụ chương trình nhập vào a, b, c và in ra a+b+c

UW0ZOZIkKORmBqllGVt fOb3AnE3bpAyhXJDwG2Gev2ipj6siWvZC0Zzi9bna2xNvJqha62HxIU L7TCK8TtyebiJ moUrRId8bdnb - quochung.cyou PTIT

Biến

Khai báo: Data-type Variable-name = giá trị;

e Z1l79WxgoZ1Ht XJAIct CYFuolCXxLXyxrT QaDum47UPv7hhg8D3fljVEB 6vFQKxTy6ZxSs7U Jwlag0NceGZb0rMyfe6ULodTkNypUGXcSr - quochung.cyou PTIT

Biến trong Java có 3 loại, mỗi loại nằm ở vị trí riêng:

  • Biến thực thể (instance variable): nằm trong class, không thuộc method nào. Biến này thuộc về các thực thể (instance – object) được tạo ra từ class.
  • Biến tĩnh (static variable): tương tự biến thực thể, nhưng có từ khóa static để đánh dấu biến thuộc về class, không phải object.
  • Biến cục bộ (local variable): được khai báo trong method.
YjEGEubPn91QA A9cn4EIIQ4WtLLgnyXbCYqoWHCyhkzntfx1o8d - quochung.cyou PTIT

Toán tử

Các toán tử trong java tương tự như C++:

  • Toán tử tăng giảm (increment, decrement): a++, ++a, a–, –a
  • Toán tử thuận và đối (position, negation): +a, -a
  • Toán tử số học (arithmetic): a + b, a – b, a * b, a / b (chia nguyên), a % b (chia dư)
  • Toán tử ba ngôi (ternary): a > b ? a : b và có thể lồng nhau như (a > 0) ? “Dương” : (a < 0) ? “Âm” : “Số không”
  • Toán tử gán (assignment): phép gán a = 5 và phép gán kết hợp như a += 5, a -= 3
  • Toán tử quan hệ (relational): So sánh bằng a == 5, khác a != 5, lớn a > 5, bé a < 5, lớn hơn hoặc bằng a >= 5, bé hơn hoặc bằng a <= 5.
  • Toán tử logic (thực hiện trên boolean): phép and a && b, or a || b và not !a
  • Toán tử thao tác bit (bitwise): gồm and 10 & 3, or 10 | 3 và not !10. Các phép bitwise thực hiện trên số nguyên, và kí hiệu chỉ gồm 1 dấu.
  • Toán tử dịch chuyển bit (bit shifting): phép dịch trái x << y (nhân 2^y), dịch phải điền cùng dấu x >> y (chia nguyên 2^y) và dịch phải điền 0 (zero filled – chia nguyên lấy trị tuyệt đối cho 2^y) x >>> y.

Câu lệnh rẽ nhánh if else

Lệnh if else tương tự như trong C++. Nếu điều kiện trong if đúng thì thực hiện lệnh, nếu không thì đi tiếp hoặc thực hiện lệnh trong else (nếu có).

HUJWDAEwyK4UpA9boMoeir7vLR6Yf9u941YhsWNBrv8 5UTCfI7LSNX9crbSswXFFOKal sKpnfiGYbKkgWULltvOYqEk7oabni4TDC15LQMB blIy9wEI2NMEYSaQRz4XMQ6kVQVDMUyP3Yoj 2nw - quochung.cyou PTIT

Ngoài ra ta có thể thay if else bằng toán tử 3 ngôi hoặc switch-case với cấu trúc giống như C++.

Vòng lặp

While loop

Vòng lặp while kiểm tra điều kiện, nếu đúng thì thực hiện lệnh, sau đó quay lại kiểm tra điều kiện lần nữa,… cho tới khi điều kiện sai thì dừng.

dxUA9zFg6R6824hGZvHTcL0Qm1nkOZfrigNin hiXFI3xLCTZYaIU TQYJZY0Wt KdjlqjiLY2Ne0birdqSrt3AsJvdruZ3QvPLUSeDWnWBgwmTmWqYtuXRQjEZJBiqnG9I9ZFjuVekde5GRBD7hAw - quochung.cyou PTIT

Do while loop

Lặp do while khác với while chút xíu, là do while sẽ thực hiện lệnh trước rồi mới check điều kiện. Do đó, lệnh luôn được thực hiện ít nhất một lần.

9 c6EirvGWa9h8 iJ4LqKO5qtcuU4AmkDvR5NRbJ3d14 z3MmWJOtSg7951clkoYQw 1WUEELH4Sd7cyDA0hG HtYGkiZZWRiS8DAwmoTM1VHnkO0qjP1DgrauCCESZaWJrRiQD8DAXoSXpOBebwWw - quochung.cyou PTIT

Cả hai vòng lặp do while và while cần tránh lặp vô tận.

For loop

bH2cbQGchETxH4LOLtRUbIGTXsFUWQhcyBQ1Fq4yfg8c9 JoEU5rI8HMYCAXloTmYCAYPdw8Txs9i8Mj3pXCa9eckvNub9bJbXiWyvXz - quochung.cyou PTIT

Lệnh lặp for gồm 3 phần trong ngoặc tròn:

  • Phần init dùng khởi tạo biến đếm
  • Phần condition chỉ định điều kiện lặp tiếp
  • Phần increment dùng tăng, giảm biến đếm, để tới lúc nào đó điều kiện trở thành false.

Java có một vòng lặp khác, gọi là for each nhưng vẫn dùng từ khóa for, nhưng theo cú pháp khác.

IETV2P326PUyV73Hv43YV8 8U6OALzC5aoayCf wkxV8TlnadDKpjxjp2qKKDG29LthVb ny1TVXYlgC09N1 Kb 5jv11DDO JXNx0Jl8EIUvFDJE VL5asaydmNQ - quochung.cyou PTIT

Mảng – Array

Cần import thư viện Array để sử dụng

tv4pBB1oiXBngPY8rXto27fnpBCTtCUWopb6qhWltdDVDf4iPfYW z DppqJ7i7AJ3V4W0Me GNkyrrN0fsTVm 8wsD8wRKjvaL6Vra5g1PR2LKAAR9Y4Jh2P1EBD5w42Zi3y5S85LB5NqEIPvSUDA - quochung.cyou PTIT

Khai báo mảng: Data-type Array-name

Zue1kRbn73F1 qEW4KC7CpS3wVNMotEqQQ1sLdFybIYF9zuJ9u AigBMyehiW8LIdsjF r3AHty EKb8MlxryVrbjSErNaDaJyNnCLaWNhmYiC8h GCBl2VC4kDcu9ukNS4MkKbC5R0MQJ3NnkQHbQ - quochung.cyou PTIT
kdH6lqXxIqP4D3Cq0 1vTyQynO2Gta uU Mnv5xvxSZi9wGPQNNaI6UCrQKgbvNSSUI6m8C8gagdHDUZU6k9zzC - quochung.cyou PTIT
  • Truy cập một phần tử trong mảng: Tên mảng[vị trí] – giống với C++ a[x]
  • Bằng các cú pháp nhập xuất tương tự C++, có thể triển khai sắp xếp, làm việc trên mảng

Wrapper Class

Lớp Wrapper sẽ giúp chúng ta chuyển đổi qua lại giữa một kiểu dữ liệu nguyên thủy sang kiểu dữ liệu đối tượng và ngược lại.

Kiểu nguyên thủyKiểu Wrapper
booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble

int a = 5;

a.ham()

Integer a = 5; a.ham()

  • Chuyển đổi một kiểu nguyên thủy sang kiểu Wrapper của nó người ta gọi là Boxing. Ngược lại, khi bạn chuyển từ một kiểu Wrapper sang kiểu nguyên thủy của nó người ta gọi là Unboxing.
  • Một số cấu trúc khác bên trong ngôn ngữ Java, như ArrayList hay Vector đều chứa đựng các tập hợp kiểu dữ liệu đối tượng thay vì kiểu nguyên thủy, nên việc biết và vận dụng các lớp Wrapper là một bắt buộc.
  • Ngoài ra thì kiểu dữ liệu đối tượng sẽ thích hợp hơn trong việc thực thi đa luồng (multithreading) và đồng bộ hóa (synchronization).
  • Các phương thức với Wrapper class:
    • …Value(): giúp chuyển đổi một giá trị của lớp Wrapper nào đó về kiểu dữ liệu nguyên thủy (unboxing).
    • parse…(“”): Tham số truyền vào cho phương thức là một chuỗi, kết quả nhận được là một giá trị nguyên thủy tương ứng với chuỗi truyền vào.
    • toString(): chuyển đổi từ liểu dữ liệu Wrapper thành string.
OI7t2DwTSt3W0WPWjI9aLjldapVMTUM3HZueXpBCt7dtwKTx lS0k8fcpPqHmZ - quochung.cyou PTIT

String Class

String không phải kiểu dữ liệu nguyên thuỷ, mà một đối tượng của class String.

Cần import thư viện

41xXlAv9jdQqNH4UPKjLjpC9XQ0aHoGybInKQ5oKWWV6zEGTHxhB8ktTEDRWXozX yNaZEF9Cq9m9HNlQ8MY0V2qbQCVTWYSYLgUfZqDmidVgjk fD n8FfYEXnH4k7LhNZKY9bM 3wNq1VdaChCuA - quochung.cyou PTIT
S5XvA0 yH0ITS - quochung.cyou PTIT
  • Class String chứa các hàm có chức năng tương đồng với kiểu dữ liệu chuỗi của các ngôn ngữ khác như C++, Python, có thể kể đến vài hàm chính như:
  • length() : Trả về độ dài chuỗi
  • charAt(i): Kí tự ở vị trí i ; 
  • trim(): Bỏ đi khoảng trắng hai đầu
  • replace(): Thay đổi hết các đoạn chuỗi con thành đoạn chuỗi con mới
  • Và rất nhiều hàm khác được tích hợp sẵn

Math Class

Là lớp chứa các phương thức, hỗ trợ Toán Học trong Java, tương tự C++, có rất nhiều hàm được dùng phổ biến như:

  • Pow : Luỹ thừa
  • Sqrt: Lấy căn
  • Sin, cos, tan: Lượng giác
  • Floor, Ceil: Làm tròn
  • Và nhiều hàm hỗ trợ khác cho các công việc đặc thù.

Regex

Thuật ngữ Regex viết tắt của Regular expression. Nó là một chuỗi ký tự khác nhau diễn tả một mẫu tìm kiếm cụ thể. Nó cũng được gọi là Rational expression.

Nó được dùng để tìm kiếm và thao tác trên chuỗi văn bản. Nói một cách dễ hiểu là bạn có thể tìm một một mẫu dễ dàng và thay thế chúng bằng những mẫu phù hợp với sự giúp đỡ của biểu thức chính quy.

Pattern Class: (Lớp khuôn mẫu)

  • Đây là phiên bản biên dịch của một biểu thức chính quy. Nó được sử dụng để xác định một khuôn mẫu cho các biểu thức chính quy.
  • Lớp này có phương thức Compile(), ta sẽ truyền Xâu cần so khớp vào trong phương thức này, và nó sẽ trả về 1 đối tượng Pattern.
No.Phương thứcMô tả
1static Pattern compile(String regex)biên dịch regex đã cho và trả về thể hiện của Pattern.
2Matcher matcher(CharSequence input)tạo một matcher khớp với đầu vào đã cho với mẫu.
3static boolean matches(String regex, CharSequence input)Nó biên dịch biểu thức chính quy và tìm kiếm các chuỗi con từ chuỗi input phù hợp với mẫu regex.
4String[] split(CharSequence input)chia chuỗi input đã cho thành mảng các kết quả trùng khớp với mẫu đã cho.
5String pattern()trả về mẫu regex.

Matcher Class – Lớp so sánh

  • Lớp Matcher thực thi interface MatchResult. Nó được sử dụng để thực hiện các hoạt động so khớp trên một chuỗi ký tự.
  • Thực hiện chức năng So khớp chính.
  • Ta sẽ có được 1 đối tượng Matcher khi gọi tới phương thức matcher() trên một đối tượng Pattern.
  • Ta truyền Xâu đầu vào – Xâu cần so khớp vào matcher() và xem xem nó có chứa mẫu mong muốn xuất hiện.
No.Phương thứcMô tả
1boolean matches()kiểm tra xem biểu thức chính quy có khớp với mẫu hay không.
2boolean find()tìm biểu thức tiếp theo khớp với mẫu.
3boolean find(int start)tìm biểu thức tiếp theo khớp với mẫu từ số bắt đầu đã cho.
4String group()trả về chuỗi con phù hợp.
5int start()trả về vị trí bắt đầu của chuỗi con phù hợp.
6int end()trả về vị trí kết thúc của chuỗi con phù hợp.
7int groupCount()trả về tổng số các chuỗi con phù hợp.

Cú pháp Regex

No.Biểu thức chính quyMô tả
1.Khớp (match) với bất kỳ ký tự nào
2^regexBiểu thức chính quy phải khớp tại điểm bắt đầu
3regex$Biểu thức chính quy phải khớp ở cuối dòng.
4[abc]Thiết lập định nghĩa, có thể khớp với a hoặc b hoặc c.
5[abc][vz]Thiết lập định nghĩa, có thể khớp với a hoặc b hoặc c theo sau là v hay z.
6[^abc]Khi dấu ^ xuất hiện như là nhân vật đầu tiên trong dấu ngoặc vuông, nó phủ nhận mô hình. Điều này có thể khớp với bất kỳ ký tự nào ngoại trừ a hoặc b hoặc c.
7[a-d1-7]Phạm vi: phù hợp với một chuỗi giữa a và điểm d và con số từ 1 đến 7.
8X |∣ ZTìm X hoặc Z.
9XZTìm X và theo sau là Z.
10$Kiểm tra kết thúc dòng.
11\dSố bất kỳ, viết ngắn gọn cho [0-9]
12\DKý tự không phải là số, viết ngắn gon cho [^0-9]
13\sKý tự khoảng trắng, viết ngắn gọn cho [ \t\n\x0b\r\f]
14\SKý tự không phải khoản trắng, viết ngắn gọn cho [^\s]
15\wKý tự chữ, viết ngắn gọn cho [a-zA-Z_0-9]
16\WKý tự không phải chữ, viết ngắn gọn cho [^\w]
17\S+Một số ký tự không phải khoảng trắng (Một hoặc nhiều)
18\bKý tự thuộc a-z hoặc A-Z hoặc 0-9 hoặc , viết ngắn gọn cho [a-zA-Z0-9].
19*∗Xuất hiện 0 hoặc nhiều lần, viết ngắn gọn cho {0,}
20++Xuất hiện 1 hoặc nhiều lần, viết ngắn gọn cho {1,}
21??Xuất hiện 0 hoặc 1 lần, ? viết ngắn gọn cho {0,1}.
22\{ X \}{X}Xuất hiện X lần, {}
23\{ X, Y \}{X,Y}Xuất hiện trong khoảng X tới Y lần.
24*?∗?* có nghĩa là xuất hiện 0 hoặc nhiều lần, thêm ? phía sau nghĩa là tìm kiếm khớp nhỏ nhất.
K0VqPW12e2Rt V nY aU3YdTYW89WMhlTVdwCHkjoE 3k5JSh9sOsfhfy5e0k1RLSNZYHslcGnz0YyPU6hQ8iH6IZOq8CttsXi8eX5FAohNZi4i2iEtaqq17l3hT Yv QkKzt0PsZBN YPgwez4Qw - quochung.cyou PTIT

Class và Object – Java OOP

Encapsulation (Tính đóng gói)

  • Tính đóng gói trong java là kỹ thuật ẩn giấu thông tin không liên quan và hiện thị ra thông liên quan. Mục đích chính của đóng gói trong java là giảm thiểu mức độ phức tạp phát triển phần mềm. Tức là thiết kế để các thuộc tính và phương thức thuộc về (bên trong) một lớp.
  • Với các access modifier, tính đóng gói sẽ có thể giúp ngăn chặn những lớp bên ngoài truy cập, thay đổi thuộc tính và phương thức của một lớp. Từ đó, giúp cho việc che giấu dữ liệu, để bảo vệ trạng thái bên trong của một đối tượng. Hơn nữa, việc ẩn giấu các biến thì các lớp sẽ không chia sẻ thông tin với nhau được. Điều này làm giảm số lượng khớp nối có thể có trong một ứng dụng.

Ví dụ: Các biến của class đều ở private và chỉ truy cập được qua các phương thức public.

Ce60mqRAo9P9W55lx0wM OxCm8lFECFJrI7eqpQN1FoJiTiw IcJaf2vvVu - quochung.cyou PTIT

Tính kế thừa

Tính kế thừa trong Java là một cơ chế trong đó một đối tượng có được tất cả các thuộc tính và hành vi của một đối tượng cha.

  • Khi bạn kế thừa từ một class có sẵn, bạn có thể tái sử dụng các phương thức và trường của class cha. Hơn nữa, bạn có thể thêm các phương thức và trường mới trong class hiện tại.
  • Sử dụng tính kế thừa, ta có thể thực hiện Method Overriding, từ đó xuất hiện tính Đa hình trong runtime.
  • Phân loại:
    • Đơn kế thừa.
    • Kế thừa nhiều cấp.
    • Kế thừa thứ bậc.
Ảnh
  • Đa kế thừa và Kế thừa hỗn hợp không được hỗ trợ thông qua Class trong Java, nhưng vẫn được hỗ trợ thông qua Interface.

Cú pháp: sử dụng từ khóa extends

p1Jp1YQ59htzcCL1CeeXlpwwBLgmGsYtp9bWkP1vs28ahClg3wL7MArd7HWLVokbJsHV09znbF6IQU KsbABrCVy9b4h1jT4LzfNECaahCiP4hniZaq VYizxxMfvRVmFe wzLNvxfhliURevK H1A - quochung.cyou PTIT
3BMaF24YMVkGVr TjYtwE5HK3krzXMXVCAXOUt UcJ nOKP2gNzK6QITmHCzEseoayATqN f1eM - quochung.cyou PTIT
  • Ngoài ra còn có từ khóa super để tham chiếu trực tiếp đến đối tượng của lớp cha gần nhất.
  • Sử dụng từ khóa super.xxx để gọi đến phương thức xxx của lớp cha gần nhất.
  • Sử dụng super() để gọi trực tiếp constructor (hàm tạo) của lớp cha gần nhất.

Polymorphism (Tính đa hình).

  • Đa hình là ta có thể thực hiện một hành động bằng nhiều cách khác nhau.
  • Có hai kiểu của đa hình trong java, đó là đa hình lúc biên dịch (compile) và đa hình lúc thực thi (runtime). Chúng ta có thể thực hiện đa hình trong java bằng cách method overriding (ghi đè phương thức) và method overloading (nạp chồng phương thức).
  • Ghi đè phương thức: method overriding – ghi đè method ở lớp cha trong lớp con, ta chỉ cần viết code thực thi khác của method đó ở trong lớp con là được.
  • Nạp chồng phương thức : method overloading – Trong một class có thẻ có nhiều method cùng tên nhưng khác nhau tham số truyền vào và dữ liệu trả về, cách hoạt động khác nhau.
zxjvmgNtzKEZWud8u02qY7sfyLPwk3AnZcZ7PiLjFMpgHlhgsklByV0kD5Mm2NvcmK bCsDH2 rlldIq - quochung.cyou PTIT
6joY FctI6mmYgFi YZ53KBJcMsJ 1K5u C132rbLXiX6C463RTxTlYSSx4PwAEzKVVBWxXY7qnWVni LaGnSyHgnZuqWiaCfw5CSeDhIfF6mBS0zCGCc9u3QtpV4h03 56MERfYMTmw YGBAzl4g - quochung.cyou PTIT

Abstract Classes (Lớp trừu tượng).

  • Một phương thức được khai báo là abstract và không có code thực thi thì đó là phương thức trừu tượng, (giống virtual trong C++), được khai báo với từ khóa abstract ở trước.
  • Lớp trừu tượng thường được dùng để kế thừa. Và lớp trừu tượng không thể tạo ra instance.
  • Lớp abstract là lớp có các abstract method (Phương thức trừu tượng) (Có thể có hàm không trừu tượng)
  • Phương thức trừu tượng (abstract method)
    • Một phương thức được khai báo là abstract và không có trình triển khai thì đó là phương thức trừu tượng.
    • Nếu muốn một lớp chứa một phương thức cụ thể nhưng bạn muốn triển khai thực sự phương thức đó để được quyết định bởi các lớp con, thì bạn có thể khai báo phương thức đó trong lớp cha ở dạng abstract.
hms8oMyC9f6CcqbpRKUSJF7aYpy AaqY5JxDHXeCpuEJovaWF zyjcCrGvZg178w1N547DaSYCm2YzS fY8eF no5wgxht10UgMm8T2ZL1hOwd UeMKz - quochung.cyou PTIT

Interface

Là một bản thiết kế của một lớp. Nó chỉ có các phương thức trừu tượng. Interface là một kỹ thuật để thu được tính trừu tượng hoàn toàn và đa kế thừa trong Java. Nó không thể được khởi tạo giống như lớp trừu tượng.

1d27jfdZ26V 6Jf7zJCbe o Iguv7NhQC4p1E92JjyAlmVqisYEAZZecGXV7EW1ab2Y DNGj9CJKNrDvLnsguzhxu8u1Iivq1V2 w3354ZC 7BgZ4fMMTLyapTpJBlPFSIBsaCaPe6i HFhw1gnhJQ - quochung.cyou PTIT

Một interface khác với một class ở một số điểm sau đây:

  • Không thể khởi tạo interface, interface không có Constructor
  • Phương thức interface đều phải là abstract (Khác lớp trừu tượng vẫn có thể có phương thức không trừu tượng)
  • Tất cả trường interface đều là static hoặc final
  • Interface chỉ có thể kế thừa từ interface
uA5WtBBrDCh2LDj OXxLrVnWpP 5BVC9Y4ZM9IxOrEv2P5fVstrgbQe5qt3bQxfIP5hFaCFHPVMM9Fog0ORpdTon8lfVupXMwKYslI3WAKupKPL0Ms k3fcpua2vdCJLOlWIoXQnrnAYfy 9VHCfog - quochung.cyou PTIT

 Modifiers

  • Có hai loại Modifier trong Java, đó là: Access ModifierNon-access Modifier
  • Tương tự OOP trong C++, trong Java có 4 phạm vi truy cập của Access Modifier như sau:
    • private
    • default
    • protected
    • public
Access ModifierTrong lớpTrong packageNgoài package bởi lớp conNgoài package
PrivateYNNN
DefaultYYNN
ProtectedYYYN
PublicYYYY

Lưu ý:

  • Constructor cần để public để có thể thực hiện tính Kế thừa.
  • Nếu không khai báo Access Modifier, thì nó mặc định là default.
  • Protected access modifier được truy cập bên trong package và bên ngoài package nhưng phải kế thừa.
  • Ngoài ra có thêm final dùng để hạn chế người dùng:
    • final variable: không thể thay đổi giá trị (hằng số)
    • final parameter: tương tự final variable
    • final method: không thể ghi đè
    • final class: không thể kế thừa