{%hackmd @themes/dracula %}
# Lý thuyết lập trình hướng đối tượng
## Non-static và static
Để dễ hiểu thì chúng ta đi luôn vào ví dụ. Ở đây, mình có một class Cat
```java
class Cat{
String name;
int age;
public void Meow(){
System.out.println("Meow Meoow!");
}
}
```
Các biến (field) và hàm (method) ở trên thì đều là non-static vì không có từ khóa static nào xuất hiện ở đây.
Các field và các method này chỉ có thể sử dụng khi chúng ta khởi tạo một đối tượng cụ thể cho **class Cat**
Mình lại có thêm một main class ở đây
```java
public class Main{
public void static main(String[] args){
Cat Tom = new Cat();
Tom.Meow();
Tom.name = "Tom";
Tom.age = 17;
}
}
// Kết quả: Meo Meoow!
```
Có thể thấy mình đã khởi tạo một đối tượng cụ thể ở đây là **Tom** với **Cat Tom = new Cat();** và có thể sử dung được các field và method của class Cat.
Nhưng sẽ thế nào nếu mình không khởi tạo một đối tượng cụ thể mà mình sử dụng luôn như thế này?
```java
public class Main{
public void static main(String[] args){
Cat.Meow();
}
}
```

Và đây là lỗi mà compiler báo về. **Vì method Meow() và các field không phải là static nên chúng ta sẽ không thể sử dụng nếu không khởi tạo một đối tượng cụ thể**.
Class Cat giống như là một bản thiết kế để tạo ra các đối tượng chứ nó không phải là một đối tượng.
#### Vậy sẽ thế nào nếu chúng ta thêm từ khóa static vào trước các field và method?
Việc thêm static vào tất cả các field và method là không được khuyến cáo
Chỉ nên thêm static vào các field dùng chung giữa các đối tượng.
Ví dụ lúc này mình sẽ viết lại class Cat như sau:
```java
public class Cat{
private static int soLuongMeoDaKhoiTao = 0;
String name;
int age;
public void Meow(){
System.out.println("Meow Meoow!");
}
Cat(){
soLuongMeoDaKhoiTao++;
}
public static int getSoLuongMeo(){
return soLuongMeoDaKhoiTao;
}
}
```
Mỗi khi một đối tượng Cat() mới được khởi tạo thì biến này sẽ tăng;
Thì lúc này ở hàm main chúng ta hoàn toàn có thể sử dụng Cat để gọi đến field **soLuongMeoDaKhoiTao** vào method **getSoLuongMeo**
```java
public class Main{
public void static main(String[] args){
Cat Tom = new Cat();
System.out.print(Cat.getSoLuongMeo());
}
}
```

Nhưng có thêm một điều lưu ý nữa đó là:
**Trong method static thì chúng ta không thể sử dụng các field non-static**
Quay trở lại với ví dụ lúc nãy. Chũng ta không thể:
```java
public class Cat{
private static int soLuongMeoDaKhoiTao = 0;
String name;
int age;
public void Meow(){
System.out.println("Meow Meoow!");
}
Cat(){
soLuongMeoDaKhoiTao++;
}
public static int getSoLuongMeo(){
return soLuongMeoDaKhoiTao;
}
public static int getTuoiMeo(){
return age;
}
}
```

## Sự khác biệt giữa Abstract Class và Interface
### Abstract class là gì?
Abstract class là một class trừu tượng và chúng ta không thể khởi tạo đối tượng.
Hmm lại quay lại với ví dụ class Cat nào.
```java
class Cat{
String name;
int age;
public void Meow(){
System.out.println("Meow Meoow!");
}
}
```
Nếu không phải là một abstract class thì chúng ta có thể khởi tạo như thế này:
```java
public class Main{
public void static main(String[] args){
Cat Tom = new Cat();
}
}
```
Còn khi chúng ta thêm từ khóa abstract đằng trước:
```java
public abstract class Cat{
String name;
int age;
public void Meow(){
System.out.println("Meow Meoow!");
}
}
```

Thì không thể khởi tạo đối tượng. Thế không khởi tạo được thì tạo class làm gì? Rảnh à?
Hmm lại thêm một ví dụ khác nhé. Đó là mình có một class là **Animal**
```java
public class Animal{
int age;
String name;
public void makeNoise(){
System.out.print("Make some noiseee!");
};
}
```
Class này có chứa 2 thuộc tính chung của tất cả các loài vật là **age** và **name**, phương thức **makeNoise** nữa. Và mình cũng không muốn khởi tạo đối tượng animal.Mình sẽ dùng nó để làm class cha để **bất kỳ một class con nào kế thừa nó đều sẽ có** hai thuộc tính **age**,**name** và phương thức **makeNoise**
Vì không có ý định khởi tạo nên mình sẽ để nó là abstract class
```java
public abstract class Animal{
int age;
String name;
public void makeNoise(){
System.out.print("Make some noiseee!");
};
}
```

Hmm vậy thì cũng giống kế thừa bình thường mà đúng không. Oke chúng ta đi đến khái niệm tiếp theo là
#### Abstract method
Có lớp trừu tượng thì cũng có phương thức trừu tượng.
Phương thức trừu tượng là một phương thức chúng ta khai báo trong abstract class và có từ khóa abstract, thêm nữa là không cần thêm body cho abstract method.
Và điều đặc biệt là **tất các class con kế thừa abtract class có chứa abtrac method thì phải override abtract method.**
```java
public abstract class Animal{
int age;
String name;
public abtract void makeNoise();
}
```

Có thể thấy ngay lập tức báo lỗi nếu mình không override.
ô sờ kê. Tiếp tục nào. Khái niệm tiếp theo là **Interface**
### Interface là gì?
Nếu class là một bản thiết kế của một đối tượng thì Interface là bản thiết kế của một class.
Nó khá tương đồng với abstract class. Nhưng cũng có kha khá điểm khác.
Đầu tiên, **tất các các thuộc tính được khởi tạo trong interface đều là static và final** (const trong một số ngôn ngữ khác, không thay đổi).

Có thể thấy nếu không có giá trị sẽ ngay lập tức báo lỗi.


Mặc dù viết là **int age = 1;** nhưng khi khai báo trong Interface nó sẽ tự hiểu là **static final int age = 1**

Có thể thấy là không thể thay đổi giá trị
Đó chính là sự khác biệt của Interface và Abstract class. Abstract class chúng ta tạo ra thuộc tính để tất cả class con đều có thuộc tính đó mà không bị ràng buộc bởi một giá trị cụ thể.
Điểm khác biệt tiếp theo là:
**Tất cả các method được viết trong Interface sẽ được tự hiểu là Abstract method.**


Cuối cùng là
**Chúng ta chỉ có thể kế thừa (extend) một class nhưng có thể implements (áp dụng) vô hạn số Interface.**
## Upcasting và Downcasting
### Upcasting là gì?
Upcasting là khi chúng ta **ép kiểu của một đối tượng thành kiểu class cha của nó**.
Downcasting là khi chúng ta **ép kiểu của một đối tượng thành kiểu class con của nó**.
Ví dụ có một class cha là **Animal**
```java=
public class Animal {
String name;
public void makeNoise(){
System.out.println("I'm just a animal");
}
}
```
Và một class con kế thừa nó