1. 程式人生 > 其它 >Java面向物件程式設計(淺)

Java面向物件程式設計(淺)

# java面向物件程式設計
## 面向物件的基本特徵
面向物件的基本特徵包括:抽象、**封裝、繼承、多型**,這三大特徵後面細寫
抽象(個人理解):將一個具體的事物,根據他的特徵和行為,將它抽離成一個不具體的一種概念,一種很大的東西,一種分類,就好比人 根據把它的特徵抽離成哺乳動物,一說哺乳動物你並不知道這是一個什麼東西,它具體是誰,是哪種動物,你可以通過哺乳動物知道人符合條件,老鼠也符合條件,這裡麵人和老鼠其實都是哺乳動物的一種具體例項。
沒寫太清除,看看再描述一遍老師寫的筆記:**讓事物脫離具體化,只將它的型別、屬性和行為抽離出來形成一個類**,例如針對貓、狗、牛、羊,根據它們的共同特徵和共同行為,可以把它們抽象出一個動物類,這個從現實中的物體到程式的類設計就是一個抽象的過程,動物類本身就是泛指,它不指具體的某一種動物,這就是脫離具體化的體現。

1. **建構函式**
用途:在**例項化的時候呼叫**,常常用來初始化物件,給物件的屬性賦初始值
書寫規則:**訪問修飾符 類名([形參])**{
//內容
}
類裡面其實有一個**預設的無參建構函式**,當你沒寫建構函式的時候會預設使用它!但是當你自己寫了建構函式時,這個建構函式就會消失。所以當你寫了有參建構函式的時候,在例項化時就要傳入引數(因為沒有無參的建構函式)這是你要是不需要傳入引數的話,你可以加上無參的建構函式,這裡就涉及到了**方法的過載**
**過載:在同類中不同的方法使用相同的方法名(同類中一種多型性的表現)(<font color = orange >同名,不同參</font>:包括順序不一樣,數量不一樣,型別不一樣,<font color = orange>返回值可以不同,訪問修飾符也可以不一樣</font>)**

2. **static(靜態的)關鍵字**: 當你使用static修飾方法或者屬性時,你可以把該方法或者屬性理解為類的專屬的方法和屬性,它不再是某個具體的物件的方法和屬性,它是所有物件所共有的,因為它是"類的屬性和方法",既然如此,那你也可以不用例項化物件,直接使用類名來使用static修飾的屬性或者方法

<font color = red><strong>注意事項:</strong></font>

1. **靜態變數只能寫在方法外面**,不能寫在方法體中
2. **靜態方法中不能使用this呼叫其他的非靜態方法或者非靜態屬性**。因為就像我剛才所說,靜態方法的是屬於類中的,而非物件,不用例項化就可以使用,但是this指向當前物件。。。。。不知道怎麼說,反正就是this指向的問題。

3. static修飾的程式碼塊:

static{
內容
}

寫在方法外邊的程式碼塊,在**類載入時執行該程式碼塊且僅僅執行一次(比建構函式早,建構函式在例項化時才執行)**。

用途:常用來做**初始化操作**

4. 快捷鍵操作
1. Eclipse中的生成建構函式的快捷鍵:Shift+Alt+S鍵+O鍵
2. Idea中的快捷鍵:Alt+insert

## 封裝
概念:隱藏內部實現細節,對外提供介面
1. 意義:
1. 避免非法資料(可以對setter方法進行流程控制)使資料更加安全。
2. 增加程式碼的重用性
2. 封裝是如何實現的:
1. 改變修飾符,所以修飾符的種類有哪些呢(將public改為`private` 或者 `protected`)
- public(公有的)
- private(私有的)
- protected(受保護的)
- friendly(預設)
封裝的原理就涉及到了一個訪問許可權的問題
訪問許可權:`public`>`protected`>`friendly`>`private`

| 訪問範圍 | public | protected | private | friendly |
| :----: | :----: | :----: | :----: | :----: |
| 同一個類 | √ | √ | √ | √ |
| 同一包中的其他類 | √ | √ | × | √ |
| 不同包中的子類 | √ | √ | × | × |
| 不同包中的非子類 | √ | x | × | × |
2. 設定get/set方法(根據需求 對它加入條件判讀)(這個get&set方法就是對外提供的介面)

## 繼承
### 一、繼承

