NỘI DUNG BÀI HỌC
✅ Package là gì?
Một package có thể hiểu như là một gói chứa các lớp các package con. Còn xét về máy tính nhận diện thì nó như một folder. Chỉ khác khi trên IDE nhận diện thôi.
🔅 Cách tạo package trong IntelliJ và Eclipse
Để tạo ra package đơn giản chỉ cần click phải chuột vào thư mục src
trong IntelliJ hoặc Eclipse chọn New > Package
rồi nhập tên
#Chú ý: chổ tên nên đặt chữ thường, nếu dài thì dùng dấu gạch dưới để phân cách. Ví dụ: learnpackage, learn_tinh_ke_thua
Trên IDE thì nó nhận diện là package hay directory. Còn trong máy tính nó đều là directory bình thường nó sẽ như sau:
🔅 Package khác Directory chổ nào?
Điểm khác chính là package mới chứa class và thực thi được code. Còn directory có thể chứa class nhưng không thực thi được code bên trong class đó.
🔅 Công dụng của Package trong Java
- Bố trí gọn source code của mình
- Có thể chứa class java để thực thi code
- Đóng gói code theo các package chỉ định => tránh edit nhầm, phân permission
- Run nhiều class thông qua package (trong automation test sau này)
🔅 Mô tả các vấn đề liên quan Package trong code Java
- Để di chuyển file trong package thì chỉ cần đè chuột trái vào tên file cần chuyển rồi kéo thả tạo tên package cần chuyến đến.
Khi đó trong code nó tự động đổi tên package theo.
Sau khi di chuyển
Việc thay đổi này do IDE tự sửa cho chúng ta:
-
Khi ta tạo một class nằm trong một package nào đó thì phải khai báo package đầu tiên: Ở đây class Mang nằm trong package array. Ta sẽ viết package array. Theo đường dẫn class Mang sau khi di chuyển nằm trong package array_child2, nên ở class Mang ta sẽ viết package
array.array_child.array_child2;
-
Khi ta sử dụng một class nằm ở packge khác, ta phải import qua để chương trình hiểu ta đang sử class của package nào. Theo như hình dưới thì class Mang sau khi được gọi lại sử dụng trong class MangNacDanh thì ta sẽ viết import
array.array_child.array_child2.Mang;
✅ Lớp và Đối tượng trong java (Class and Object)
Trong bài này chúng ta sẽ học về lớp và đối tượng trong java. Trong phương pháp lập trình hướng đối tượng, chúng ta thiết kế chương trình bằng việc sử dụng các lớp và đối tượng.
🔆 Đối tượng (object)
Một thực thể có trạng thái và hành vi được gọi là đối tượng. Ví dụ như máy pha cà phê, xe đạp, cái quạt...
Trong Java thì đối tượng là một cái tên, đại diện cho một class để có thể gọi các thành phần trong class ra sử dụng.
Một đối tượng có ba đặc điểm:
- Trạng thái: Đại diện cho dữ liệu (giá trị) của một đối tượng.
- Hành vi: Đại diện cho hành vi (chức năng) của một đối tượng như gửi tiền, rút tiền, ...
- Danh tính: Danh tính của một đối tượng thường được cài đặt thông qua một ID duy nhất. ID này được ẩn đối với user bên ngoài. Tuy nhiên nó được sử dụng trong nội bộ máy ảo JVM để định danh từng đối tượng.
Ví dụ: Bút nói chung. Thì Bút chì là một đối tượng. Tên của nó là B4, màu vàng, chì đậm, ... được gọi là trạng thái. Nó được sử dụng để viết, viết được gọi là hành vi. Và nó sẽ có một mã định danh cho sản phẩm thì được gọi là Danh tính.
Ví dụ khác:
Lớp Động vật: các thuộc tính chung như có Vú, có Sừng, Màu, Chân,...
=> Đối tượng là con Chó Golden: có vú nhỏ, không sừng, màu vàng, 4 chân
=> Đối tượng là con Trâu: có vú to, có sừng, màu đen, 4 chân
Trong Java thì Đối tượng (Object) là một thể hiện của một lớp (Class). Lớp là một mẫu hoặc thiết kế chung gồm nhiều thành phần thuộc tính tính năng, và tạo ra các đối tượng cụ thể khác nhau để dùng các thành phần bên trong Class. Vì vậy, đối tượng là các thể hiện (kết quả) của một lớp.
🔆 Lớp (class)
Class là một cấu trúc chung có nhiều thành phần khai báo sẵn để có thể gọi ra dùng hoặc xử lý và Class thì có thể nhiều đối tượng thể hiện cho 1 class.
Một class trong java có thể chứa:
- Thành viên dữ liệu
- Constructor
- Phương thức
- Khối lệnh
- Lớp và Interface
Ví dụ đơn giản về Lớp và Đối tượng trong Java
Trong ví dụ này, chúng ta tạo một lớp Student có hai thành viên dữ liệu là id và name. Chúng ta đang tạo đối tượng của lớp Student bởi từ khóa new và in giá trị đối tượng.
class Student1{
int id; //thanh vien du lieu (cung la bien instance)
String name; //thanh vien du lieu (cung la bien instance)
public static void main(String args[]){
Student1 s1=new Student1(); //tao mot doi tuong Student
System.out.println(s1.id);
System.out.println(s1.name);
}
}
Các ví dụ đơn giản về lớp và đối tượng trong java
Ví dụ 1:
Trong ví dụ này, chúng tôi đã tạo ra một lớp Student có hai thành viên dữ liệu id và name. Chúng ta tạo ra các đối tượng của lớp Student bởi từ khóa new và in giá trị của các đối tượng.
public class Student {
int id; // thành viên dữ liệu
String name; // thành viên dữ liệu
public static void main(String args[]) {
Student student1 = new Student(); // tạo một đối tượng student1
System.out.println(student1.id);
System.out.println(student1.name);
}
}
Kết quả:
0 null
Ví dụ 2:
public class Student2 {
int id;
String name;
// phương thức insertRecord
void insertRecord(int id, String name) {
this.id = id;
this.name = name;
}
// phương thức displayInformation
void displayInformation() {
System.out.println(id + " " + name);
}
public static void main(String args[]) {
Student2 s1 = new Student2();
Student2 s2 = new Student2();
s1.insertRecord(111, "Anh");
s2.insertRecord(222, "Tester");
s1.displayInformation();
s2.displayInformation();
}
}
Kết quả:
Anh
Tester
Ví dụ 3:
public class Student3 {
int id;
String name;
// constructor
public Student3(int id, String name) {
this.id = id;
this.name = name;
}
// phương thức displayInformation
void displayInformation() {
System.out.println(id + " " + name);
}
public static void main(String args[]) {
Student3 s1 = new Student3(111, "Anh");
Student3 s2 = new Student3(222, "Tester");
s1.displayInformation();
s2.displayInformation();
}
}
Kết quả:
Anh
Tester
🔆 Có những cách nào để tạo đối tượng trong java?
Có vài cách để tạo đối tượng trong java, đó là:
- Sử dụng từ khóa new
- Sử dụng phương thức newInstance()
- Sử dụng phương thức clone()
- Sử dụng phương thức factory,...
Chúng ta sẽ chỉ học cách tạo đối tượng qua từ khóa "new"
Còn 3 cách kia thì các bạn tham khảo thêm tại đây
🔆 Đối tượng Anonymous trong java
Anonymous nghĩa là vô danh. Một đối tượng không có tham chiếu gọi là đối tượng Anonymous.
Nếu bạn sử dụng đối tượng 1 lần duy nhất, thì lựa chọn tạo đối tượng Anonymous là tốt nhất trong trường hợp này.
Ví dụ:
public class Calculation {
void fact(int n) {
int giaithua = 1;
for (int i = 1; i <= n; i++) {
giaithua = giaithua * i;
}
System.out.println("Giai thừa của " + n + " là: " + giaithua);
}
public static void main(String args[]) {
// gọi phương thức của đối tượng anonymous
new Calculation().fact(5);
}
}
Kết quả:
Giai thừa của 5 là: 120
🔆 Sự khác nhau giữa Lớp và Đối tượng trong java
Sự khác nhau giữa lớp và đối tượng trong java được thống kê trong bảng sau:
No | Đối tượng | Lớp |
1. | Đối tượng là thể hiện của 1 lớp. | Lớp là một khuân mẫu hay thiết kế để tạo ra các đối tượng. |
2. | Đối tượng là 1 thực thể trong thế giới thực như Bút chì, Xe đạp, ... | Lớp là một nhóm các đối tượng tương tự nhau. |
3. | Đối tượng là 1 thực thể vật lý | Lớp là 1 thực thể logic |
4. | Đối tượng được tạo ra chủ yếu từ từ khóa new. Ví dụ: Student s1=new Student(); |
Lớp được khai báo bằng việc sử dụng từ khóa class. Ví dụ: class Student{} |
5. | Đối tượng có thể được tạo nhiều lần. | Lớp được khai báo 1 lần duy nhất. |
6. | Đối tượng được cấp bộ nhớ khi nó được tạo ra. | Lớp không được cấp bộ nhớ khi nó được tạo ra. |
7. | Có rất nhiều cách để tạo ra đối tượng trong java như từ khóa new, phương thức newInstance(), phương thức clone(), phương thức factory và deserialization. | Chỉ có một cách để định nghĩa lớp trong java sử dụng từ khoá class. |
Bài tập rèn luyện
Bạn hãy viết chương trình khai báo lớp Student
với thông tin giống như sau:
Giải thích: hình trên là mô tả của lớp Student
với các thuộc tính và phương thức giống như sau:
name
vàage
là hai thuộc tính dùng để lưu trữ tên và tuổi của đối tượng học sinh.display()
là phương thức dùng để hiển thị thông tin của đối tượng ra màn hình. Ví dụ, nếu đối tượng có thuộc tínhname = "An", age = 25
thì khi gọi phương thứcdisplay()
thì màn hình sẽ hiển thị ra:
Name: An Age: 25
getInformation()
là phương thức dùng để nhập dữ liệu cho thuộc tínhname
vàage
từ bàn phím. Ví dụ nếu bạn dùng phương thức này để nhập thông tin cho một đối tượng học sinh vớiname = "Hoa", age = 22
như bên dưới:
thì khi gọi phương thứcHoa 22
display()
màn hình sẽ hiển thị ra:
Name: Hoa Age: 22
Lý thuyết
Bài trước bạn đã hiểu sơ qua về lập trình hướng đối tượng và đã biết một số khái niệm như lớp, đối tượng, thuộc tính và phương thức.
Ở phần này bạn sẽ biết cách định nghĩa ra một lớp và sử dụng các đối tượng thuộc lớp này.
Về bản chất, lớp (class) là một kiểu dữ liệu do bạn tự định nghĩa và đối tượng chính là biến của kiểu dữ liệu này. Cú pháp để khai báo một lớp như sau:
class <tên_lớp> {
// Khai báo danh sách các thuộc tính
<kiểu_dữ_liệu> <tên_thuộc_tính>;
...
// Khai báo danh sách các phương thức
public <kiểu_trả_về> <tên_phương_thức>(<kiểu_tham_số> <tên_tham_số>, ...) {
...
}
...
}
Ví dụ bạn có thể tạo ra lớp Student
với 2 thuộc tính là name, age
và 2 phương thức là getInformation()
và display()
giống như sau:
class Student{
String name;
int age;
public void getInformation() {
}
public void display() {
}
}
Để khai báo đối tượng của một lớp bạn dùng từ khóa new
. Ví dụ để khai báo một đối tượng thuộc lớp Student
bạn làm như sau:
// Khai báo đối tượng s1 thuộc lớp Student
Student s1 = new Student();
Để truy xuất tới các thuộc tính và phương thức của một đối tượng bạn dùng toán tử "."
:
class Student {
String name;
int age;
public void display() {
System.out.println("My name is " + name);
}
}
public class Entry {
public static void main(String[] args) {
Student s1 = new Student();
// Gán giá trị cho thuộc tính name của đối tượng s1
s1.name = "Long";
// Gọi tới phương thức display của đối tượng s1
s1.display();
}
}
Kết quả khi chạy chương trình:
My name is Long
Có thể thấy lớp và đối tượng chỉ đơn giản là kiểu dữ liệu và biến. Kiểu dữ liệu này khác các kiểu dữ liệu thông thường như int, string, double, ... ở chỗ nó có thể chứa được các phương thức và kiểu dữ liệu khác.
Đọc tới đây bạn đã biết cách định nghĩa một lớp và sử dụng đối tượng của lớp này, hãy quay lại phần bài tập và làm thử.
Hướng dẫn
Code mẫu:
import java.util.Scanner;
class Student{
String name;
int age;
public void getInformation() {
Scanner sc = new Scanner(System.in);
name = sc.next();
age = sc.nextInt();
}
public void display() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
}
public class Entry {
public static void main(String[]args) {
Student s1 = new Student();
s1.getInformation();
s1.display();
}
}
✅ Access Modifier (phạm vi truy cập) trong java
Bảng dưới đây mô tả khả năng truy cập của các Access Modifier trong java:
Access Modifier |
Trong lớp |
Trong package |
Ngoài package bởi lớp con |
Ngoài package |
---|---|---|---|---|
Private |
Y |
N |
N |
N |
Default |
Y |
Y |
N |
N |
Protected |
Y |
Y |
Y |
N |
Public |
Y |
Y |
Y |
Y |
Hình ảnh minh họa:
✅ Phạm vi truy cập là gì?
Phạm vi truy cập (access modifiers) là xác định độ truy cập phạm vi vào dữ liệu của các thuộc tính, phương thức hoặc class.
Package (gói) là nhóm các class, interface hoặc các package con liên quan lại với nhau. Việc dùng package dùng để nhóm các class liên quan với nhau thành thư viện như thư viện swing, awt,…Ngoài ra, mục đích của package ngăn cản xung đột đặt tên, điều kiện truy cập, thuận tiện tìm kiếm và lưu trữ.
Các loại phạm vi truy cập
Có 4 loại phạm vi truy cập:
-
Private
-
(Default)
-
Protected
-
Public
1. Phạm vi truy cập Private
Private Access Modifier chỉ được truy cập trong phạm vi lớp.
Ví dụ về private access modifier trong java
Trong ví dụ, chúng ta tạo 2 lớp A và Simple. Lớp A chứa biến và phương thức được khai bao là private. Chúng ta cố gắng truy cập chúng từ bên ngoài lớp A. Điều này dẫn đến Compile time error:
class A {
private int data = 40;
private void msg() {
System.out.println("Hello java");
}
}
public class Simple {
public static void main(String args[]) {
A obj = new A();
System.out.println(obj.data);// Compile Time Error
obj.msg();// Compile Time Error
}
}
Vai trò của Private Constructor
Nếu bạn tạo bất kỳ constructor là private trong lớp, bạn sẽ không thể tạo instance của class bên ngoài nó. Ví dụ:
class A {
private A() {
}//private constructor
public void msg() {
System.out.println("Hello java");
}
}
public class Simple {
public static void main(String args[]) {
A obj = new A();//Compile Time Error
}
}
Chú ý: Một lớp không thể là private hoặc protected, ngoại trừ lớp lồng nhau.
2. Phạm vi truy cập Default
Nếu bạn không khai báo modifier nào, thì nó chính là trường hợp mặc định. Default Access Modifier là chỉ được phép truy cập trong cùng package.
Ví dụ về Default Access Modifier trong Java
package anhtester.demo;
class A {
void msg() {
System.out.println("Hello");
}
}
package anhtester.mypack;
import anhtester.demo.*;
public class B {
public static void main(String args[]) {
A obj = new A(); // Compile Time Error
obj.msg(); // Compile Time Error
}
}
Trong ví dụ trên, phạm vi truy cập của lớp A và phương thức của msg() của nó là mặc định nên chúng không thể được truy cập từ bên ngoài package.
3. Phạm vi truy cập Protected
Protected access modifier được truy cập bên trong package và bên ngoài package nhưng phải kế thừa.
Protected access modifier có thể được áp dụng cho biến, phương thức, constructor. Nó không thể áp dụng cho lớp.
Ví dụ về protected access modifier trong Java:
// Tại package anhtester.demo
package anhtester.demo;
public class A {
protected void msg() {
System.out.println("Hello");
}
}
// Tại package anhtester.mypack
package anhtester.mypack;
import anhtester.demo.*;
public class B extends A {
public static void main(String args[]) {
B obj = new B();
obj.msg();
}
}
Kết quả:
Hello
4. Phạm vi truy cập public
Public access modifier được truy cập ở mọi nơi.
Ví dụ về public access modifier trong java:
// Tại package anhtester.demo
package anhtester.demo;
public class A {
protected void msg() {
System.out.println("Hello");
}
}
Tạo class B ở package khác và không cần kế thừa
// Tại package anhtester.mypack
package anhtester.mypack;
import anhtester.demo.*;
public class B {
public static void main(String args[]) {
B obj = new B();
obj.msg();
}
}
Kết quả:
Hello
Như vậy chúng ta đã tìm hiểu các loại phạm vi truy cập trong lập trình hướng đối tượng
✅ Tính kế thừa trong Java
Phần này chúng ta sẽ tìm hiểu những vấn đề sau:
- Khái niệm kế thừa
- Khai báo và sử dụng kế thừa
- Chú ý về kế thừa
Khái niệm kế thừa
Kế thừa có nghĩa là thừa hưởng lại, ví dụ như tài sản của ba mẹ sẽ được giao lại cho con cái.
Kế thừa trong lập trình (Inheritance) có nghĩa là một lớp sẽ thừa hưởng lại những thuộc tính, phương thức từ lớp khác.
Việc sử dụng kế thừa nhằm tái sử dụng code đã viết trước đó, thuận tiện trong việc bảo trì và nâng cấp chương trình.
Khai báo và sử dụng kế thừa
Cú pháp:
class <tên lớp con> extends <tên lớp cha> {
}
Ví dụ: ta có 2 lớp Person và Student như sau
Class Person:
public class Person {
public String name;
public int age;
public float height;
public Person(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public void getInfo() {
System.out.println("Name:" + this.name);
System.out.println("Age:" + this.age);
System.out.println("Height:" + this.height);
}
}
Class Student:
public class Student {
public String name;
public int age;
public float height;
public Student(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public void getInfo() {
System.out.println("Name:" + this.name);
System.out.println("Age:" + this.age);
System.out.println("Height:" + this.height);
}
}
Ta thấy 2 lớp Person và Student có chung thuộc tính và phương thức. Ta sẽ giữ nguyên code ở lớp Person lại
Bây giờ mình chỉ cần cho lớp Student kế thừa Person như sau:
public class Student extends Person{
public Student(String name, int age, float height) {
super(name, age, height);
}
}
Trong phương thức khởi tạo Student, ta sẽ dùng từ khóa super để cho lớp con truy cập các những thứ liên quan đến lớp cha. Như ví dụ trên thì ta dùng super(name, age, height) để gọi phương thức khởi tạo lớp cha. Chổ này BẮT BUỘC phải như vậy vì trong Java quy định.
Cái tên name, age, height trong super không cần phải giống tên với lớp Cha mà nó chỉ cần mapping kiểu dữ liệu với nhau là được.
Tiếp theo, ta thử khởi tạo đối tượng cho lớp Student và gọi phương thức getInfo() ở main để chạy:
public class Student extends Person {
public Student(String name, int age, float height) {
super(name, age, height);
}
public static void main(String[] args) {
Student student = new Student("Nhung", 40, 160);
student.getInfo();
}
}
Theo kết quả, đối tượng student của lớp Student sử dụng được phương thức getInfo() từ lớp cha Person:
Name:Nhung
Age:40
Height:160.0
Chú ý về kế thừa
Slogan đặc trưng kế thừa: “Cha có thì con có, con có chưa chắc cha đã có”
Tính chất kế thừa các ngôn ngữ lập trình hướng đối tượng đa số đều tương đồng với nhau về tính chất. Có thể các bạn không nhớ khái niệm và cú pháp, nhưng chỉ cần hiểu câu nói trên là bạn đã hiểu về kế thừa.
Ví dụ: Như ví dụ trước thì lớp Student kế thừa Person, ngoài những thuộc tính kế thừa ra, ta muốn thêm thuộc tính universityName cho Student
public class Student extends Person {
public String universityName;
public Student(String name, int age, float height, String universityName) {
super(name, age, height);
this.universityName = universityName;
}
}
Như vậy theo đúng tính chất: lớp cha Person có name, age, height thì lớp con Student có. Lớp con Student có universityName thì lớp cha Person không có.
Tận dụng từ khóa super để bảo trì và nâng cấp code
Từ khóa super mục đích chính truy cập những phương thức của lớp cha. Trong việc phát triển phần mềm, ta cần nâng cấp chương trình. Việc tận dụng từ khóa super sẽ giúp ta vừa tận dụng những dòng code trước đó và viết tiếp code mới.
Ví dụ: ta thấy phương thức getInfo() ở Person chỉ trả về thông tin name, age, height. Bây giờ, ta sẽ nâng cấp phương thức có thể trả về thông tin universityName ở lớp Student bằng cách tạo hàm tên trùng với lớp cha và dùng từ khoá super để gọi lại hàm từ lớp cha. Xong bổ sung thêm code ở hàm của lớp con.
(Tính chất này là kêu bằng tính Ghi đè hay Đa hình trong OOP. Mình học ở phần dưới kĩ hơn.)public class Student extends Person {
public String universityName;
public Student(String name, int age, float height, String universityName) {
super(name, age, height);
this.universityName = universityName;
}
public void getInfo() {
super.getInfo();
System.out.println("University Name:" + this.universityName);
}
}
Ta truyền thêm tham số thứ 4 vào lại chổ khởi tạo để kiểm tra:
public class Student extends Person {
public String universityName;
public Student(String name, int age, float height, String universityName) {
super(name, age, height);
this.universityName = universityName;
}
public void getInfo() {
super.getInfo();
System.out.println("University Name:" + this.universityName);
}
public static void main(String[] args) {
Student student = new Student("Nhung", 40, 160, "Dai hoc Dong Thap");
student.getInfo();
}
}
Kết quả:
Name:Nhung
Age:40
Height:160.0
University Name:Dai hoc Dong Thap
Như vậy chúng ta đã tìm hiểu kế thừa trong lập trình hướng đối tượng.
✅ Tính đóng gói trong Java
Tính đóng gói trong java là kỹ thuật ẩn giấu thông tin không liên quan và chỉ cho phép hiển thị ra những thông liên quan hoặc cần thiết. Mục đích chính của đóng gói trong Java là giảm thiểu mức độ phức tạp code trong phát triển phần mềm.
Chúng ta có thể tạo một lớp được đóng gói hoàn toàn trong Java bằng việc tạo tất cả thành viên dữ liệu của lớp là private. Tiếp theo, chúng ta sử dụng phương thức setter và getter để thiết lập và lấy dữ liệu trong nó. Đặt các phương thức có phạm vi truy cập là public hoặc protected
Đóng gói cũng được sử dụng để bảo vệ trạng thái bên trong của một đối tượng. Bởi việc ẩn giấu các Biến biểu diễn trạng thái của đối tượng. Việc chỉnh sửa đối tượng được thực hiện xác nhận thông qua các phương thức.
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 hoặc duplicated có thể có trong một ứng dụng.
Tính đóng gói là kỹ thuật tạo một trường (thuộc tính, biến) của một lớp là private và cung cấp khả năng truy cập trường này qua các phương thức public. Nếu một trường được khai báo là private, nó không thể được truy cập bởi bên ngoài lớp, do đó có thể che dấu các trường có lớp này. Vì lý do này, tính đóng gói được ám chỉ như việc dấu dữ liệu (data hiding).
🔆 Để đạt được đóng gói trong Java chúng ta cần:
- Khai báo các biến của một lớp là private.
- Cung cấp phương thức setter và getter là public để có thể sửa đổi và xem các giá trị biến.
Ví dụ về đóng gói trong java
Hãy xem ví dụ sau về đóng gói trong java với một lớp chỉ có một trường và các phướng thức setter và getter của nó.
Class: Student.java
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Các phương thức public setXXX() và getXXX() là các điểm truy cập đến các biến của lớp Student. Thông thường, các phương thức này được gọi là getters và setters. Vì vậy, bất kỳ đối tượng nào nào muốn truy cập vào các biến private sẽ truy cập chúng thông qua các trình getters và setters này.
Class: LearnTinhDongGoi.java
public class LearnTinhDongGoi {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s = new Student();
s.setName("Anh Tester");
System.out.println("Tính đóng gói trong Java: \n"+s.getName());
}
}
Kết quả:
Tính đóng gói trong Java:
Anh Tester
🔆 Lưu ý Quy ước đặt tên:
Quy ước đặt tên biến trong Java: từ đầu tiên là viết thường, từ thứ hai trở đi viết hoa chữ cái đầu tiên ở mỗi từ. Xem thêm ở bài viết Tiêu chuẩn coding trong Java (Coding Standards).
Quy ước đặt tên phương thức getter và setter như sau:
- Getter: bắt đầu bằng chữ get + viết hoa chữ đầu tiên tất cả các từ (viết hoa chữ đầu tiên của tên biến).
- Setter: bắt đầu bằng chữ set + viết hoa chữ đầu tiên tất cả các từ (viết hoa chữ đầu tiên của tên biến).
VD: getName, getAge, getAddress,...setName, setAge, setAttribute, setText,...
🔆 Lợi ích của đóng gói trong Java
- Tất cả các trường (field) của lớp có có chế độ chỉ đọc (read-only) hoặc chỉ ghi (write-only), tức là chỉ có hàm getter hoặc setter.
- Một lớp có thể có toàn bộ điều khiển thông qua những gì được lưu giữ trong các trường (field) của nó.
- Người sử dụng của class không biết cách các class lưu trữ dữ liệu. Một class có thể thay đổi kiểu dữ liệu của một trường và người dùng class không cần sự thay đổi trong code.
- Bạn có thể kiểm soát đối với dữ liệu. Giả sử bạn muốn đặt giá trị của id chỉ lớn hơn 100 bạn có thể viết logic bên trong lớp setter.
✅ Tính đa hình trong Java
Tính đa hình (polymorphism) là một trong bốn tính chất cơ bản của lập trình hướng đối tượng trong Java.
Tính đa hình là khả năng một đối tượng có thể thực hiện một tác vụ theo nhiều cách khác nhau.
Đối với tính chất này, nó được thể hiện rõ nhất qua việc gọi phương thức của đối tượng. Các phương thức hoàn toàn có thể giống nhau, nhưng việc xử lý luồng có thể khác nhau. Nói cách khác: Tính đa hình cung cấp khả năng cho phép người lập trình gọi trước một phương thức của đối tượng, tuy chưa xác định đối tượng có phương thức muốn gọi hay không. Đến khi thực hiện (run-time), chương trình mới xác định được đối tượng và gọi phương thức tương ứng của đối tượng đó. Kết nối trễ giúp chương trình được uyển chuyển hơn, chỉ yêu cầu đối tượng cung cấp đúng phương thức cần thiết là đủ.
Trong Java, chúng ta sử dụng nạp chồng phương thức (method overloading) và ghi đè phương thức (method overriding) để có tính đa hình.
- Nạp chồng (Overloading): Đây là khả năng cho phép một lớp có nhiều thuộc tính, phương thức cùng tên nhưng với các tham số khác nhau về loại cũng như về số lượng. Khi được gọi, dựa vào tham số truyền vào, phương thức tương ứng sẽ được thực hiện.
- Ghi đè (Overriding): là hai phương thức cùng tên, cùng tham số, cùng kiểu trả về nhưng thằng con viết lại và dùng theo cách của nó, và xuất hiện ở lớp cha và tiếp tục xuất hiện ở lớp con. Khi dùng override, lúc thực thi, nếu lớp Con không có phương thức riêng, phương thức của lớp Cha sẽ được gọi, ngược lại nếu có, phương thức của lớp Con được gọi.
Đa hình lúc runtime trong java
Đa hình lúc runtime là quá trình gọi phương thức đã được ghi đè trong thời gian thực thi chương trình. Trong quá trình này, một phương thức được ghi đè được gọi thông qua biến tham chiếu của một lớp cha.
Trước khi tìm hiểu về đa hình tại runtime, chúng ta cùng tìm hiểu về Upcasting.
Upcasting là gì?
Khi biến tham chiếu của lớp cha tham chiếu tới đối tượng của lớp con, thì đó là Upcasting. Ví dụ:
class A{}
class B extends A{}
A a=new B();//upcasting
🔆 Ví dụ về đa hình lúc runtime trong java
Ví dụ 1:
Chúng ta tạo hai lớp Bike và Splendar. Lớp Splendar kế thừa lớp Bike và ghi đè phương thức run() của nó. Chúng ta gọi phương thức run bởi biến tham chiếu của lớp cha. Khi nó tham chiếu tới đối tượng của lớp con và phương thức lớp con ghi đè phương thức của lớp cha, phương thức lớp con được gọi lúc runtime.
Khi việc gọi phương thức được quyết định bởi JVM chứ không phải Compiler, vì thế đó là đa hình lúc runtime.
class Bike {
void run() {
System.out.println("Chạy");
}
}
public class motorbike extends Bike {
void run() {
System.out.println("Chạy khoảng 40km/giờ");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Bike bk = new motorbike();
System.out.println("Ví dụ về tính đa hình:");
bk.run();
}
}
Kết quả:
Ví dụ về tính đa hình:
Chạy khoảng 40km/giờ
Ví dụ 2: Bank (ngân hàng)
Giả sử Bank là một lớp cung cấp chức năng xem thông tin tỷ lệ lãi suất. Nhưng mỗi ngân hàng có một lãi xuất khác nhau, ví dụ các ngân hàng SBI, ICICI và AXIS có tỷ lệ lãi suất lần lượt là 8%, 7% và 9%.
class Bank {
int getRateOfInterest() {
return 0;
}
}
class Vietcombank extends Bank {
int getRateOfInterest() {
return 8;
}
}
class BIDV extends Bank {
int getRateOfInterest() {
return 7;
}
}
class MB extends Bank {
int getRateOfInterest() {
return 9;
}
}
public class tinhdahinh02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Bank b;
b = new Vietcombank();
System.out.println("Ví dụ về tính đa hình:");
System.out.println("Vietcombank Rate of Interest: " + b.getRateOfInterest());
b = new BIDV();
System.out.println("BIDV Rate of Interest: " + b.getRateOfInterest());
b = new MB();
System.out.println("MB Rate of Interest: " + b.getRateOfInterest());
}
}
Kết quả:
Vietcombank Rate of Interest: 8
BIDV Rate of Interest: 7
MB Rate of Interest: 9
Ví dụ 3: Shape:
class Shape {
void draw() {
System.out.println("Drawing...");
}
}
class Rectangle extends Shape {
void draw() {
System.out.println("Drawing rectangle...");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing circle...");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Drawing triangle...");
}
}
public class tinhdahinh03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Shape s;
System.out.println("Ví dụ về tính đa hình:");
s = new Rectangle();
s.draw();
s = new Circle();
s.draw();
s = new Triangle();
s.draw();
}
}
Kết quả:
Ví dụ về tính đa hình:
Drawing rectangle...
Drawing circle...
Drawing triangle...
Đa hình lúc runtime trong Java với thành viên dữ liệu
Phương thức bị ghi đè không là thành viên dữ liệu, vì thế đa hình tại runtime không thể có được bởi thành viên dữ liệu. Trong ví dụ sau đây, cả hai lớp có một thành viên dữ liệu là speedlimit, chúng ta truy cập thành viên dữ liệu bởi biến tham chiếu của lớp cha mà tham chiếu tới đối tượng lớp con. Khi chúng ta truy cập thành viên dữ liệu mà không bị ghi đè, thì nó sẽ luôn luôn truy cập thành viên dữ liệu của lớp cha.
Quy tắc: Đa hình lúc runtime không thể xảy ra với thành viên dữ liệu.
class Bike {
int speedlimit = 90;
}
public class Supper_Cup_50 extends Bike {
int speedlimit = 150;
public static void main(String[] args) {
System.out.println("Vi du ve tinh da hinh:");
Bike obj = new Supper_Cup_50();
System.out.println(obj.speedlimit);// 90
}
}
Kết quả:
Vi du ve tinh da hinh:
90
Đa hình lúc runtime trong Java với kế thừa nhiều tầng
Ví dụ 1:
class Animal {
void eat() {
System.out.println("Eating");
}
}
class Dog extends Animal {
void eat() {
System.out.println("Eating fruits");
}
}
public class tinhdahinh05 extends Dog {
void eat() {
System.out.println("Drinking milk");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Animal a1, a2, a3;
System.out.println("Ví dụ về tính đa hình:");
a1 = new Animal();
a2 = new Dog();
a3 = new tinhdahinh05();
a1.eat();
a2.eat();
a3.eat();
}
}
Kết quả:
Ví dụ về tính đa hình:
Eating
Eating fruits
Drinking milk
Ví dụ 2:
class Animal {
void eat() {
System.out.println("Animal is eating...");
}
}
class Dog extends Animal {
void eat() {
System.out.println("Dog is eating...");
}
}
public class tinhdahinh06 extends Dog {
public static void main(String args[]) {
// TODO Auto-generated method stub
System.out.println("Ví dụ về tính đa hình:");
Animal a = new tinhdahinh06();
a.eat();
}
}
Kết quả:
Ví dụ về tính đa hình:
Dog is eating...
Vì BabyDog1 không ghi đè phương thức eat(), nên phương thức eat() của lớp Dog được gọi.
🔆 Nạp chồng phương thức trong Java
Nạp chồng phương thức (method overloading)
Nạp chồng phương thức trong java xảy ra nếu một lớp có nhiều phương thức có tên giống nhau nhưng khác nhau về kiểu dữ liệu hoặc số lượng các tham số.
Giả sử bạn phải thực hiện tính tổng của các số đã cho với bất kỳ số lượng các đối số, nếu bạn viết phương thức a(int, int) cho 2 tham số, b(int, int, int) cho 3 tham số điều này có thể gây khó hiểu cho các lập trình viên khác về ý nghĩa của các phương thức đó vì chúng có tên khác nhau.
Lợi ích của nạp chồng phương thức
Sử dụng nạp chồng phương thức giúp tăng khả năng đọc hiểu chương trình.
Các cách nạp chồng phương thức
Có 2 cách nạp chồng phương thức trong java
- Thay đổi số lượng các tham số
- Thay đổi kiểu dữ liệu của các tham số
Trong java, không thể nạp chồng phương thức bằng cách chỉ thay đổi kiểu trả về của phương thức.
🔆 1. Nạp chồng phương thức: thay đổi số lượng các tham số
Trong ví dụ này, chúng ta cần tạo 2 phương thức, phương thức add() đầu tiên thực hiện việc tính tổng của 2 số, phương thức thứ hai thực hiện việc tính tổng của 3 số. Sử dụng phương thức static để gọi hàm thông qua tên class thay vì phải tạo thể hiên của lớp.
class Adder {
static int add(int a, int b) {
return a + b;
}
static int add(int a, int b, int c) {
return a + b + c;
}
}
public class Example_napchong_01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Ví dụ về nạp chồng:");
System.out.println(Adder.add(11, 11));
System.out.println(Adder.add(11, 11, 11));
}
}
Kết quả:
Ví dụ về nạp chồng:
22
33
🔆 2. Nạp chồng phương thức: thay đổi kiểu dữ liệu của các tham số
Trong ví dụ này, chúng ta sẽ tạo ra 2 phương thức có kiểu dữ liệu khác nhau. Phương thức add() đầu tiên nhận 2 đổi số có kiểu giá trị là integer, phương thức thứ hai nhận 2 đổi số có kiểu giá trị là double.
class Adder {
static int add(int a, int b) {
return a + b;
}
static double add(double a, double b) {
return a + b;
}
}
public class Example_napchong_02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Ví dụ về nạp chồng:");
System.out.println(Adder.add(11, 11));
System.out.println(Adder.add(12.3, 12.6));
}
}
Kết quả:
Ví dụ về nạp chồng:
22
24.9
Các câu hỏi về nạp chồng phương thức trong java
Câu hỏi 1: Tại sao không thể nạp chồng phương thức bằng cách chỉ thay đổi kiểu trả về của phương thức?
Trong java, không thể nạp chồng phương thức bằng cách chỉ thay đổi kiểu trả về của phương thức bởi vì không biết phương thức nào sẽ được gọi.
Ví dụ:
class Adder {
static int add(int a, int b) {
return a + b;
}
static double add(int a, int b) {
return a + b;
}
}
public class Example_napchong_03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Adder.add(11, 11));
}
}
Kết quả:
Compile Time Error: method add(int,int) is already defined in class Adder
Câu hỏi 2: Có thể nạp chồng phương thức main() không?
Có, bạn có thể nạp chồng n phương thức main. Nhưng JVM chỉ gọi phương thức main() có tham số truyền vào là một mảng String. Ví dụ:
public class Example_napchong_04 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("main with String[]");
}
public static void main(String args) {
System.out.println("main with String");
}
public static void main() {
System.out.println("main without args");
}
}
Kết quả:
main with String[]
🔆 Nạp chồng phương thức và sự thay đổi kiểu giá trị
Kiểu dữ liệu của đối số truyền vào được thay đổi sang kiểu dữ liệu khác (tự động ép kiểu) nếu giá trị của đối số đó không phù hợp với kiểu dữ liệu của tham số đã được đinh nghĩa. Để hiểu khái niệm này hãy xem ảnh sau:
Kiểu byte có thể được ép sang các kiểu short, int, long, float hoặc double. Kiểu dữ liệu short có thể được ép sang các kiểu int, long, float hoặc double. Kiểu dữ liệu char có thể được ép sang các kiểu int, long, float or double…
Ví dụ 1:
public class Example_napchong_05 {
void sum(int a, long b) {
System.out.println(a + b);
}
void sum(int a, int b, int c) {
System.out.println(a + b + c);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example_napchong_05 obj = new Example_napchong_05();
// kiểu integer tham số 2 sẽ được thay đổi thành kiểu long
obj.sum(20, 20);
obj.sum(20, 20, 20);
}
}
Kết quả:
40
60
Ví dụ 2: nếu không có kiểu đối số nào phù hợp, chuyển đổi kiểu sẽ không được thực hiện.
public class Example_napchong_06 {
void sum(int a, int b) {
System.out.println("int arg method invoked");
}
void sum(long a, long b) {
System.out.println("long arg method invoked");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example_napchong_06 obj = new Example_napchong_06();
// phương thứ sum() có đối số int sẽ được gọi
obj.sum(20, 20);
}
}
Kết quả:
int arg method invoked
Ví dụ 3: không có kiểu đối số nào phụ hợp trong phương thức và mỗi phương thức thay đổi số đối số tương tự nhau. Th này sẽ không xác định được phương thức nào được gọi.
public class Example_napchong_07 {
void sum(int a, long b) {
System.out.println("a method invoked");
}
void sum(long a, int b) {
System.out.println("b method invoked");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example_napchong_07 obj = new Example_napchong_07();
// không xác định được phương thức nào được gọi
obj.sum(20, 20);
}
}
Kết quả:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method sum(int, long) is ambiguous for the type Example_napchon.
Một kiểu không được tự động ép sang kiểu bé hơn, ví dụ kiểu double không được ép sang bất kỳ kiểu nào khác.
🔆 Ghi đè phương thức trong java
Ghi đè phương thức (method overriding)
Ghi đè phương thức trong java xảy ra nếu lớp con có phương thức giống lớp cha.
Nói cách khác, nếu lớp con cung cấp sự cài đặt cụ thể cho phương thức đã được cung cấp bởi một lớp cha của nó được gọi là ghi đè phương thức (method overriding) trong java.
Sử dụng ghi đè phương thức trong java
- Ghi đè phương thức được sử dụng để cung cấp cài đặt đặc biệt của một phương thức mà đã được định nghĩa ở lớp cha.
- Ghi đè phương thức được sử dụng cho đa hình runtime.
Các nguyên tắc ghi đè phương thức trong java
- Phương thức phải có tên giống với lớp cha.
- Phương thức phải có tham số giống với lớp cha.
- Lớp con và lớp cha có mối quan hệ kế thừa.
Ví dụ ghi đè phương thức trong java
Trong ví dụ này, chúng ta định nghĩa phương thức run() trong lớp con giống như đã được định nghĩa trong lớp cha, nhưng được cài đặt rõ ràng trong lớp con. Tên và tham số của phương thức là giống nhau, 2 lớp cha và con có quan hệ kế thừa.
class Vehicle {
void run() {
System.out.println("Vehicle is running");
}
}
public class ExampleGhiDe01 extends Vehicle {
void run() {
System.out.println("Bike is running safely");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ExampleGhiDe01 obj = new ExampleGhiDe01();
obj.run();
}
}
Kết quả:
Bike is running safely
Ví dụ thực tế về ghi đè phương thức trong java
Giả sử Bank là một lớp cung cấp chức năng xem thông tin tỷ lệ lãi suất. Nhưng mỗi ngân hàng có một lãi xuất khác nhau, ví dụ các ngân hàng SBI, ICICI và AXIS có tỷ lệ lãi suất lần lượt là 8%, 7% và 9%.
Dưới đây là cài đặt cho ví dụ trên:
class Bank {
int getRateOfInterest() {
return 0;
}
}
class Vietcombank extends Bank {
int getRateOfInterest() {
return 8;
}
}
class BIDV extends Bank {
int getRateOfInterest() {
return 7;
}
}
class MB extends Bank {
int getRateOfInterest() {
return 9;
}
}
public class ExampleGhiDe02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vietcombank s = new Vietcombank();
BIDV i = new BIDV();
MB a = new MB();
System.out.println("Vietcombank Rate of Interest: " + s.getRateOfInterest());
System.out.println("BIDV Rate of Interest: " + i.getRateOfInterest());
System.out.println("MB Rate of Interest: " + a.getRateOfInterest());
}
}
Kết quả:
Vietcombank Rate of Interest: 8
BIDV Rate of Interest: 7
MB Rate of Interest: 9
Câu hỏi về ghi đè phương thức trong java
Có ghi đè được phương thức static không?
Không, phương thức static không thể ghi đè được, bằng chứng là đa hình runtime, vấn đề này sẽ được học trong bài sau.
Tại sao không ghi đè được phương thức static?
Vì phương thức static được ràng buộc với class còn phương thức instance được ràng buộc với đối tượng. Static thuộc về vùng nhớ class còn instance thuộc về vùng nhớ heap.
Có ghi đè phương thức main được không?
Không, vì main là phương thức static.
✅ Tính Trừu tượng trong Java
Trừu tượng trong OOP cho phép ẩn đi quá trình thực thi bên trong và chỉ hiển thị những tính năng thiết yếu tới người dùng.
Ví dụ: Bạn đang có tiền và bạn muốn kiếm một nhà thầu xây dựng để xây cho mình một căn nhà ưng ý. Có ba nhà thầu A, B, C họ đến và đưa cho bạn bản thiết kế, mức giá, thời gian hoàn thành, ngôi nhà bạn sẽ như thế nào v.v. Còn việc họ làm thế nào để xây được căn nhà cho bạn thì bạn không biết.
Trong java, tính trừu tượng được thể hiện thông qua abstract class và interface.
Áp dụng trừu tượng vào lập trình chúng ta sẽ có những ưu điểm:
- Một abstract class được khai báo bằng từ khoá abstract.
- Một abstract method là một method được khai báo không có thân hàm.
- Các class con thừa kế abstract class phải implement abstract method.
- Abstract class không thể khởi tạo .
- Đơn giản hoá đối tượng bằng cách đưa ra các thuộc tính và phương thức mà nó có và thực hiện.
- Tập trung vào tính cốt lỗi của đối tượng. Ví dụ khi bạn xem method của class bất kỳ trong java chúng đều có mô tả rõ ràng về cách sử dụng, giá trị trả về, tham số truyền vào etc là bạn có thể sử dụng được.
Abstract Class là gì ?
- Nó là một class
- Nó chứa phương thức trừu tượng
- Các class khi kế thừa một abstract class sẽ phải định nghĩa lại các phương thức trừu tượng của abstract class
- Một class chỉ có thể kế thừa 1 lớp trừu tượng
- Có 2 loại method là abstract method và method thường:
- abstract method là method trống không có thực thi.
- method thường là method có thực thi.
- không thể khởi tạo đối tượng của abstract class
- Không hỗ trợ đa kế thừa.
- Phương thức abstract của abstract class không được phép khai báo nội dung phương thức. Nó chỉ có thể định nghĩa tên cũng như các tham số đầu vào.
- Mức truy cập các hàm của abstract phải ở public hoặc protected để lớp kế thừa có thể định nghĩa lại và các thuộc tính của lớp abstract không được khai báo abstract. Ví dụ : khai báo 1 abstract class
abstract class BaseClass
{
// phương thức ở mức protected
abstract protected void hello();
// Phương thức ở mức public
abstract public void hi();
}
Ví dụ : Lớp kế thừa từ lớp abstract
abstract class Person
{
protected String ten;
protected String cmnd;
protected String namsinh;
abstract public void showInfo();
}
// class extends cần phải viết lại đầy đủ các hàm abstract
class SinhVien extends Person
{
public void showInfo(){
}
}
Interface là gì?
- Interface là một Template (khuôn mẫu), nó không phải là một lớp đối tượng.
- Để khai báo một Interface ta dùng từ khóa interface để thay cho từ khóa class.
- Tất cả các hàm trong interface đều ở dạng khai báo và không được định nghĩa (giống lớp abstract).
- Là cấu trúc trong OOP cho phép các class khác có thể implements.
- Một đối tượng implement một interface thì nó phải khai báo và định nghĩa tất cả các hàm trong Interface.
- Interface không thể khởi tạo.
- Phương thức trong Interface bắt buộc toàn bộ là các phương thức trừu tượng.
- Các phương thức trong Interface chỉ có thể được định nghĩa với khả năng là public và cũng không được định nghĩa nội dung.
- Interface có thể được extends với nhau.
- 1 class có thể implements nhiều Interface.
Ví dụ
interface MoveInterface
{
void run();
}
class Dog implements MoveInterface
{
public void run ()
{
System.out.print("Con chó chạy bằng 4 chân");
}
}
class Person implements MoveInterface
{
public void run ()
{
System.out.print("Loài người đi bằng 2 chân");
}
}
class Car implements MoveInterface
{
public void run ()
{
System.out.print("Xe hơi chạy bằng 4 bánh");
}
}
Sự khác nhau giữa interface và abstract class
- Tất cả phương thức trong interface luôn luôn(được hiểu) là abstract.
- Các phương thức trong abstract class là abstract hoặc không phải là abstract.
- Có thể implements nhiều interface nhưng không thể extends trực tiếp nhiều class.
- Các phương thức trong interface luôn luôn phải để là public, khác với abstract class có thể là public hoặc protected.
Sự giống nhau giữa interface và abstract class
- Không thể tạo một biến kiểu interface hoặc abstract class.
- Nếu là phương thức abstract thì phải được khai báo lại trong class con.
- Cả interface và abstract class đều có tính kế thừa.
Khi nào thì sử dụng interface, khi nào sử dụng abstract class
Khi một nhóm đối tượng có cùng bản chất kế thừa từ một class thì sử dụng abstract class.
Khi một nhóm đối tượng không có cùng bản chất nhưng chúng có hành động giống nhau thì sử dụng interface.
Abstract class và cách sử dụng
Đôi khi chúng ta cần một superclass để định nghĩa cấu trúc cho các class khác thừa kế và triển khai theo cách riêng của nó nhưng vẫn tuân thủ khuôn mẫu của supperclass đặt ra. Đôi lúc chúng ta cần một supperclass định nghĩa những method dùng chung cho tất cả các class con thừa kế nó.
Abstract class sẽ kết hợp 2 điểm trên mà cung cấp cho chúng ta các kỹ thuật để vừa có thể định nghĩa khuôn mẫu, vừa có các method dùng chung cho các class con.
Ví dụ Chúng ta có superclass Shape, class Circle, Rectangle thừa kế Shape. Chúng ta có abstract method area(). Circle, Rectangle thừa kế từ Shape thì phải implement method area() riêng cho mình. method getColor() sẽ được Shape định nghĩa và cho các class con dùng chung.
// File Shape.java
public abstract class Shape {
String color;
Shape(String color) {
this.color = color;
}
abstract double area();
String getColor() {
return this.color;
}
}
//File Rectangle.java
public class Rectangle extends Shape{
double length;
double width;
Rectangle(String color,double length,double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
double area() {
return length*width;
}
}
//File class Circle.java
public class Circle extends Shape{
double radius;
Circle(String color,double radius) {
super(color);
this.radius = radius;
}
@Override
double area() {
return Math.PI * Math.pow(radius, 2);
}
}
//File class Main chứa hàm Main chạy:
public class Main {
public static void main(String[] args) {
Shape circle = new Circle("red", 12d);
Shape rectangle = new Rectangle("blue", 4, 3);
System.out.println("Color circle: " + circle.getColor());
System.out.println("Area circle: " + circle.area());
System.out.println("Rectangle circle: " + rectangle.getColor());
System.out.println("Area rectangle: " + rectangle.area());
}
}
Output:Color circle: red Area circle: 452.3893421169302 Rectangle circle: blue Area rectangle: 12.0
Note:
- area() là abstract method cho nên các class con bắt buộc phải implement lại area(), nếu không chúng ta sẽ bị lỗi biên dịch.
- area() đóng vai trò là một khuôn mẫu cho các class con. Các class con dựa vào khuôn mẫu đó mà triển khai riêng cho nó.
- getColor() được xem như là một method sử dụng chung cho tất cả các class con.
Ưu điểm tính trừu tượng
- Tránh duplicate code và tăng tính tái sử dụng code.
- Tăng tính minh bạch nhờ vào việc áp dụng trừu tượng để tạo khuôn mẫu chung cho các đối tượng. Nhờ vào đó chúng ta có thể biết được luồng chạy của các object cùng loại(extends từ một superclass) mà không cần đi đến chi tiết.
TÍNH TRỪU TƯỢNG TRONG JAVA OOP CHÚNG TA CHỈ DÙNG TRONG AUTOMATION TEST KHI BUILD FRAMEWORK NÊN CÁC BẠN KHÔNG CẦN QUAN TÂM SÂU PHẦN NÀY. SAU NÀY CHÚNG TA CẦN MỚI NGHIÊN CỨU LẠI SẼ DỄ HIỂU VÀ ÁP DỤNG HƠN.