1. 概念:繼承是類與類之間的關係,被繼承的那一方,可以使用基類的方法和屬性,Java中只能**單繼承**(即一個子類只能繼承自一個父類)**繼承要滿足 is-a的關係**,例如:熊貓是一隻哺乳動物;那麼熊貓不僅有哺乳動物的特性還有一些特有的屬性和行為;假如**有多個類,它們有很多類似的地方,那我們就可以將這些共性抽離成一個父類**,然後再讓他們繼承這個父類;

2. 語法,如下面程式碼所示,在**需要繼承的類後面加`extends`關鍵字再在關鍵字後面加入需要繼承的類名**
```java
package cn.Sukura.www;
class Animals{
public String name;
public Animals(String name) {
this.name = name;
}
public void sayHi() {
System.out.printf("My name is %s\n",this.name);
}
}
class Pandas extends Animals{
public Pandas(String name) {
//super()必須寫在第一行
super(name);
}
}
public class Test {
public static void main(String[] args) {
Pandas p = new Pandas("榮榮");
p.sayHi();
}
}
```

3. **哪些不能被繼承**

- **被`final`(最終的)修飾的類**
- **不能繼承父類的`構造方法`**
- **不能繼承被`private`修飾的方法或屬性**
- **不能繼承在不同包下的父類中使用預設修飾符`friendly`的修飾的屬性和方法**(上面那條和這條其實就是設計一個訪問許可權的問題)

4. super關鍵字

**`super()`呼叫父類的構造方法**,其實**和`this`的使用是一樣的**,只不過super是呼叫關於父類的一切,`super.Method()`呼叫父類方法,`super.attribute`呼叫父類屬性,**<font size = 5 color = red>在子類構造中呼叫super()時,super()必須寫在第一行,不然不能通過編譯</font>**

5. 例項化子類物件的執行過程:

父類的靜態成員,靜態程式碼塊 --> 子類的靜態成員,靜態程式碼塊 -->父類屬性 -->父類構造 -->子類屬性 -->子類構造 -->例項化完成

### 二、重寫

1. 概念:是指子類繼承自父類,寫一個和父類中方法名和引數相同的方法,子類中的該方法會覆蓋父類的方法。(發生在繼承當中)

2. 好處,可以子類根據不同的需要來實現不同的行為,父類只用來提供一個模板

3. 注意事項:

- 繼承是重寫的大前提,沒有繼承就沒有重寫

- 方法名和引數列表完全一致才叫做重寫

- 子類方法的訪問修飾符必須大於或者等於父類方法的訪問修飾符

- 子類方法的返回值必須與父類的返回值相關(返回基本資料型別時子類必須與父類完全一樣,返回引用資料型別時,子類的返回值型別與父類的返回值型別相同,或是父類的返回值型別的子類)

4. 註解:以@開頭的稱為註解,@Override是重寫的註解,表示該方法是重寫方法,如果該方法不滿足重寫規則,使用該註解時就會報錯。

### 三、抽象類

1. 什麼是抽象類:使用abstract修飾類是抽象類,語法如下:

```java
public abstract class 類名{
public void sayHi(){
//這是一個正經的方法
}
public abstract void sayLove();//這是一個抽象方法
}
```

2. 注意事項:
- 抽象類**不能被例項化**,它主要的目的就是為了給子類做模板的!
- `abstract`關鍵字**不能和`final`或`static`一起出現**(他們有衝突)
- 用`abstract`修飾的方法是抽象方法,它**沒有**方法體,**抽象方法必須被子類實現,除非子類也是一個抽象類**
- **抽象方法必須寫在抽象類中**,但抽象類不止有抽象方法

3. `abstract`關鍵字 和 `final`關鍵字的異同
- `abstract`可以修飾**方法和類 ** `final`可以修飾**類**,**方法**,**屬性**
- `abstract`修飾類時 該類**不能被例項化**,但是可以被子類繼承(設計出來就是用來繼承的)
- `final`修飾類時 該類為最終類,不能被繼承
- `abstract`修飾方法時 該方法**必須被子類實現**除非子類也是一個抽象類,該方法不能有方法體!
- `final`修飾方法時為最終方法,不能被重寫
- `final`修飾屬性時,表示常量,必須賦初始值,並且一旦賦值就不能在修改

## 多型

## 介面

1. 介面的概念:Java中的`介面`是一個全部由抽象方法組成的集合,使用`interface`關鍵字來宣告一個介面,接口裡面只能有抽象方法和常量,實際上,它就是**提供了一種規範,表示一種能力,增強了對於事物的擴充套件**

2. 對於**面向介面程式設計的理解**: 要儘可能的使程式碼更加簡介,複用性更高,我們就可以考慮,我們需要的是什麼,我們需要知道它是什麼嗎?還是隻需要知道它能做什麼。可能我沒說明白,還是寫一段程式碼看看吧

```java
//首先根據業務需要 老師要教小朋友說普通話
class Teacher{
public void teachToTalk(){
//方法
}
}
class Kids{
String name;
public void listenForClass{
//假裝裡面有程式碼
}
public void readBookLoudly{
//假裝裡面有程式碼
}
}
//這時業務很簡單,很容易就實現了
```

```java
//後來業務升級 老師的服務物件不僅僅只是小孩了 還有其他型別的學生 這時就會比較繁瑣了,這時我們可以把他們的共性抽離出來寫一個抽象類 也可以實現
class abstract Person{
String name;
public abstract void listenForClass();
public abstract void readBookLoudly();
}
class Kids extends Person{
public void listenForClass{
//假裝裡面有程式碼
}
public void readBookLoudly{
//假裝裡面有程式碼
}
}
class Adults extends Person{
public void listenForClass{
//假裝裡面有程式碼
}
public void readBookLoudly{
//假裝裡面有程式碼
}
}
class Teacher{
public void teachToTalk(){
//方法
}
}
```

```java
//現在你會發現只要服務物件是人都可以解決 業務繼續升級 這時老師又需要服務動物 但是動物的技能和前面的不匹配了 而且也不可能實現多種動物,這時你會想到你關注的到底是什麼,是他們是誰?還是他們能做什麼?
public interface ListenAndRead{
public void listen();
public void recite();
}
abstract class Person implements ListenAndRead{
}
class Child extends Person{//兒童
public void listen(){}//聽課
public void recite(){}//朗讀
}
class Student extends Person{
public void listen(){}//聽課
public void recite(){}//朗讀
}
class Foreigner extends Person{
public void listen(){}
public void recite(){}
}
class Parrot implements ListenAndRead{//鸚鵡
public void listen(){}
public void recite(){}
}
class Teacher{
public void teach(ListenAndReciteAble lar){}
}
```

**引用**: **要讓一個依賴方達到最大的相容性或擴充套件性,就要讓依賴的物件型別儘量寬泛**,當然(為什麼不用Object)**Object是最寬泛的,但是這就不明確了**,而且不能保證依賴方的需要。這是就要跳出一些定式,我需要什麼的是什麼?還是我需要的是他們會什麼?這就變成了面向介面程式設計。

3. 介面的語法

定義介面:

```java
訪問修飾符 interface關鍵字 介面的名字{

}
例如:
public interface Call{
void CallByWechat();
}
```

實現介面:

- 父類和子類的關係叫做繼承
- 介面與實現類的關係叫做實現
- 使用`implements`關鍵字來實現介面,可以實現很多介面(實現多繼承)

```java
class Phone implements Call,Play{
public void CallByWechat(){
//實現
}
}
```

4. 介面的特點

- **不能被例項化**
- 介面中的方法**都是抽象方法訪問修飾符預設為public 且必須是public**
- 介面可以有屬性,但他們**只能是公有的靜態常量(public static final)寫於不寫都如此**

- **實現類必須重寫父介面的所有方法**
- 介面可以繼承介面

5. 介面與抽象類的異同點
<table>
<tr>
<td align = "center">介面</td>
<td align = "center">抽象類</td>
<tr>
<tr>
<td colspan="2" align = "center">不能被例項化<br>可以包含抽象方法<br>可以包含靜態常量</td>
<tr>
<tr>
<td align = "center">使用interface關鍵字宣告</td>
<td align = "center">使用abstract修飾的類</td>
<tr>
<tr>
<td align = "center">沒有構造方法</td>
<td align = "center">有構造方法</td>
<tr>
<tr>
<td align = "center">不能包含非抽象方法,且所有方法必須是public的,只能包含public的靜態常量。</td>
<td align = "center">可以包含非抽象方法並且可以是非public的,可以包含非public的普通變數。</td>
<tr>
<tr>
<td align = "center">滿足 has-a的關係 表示某有某一種能力 表示什麼可以做什麼。</td>
<td align = "center">滿足 is-a的關係 比如哈士奇是一條狗 這裡就是繼承關係</td>
<tr>
</table>

## 異常

1. 概念: 程式在執行中出現預料之外的錯誤
2. 在Java中一般產生異常的情況有以下三種:
- Java內部發生錯誤產生異常,Java虛擬機器產生的異常
- 編寫程式碼錯誤產生的異常,如空指標異常,陣列越界等異常,這種異常稱為未檢查異常
- 通過throw語句手動丟擲的異常

3. 異常的型別

```mermaid
graph TD
A(Throwable)-->B1(Error錯誤)
A -->B2(Exception異常)
B2 -->C1(執行時異常即非檢查異常)
B2 -->C2(非執行時異常即檢查異常)

執行時異常:都是`RuntimeException 類`及其`子類`異常,例如**空指標異常、陣列越界異常等**,這些異常是**不檢查異常**,程式可以選擇捕獲處理,或者不處理,**這些異常一般都是程式的邏輯引起的**

檢查異常:指**執行時異常以外的型別**,在型別上都屬於Exception及其子類,**該類異常必須處理,如果不處理就無法編譯通過**,常見的有`IOException`、`ClassNotFoundException `等還有使用者自己丟擲的異常,如果你不處理的話肯定是會報紅的對不對

4. 常見的異常

| 異常型別 | 說明 |
|:-----:|:----:|
|`Exception`|異常類,它是所有異常類的頂級父類。|
|`RuntimeException`|執行時異常|
|`ArithmeticException`|除0異常,除數不能為0|
|`ArrayIndexOutOfBoundsException`|陣列下標越界|
|`NullPointerExceptio`|空指標異常|
|`ClassNotFoundException`|不能載入所需要的類|
|`ClassCastException`|物件強制型別轉換錯誤|

5. 處理的方式,如下程式碼演示

捕獲處理方式

```java
try{
//可能出現異常的程式碼塊
}catch(異常型別 物件名字){
//出現異常時處理方式
}catch(異常型別 物件名字){
//出現異常時處理方式
}finally{
//最後執行這個 無論如何都會執行 除非System.exit()
}
```

捕獲異常程式碼一般根據異常型別從**小到大捕獲** 因為**程式出現異常時只會進入第一個符合條件的程式碼塊中**

還可以**通過丟擲的方式處理異常**

```java
//將異常往外丟擲,不再用本類中的try-catch來處理,讓呼叫該方法的那一方來處理
public class Test {
//throws 往外丟擲異常 throw 人為製造一個異常
public void playing() throws Exception {//在方法名的小括號後面、大括號前面加上 throws 異常類名1,異常類名2
throw new Exception("作業沒寫完,還想玩?");
}
public static void main(String[] args) {
Test t = new Test();
try {
t.playing();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```

6. finally、finalize、final的區別
| finally | finalize |final的區別 |
|:-----:|:----:|:----:|
|修飾類時,此類不能被繼承,修飾方法時,該方法不能被重寫,修飾屬性時,屬性的值不能被更改而且一定要賦值 | `java`中的垃圾回收方法,可以清理記憶體未使用的物件該方法不需要呼叫,因為`java`會自動檢測並清理記憶體,簡稱為`GC` | 異常處理的一部分 表示一定執行 除非遇到上面講的那個語句 |

## 集合框架
1. 為什麼要使用集合框架?
陣列的弊端:**型別必須相同、長度是固定的**,如果一組資料在程式執行時並不能確定長度,那就不能再使用陣列了
相對於陣列,**集合框架不限型別,並且長度可變**,運用更加靈活。
<img src=".\集合框架.png" style="zoom:50%;" />

現學部分的重點是標紅部分

`Collection`和`Map`是`java.util`包下的兩個集合上級介面

- Collection:儲存單個物件的集合 特點:<font color = red>**無序**</font>(**有序、無序是指在進行插入操作時,插入位置的順序性
先插的位置在前,後插的位置在後,則為有序,反之無序**),**<font color = red>不唯一</font>**

如圖所示,它擁有三個子型別集合`List`、`Set` 和 `Queue`(重點是List)

- List :**有序,不唯一**

​ 常用的實現類:

1. `ArrayList` :以`陣列`的方式儲存,**查詢的效率高,插入和刪除的效率低**
2. `LinkedList`:以`連結串列`的方式,**插入和刪除的效率高,查詢的效率低**,和`ArrayList`相反

| List常用方法 | 功能 |
| :----------------------------: | :-----------------------: |
| add(obj) /add(int index,obj o) | 新增物件/在指定位置插入 |
| remove(obj (這是Collection提供的方法))/remove(int index)(這是List提供的方法) | 按物件刪除/按下標刪除 |
| get(int index) | 獲取物件 |
| size() | 獲取集合的長度 |
| contains() | 檢視集合中是否包含某物件 |

前三個方法`LinkedList`有擴充套件的方法如`addFirst()`,`addLast()`;(功能就不解釋了,顧名思義,其餘兩個以此類推)

- Set:**無序、唯一**,典型的實現類有:
`HashSet`:以雜湊表的形式儲存
`TreeSet`:以樹形表的形式儲存

- Map:儲存鍵(key,value)值對的集合 鍵唯一,值不唯一。

| Map常用方法 | 功能 |
| :----------------------------: | :-----------------------: |
| put(key,value) | 往Map中新增鍵值對 |
| remove(key) | 移除一對鍵值對 |
| get(key) | 根據鍵獲取值 |
| size() | 獲取集合的長度 |
| values() | 獲取值集,返回Collection物件,因為Collection是不唯一的,所以值不唯一 |
| keySet() | 獲取鍵集,返回Set物件,因為Set是唯一的,所以鍵唯一 |
| containsKey(key) | 判斷集合中是否有某鍵 |

Map典型的實現類
- **HashMap**: 允許插入空值,效率高,執行緒不安全
- **Hashtable** :不允許插入空值,效率低,但執行緒安全
- **TreeMap**

### 集合的遍歷

1. java中的foreach

```java
for(類名 物件名:集合){

}
//例如
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}

}
public class Test {
public static void main(String[] args) {

List l = new ArrayList();
l.add(new Student("Yuan",19));
l.add(new Student("He",19));
for(Object s:l) {
Student stu = (Student)s;
System.out.println(stu.name);
}
}
}
```

2. 使用iterator迭代器

- 如何獲得iterator迭代器?

通過Collection提供的方法Iterator\<E> Iterator()方法來得到一個迭代器

- Iterator迭代器有哪些方法

| 方法 | 功能 |
| :----------------------------: | :-----------------------: |
| boolean hasNext() | 判斷是否還有下一個元素,如果有返回true,沒有返回false |
| E next() | 獲得下一個元素 |

```java
List list = new ArrayList() {
{
this.add(new Student("張三",20));
this.add(new Student("李四",20));
}
};
Iterator iter= list.iterator();
while(iter.hasNext()) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
```

3. 使用for迴圈 + Iterator迭代器

```java
List list = new ArrayList() {
{
this.add(new Student("張三",20));
this.add(new Student("李四",20));
}
};
for(Iterator iter = list.iterator();iter.hasNext();) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
//另外 單純的用for迴圈也是可以遍歷的 因為List是有序的
```

### 泛型

1. 為什麼要用泛型:先注意集合的特點:**集合長度可變,並且可以放任意的<font color = red>引用資料型別</font>,儲存的資料型別也可以不一樣**假如我有一個這樣的需求,我需要獲取全班所有人的姓名,但有個別人搗亂,把名字寫成電話號碼,導致我後面的程式碼出錯,這種時候,就需要使用到泛型了,如果沒按照我的規定來存放資料就會報錯,等於是**將執行時期會發生的異常提前到編譯時期了**。所以可見泛型是一種**安全機制**,一種**規範**,它和介面一樣在制定規則。

2. **如何理解泛型?**大家上學的時候都寫過作文吧,考試的時候作文上面的要求經常是題材不限,但是一旦你確認題材了,你就不能再寫其他題材的作文了,泛型也是如此,本質上泛型是一個變數,它是需要我們賦值的。

```java
//例如
List<Student> names = new ArrayList<Student> names();
//這時我如果給他存放其他的資料型別就會出錯 而且我們在取元素的時候不需要強制型別轉換
```

還有泛型類泛型方法之類的沒太多時間去補充,到時候補充和完善。