Java 基礎08-OOP面向對象編程

面向對象編程 OOP

1. 入門即是不識

從咱們剛開始接觸到 Java 這門語言以後,除了所謂的增刪改查,就是會有人告訴咱們這是一個面向對象編程的語言,然而說道面向對象,聽到最多的就是"不就對象嘛,new 一個不就好了!",其實並不知道什麼是對象。

如今就趕忙回來看看什麼是 OOP 面向對象編程(Object Oriented Programming)。
複製代碼

2. 舉例說明什麼是面向對象

舉個最簡單點的例子來區分,面向過程和麪向對象。

有一天你想吃魚香肉絲了,怎麼辦呢?你有兩個選擇:
	1、本身買材料,肉,魚香肉絲調料,蒜薹,胡蘿蔔等等而後切菜切肉,開炒,盛到盤子裏。
	2、去飯店,張開嘴:老闆!來一份魚香肉絲!

看出來區別了嗎?在這裏 1 是面向過程,2 是面向對象。

面向對象有什麼優點呢?首先你不須要知道魚香肉絲是怎麼作的,下降了耦合性。若是你忽然不想吃魚香肉絲了,想吃烤羊排,對於 1 你可能不太容易了,還須要從新買菜,買調料什麼的。對於 2的話太容易了,大喊:老闆!那個魚香肉絲換成烤羊排吧!提升了可維護性。總的來講就是下降耦合,提升維護性!

面向過程是具體化的,流程化的,解決一個問題,你須要一步一步的分析,一步一步的實現。

面向對象是模型化的,你只需抽象出一個類,這是一個封閉的盒子,在這裏你擁有數據也擁有解決問題的方法。須要什麼功能直接使用就能夠了,沒必要去一步一步的實現,至於這個功能是如何實現的,管咱們什麼事?咱們會用就能夠了。

面向對象的底層其實仍是面向過程,把面向過程抽象成類,而後封裝,方便咱們咱們使用的就是面向對象了。
複製代碼

2.1 優缺點分析

上面舉例描述了什麼是面向過程和麪向對象,接下來咱們總結一下各自的特色。

對於面向過程:
	優勢:性能比面向對象好,由於調用時須要實例化,開銷比較大,比較消耗資源。
	缺點:不易維護、不易複用、不易擴展。
	
對於面向對象:
	優勢:易維護、易複用、易擴展,因爲面向對象有封裝、多態、繼承的特性,能夠設計出低耦合的系統,使系統更加靈活、更加易用維護。
	缺點:性能方面比面向過程差。
複製代碼

3. 面向對象的三大特徵

面向對象有三大特徵,分別是封裝、多態和繼承。接下來咱們來逐個來學習一下。

那麼首先咱們就要舉個例子,對象。

對象就是對事物的一種抽象描述。現實世界中的事物,均可以用"數據""能力"來描述。

好比我要描述一我的,"數據"就是他的年齡、性別、身高體重,"能力"就是他能作什麼工做,承擔什麼樣的責任。

描述一個外賣軟件,"數據"就是他包含的菜品,而"能力"就是他能夠點菜。
複製代碼

3.1 面向對象特徵-封裝

隱藏對象的屬性和實現細節,僅對外提供公共訪問方式,將變化隔離,便於使用,提升複用性和安全性。

咱們把"數據""能力"組合成一個對象的過程就叫作"封裝"。
    
封裝的結果就是能夠有一個類,經過這個類咱們能夠得到一個對象。而後咱們就能夠經過給這個對象下達指令,而後讓他執行本身的"能力"。

封裝只是面向對象的第一步,目的是把現實世界的東西抽象成對象。面向對象真正有威力的地方是"多態""繼承"。
    
封裝的核心思想是:隱藏實現,暴露接口。
    
封裝的目的:
    1、解耦
    2、保護數據安全
複製代碼
3.1.1 訪問修飾權限符
修飾符 權限範圍
public 本類,子類,本包,外部包
protected 本類,子類,本包
default 本類,子類
private 本類

3.2 面向對象特徵-多態

父類或接口定義的引用變量能夠指向子類或具體實現類的實例對象。提升了程序的拓展性。

同一操做,做用於不一樣的對象,能夠產生不一樣結果,這就是"多態"。一般說的多態是指運行期的多態,也叫動態綁定。

要實現多態,須要知足三個條件:
	1、有類繼承或接口實現;
	2、子類重寫父類的方法;
	3、父類的引用指向子類的對象。
	
好比:
	犬科動物 {
		吠();
	}
	
	狗 繼承 犬科動物 {
        吠(){
            汪汪汪!
        }
    }

	狼 繼承 犬科動物 {
        吠(){
            嗷嗷嗷!
        }
    }

狗和狼都是犬科動物,拉來一隻犬科動物,若是讓你來分辨的話,你可能沒辦法直接分辨出它究竟是狗仍是狼。只要它真正的叫出來的時候,你才知道。這就是運行是多態。

轉化成 Java 代碼以下:
	public class Canidae {
		
		public void bark(){};
	}
	
	public class Dog extends Canidae {
		
        public void bark() {
			
            System.out.println("汪汪汪...")
		}
	}
	
	public class Wolf extends Canidae {
		
        public void bark() {
			
            System.out.println("嗷嗷嗷...")
		}
	}
	
	public class Main() {
        
		Canidae canidae = new Dog();
		Canidae canidae1 = new Wolf();
        
         canidae.bark();
         canidae1.bark();
	}

這樣,就實現了多態,一樣的是 Canidae 的實例,canidae.bark() 調用的就是 Dog 類的方法,而 canidae1.bark() 調用的倒是 Wolf 的方法。
複製代碼
3.2.1 重寫和重載
"重寫"指子類重寫父類的方法,方法名、參數列表必須相同,返回值範圍小於等於父類,拋出異常範圍小於等於父類,訪問修飾符範圍大於等於父類;若是父類方法訪問修飾符爲 private 或者 final 則子類就不能重寫方法。
    
"重載"發生在同一個類中,方法名必須相同,參數類型不一樣、個數不一樣、順序不一樣,方法返回值和訪問修飾符能夠不一樣。
複製代碼
3.2.2 向上轉型
在多態中須要將子類的引用賦給父類對象,只有這樣該引用才既能調用父類的方法,又能調用子類的方法。
複製代碼
3.2.3 強制轉換
從子類到父類的類型轉換能夠自動進行。
    
從父類到子類的類型轉換必須經過造型(強制類型轉換)實現。
    
無繼承關係的引用類型間的轉換是非法的。
複製代碼

3.3 面向對象特徵-繼承

提升代碼複用性;繼承是多態的前提。

在面向對象編程中,當兩個類具備相同的特徵(屬性)和行爲(方法)時,能夠將相同的部分抽取出來放到一個類中做爲父類,其餘兩個類"繼承"這個父類。繼承後子類自動擁有了父類的部分屬性和方法。

經過繼承建立新類稱爲"子類""派生類"。

被繼承的類稱爲"基類""父類""超類"。

好比:
	狗 {
        吠();
    }

	牧羊犬 繼承 狗 {
        放羊();
    }

上面的例子中,狗類時父類,牧羊犬是子類。牧羊犬類經過繼承得到狗類的 吠() 的能力,同時增長了本身獨有的 放羊() 的能力。

轉換成 Java 代碼以下:
	public class Dog {
        
        public void bark() {
           
            System.out.println("汪汪汪...")
        }
    }

	public class HerdingDog extends Dog {
       
        public void herd() {
           
            System.out.println("放羊中...")
        }
    }

	public class Main() {
       
        HerdingDog dog = new HerdingDog();
        
        dog.bark();
        dog.herd();
    }
複製代碼
3.3.1 構造器的繼承問題
1、構造器是不會被子類繼承的,可是子類的對象在初始化時會默認調用父類的無參構造器
    
2、當父類顯示寫了有參構造器,且沒有無參構造器,子類繼承父類的時候必須顯示的調用父類的有參構造器。調用的方式可使用 super(a,b) 來調用。
複製代碼
3.3.2 static 修飾符的繼承問題
子類是不會繼承父類被 static 修飾的方法和變量,可是能夠調用。
複製代碼
3.3.3 super 關鍵字
super 關鍵字用於引用使用該關鍵字的類的父類。

做爲獨立語句出現的 super 表示調用父類的構造方法。

若是是爲了調用父類的成員變量,成員方法是能夠直接使用的,不須要藉助於 super。

若是子類中出現了和父類同名的成員變量,成員方法,可使用 super 關鍵字告知編譯器,這裏調用的是父類的成員變量,成員方法。

super關鍵字調用構造方法,父類的構造方法
	
    格式:
		super(實際參數);

		1. super 調用父類構造方法,是經過參數類型,個數和順序來肯定對應的父類構造方法;

		2. super 調用父類構造方法,必須在當前子類構造方法代碼塊的第一行;

		3. thissuper 不能同時出如今同一個子類構造方法中調用構造方法;

		4. Java 編譯器會自動選擇"隱式"調用 super() 對應的就是父類的無參構造方法,用於初始化父類的成員變量內存空間。
複製代碼
3.3.4 this 關鍵字
this 關鍵字用於引用當前實例。

當引用可能不明確時,可使用 this 關鍵字來引用當前的實例。

表示調用當前方法的類對象。

能夠利用 this 調用類對象的成員變量和成員方法,能夠用於操做當前類對象。

this 關鍵字調用構造方法:
	
	格式:
		this(實際參數);

	1、有且只能用在 Constructor 構造方法內;
	
	2this() 在類內調用其餘構造方法,是根據小括號內的實際參數類型來選擇的;
	
	3、兩個構造方法,不能經過 this 關鍵字,相互調用,出現語法錯誤;
	
	4this(實際參數)調用其餘構造方法,有且只能是在當前構造方法代碼塊的第一行,而且只能調用一個。
複製代碼
3.3.5 final 關鍵字
final 修飾類:
	不能被繼承,例如 String

final 修飾成員方法:
	不能被子類或者實現類重寫

final 修飾成員變量:
	定義時必須初始化,而且初始化以後沒法修改

final 修飾局部變量:
	賦值以後沒法修改
        
final 標記的變量(成員變量或局部變量)即稱爲常量。名稱大寫,且只 能被賦值一次。
複製代碼
3.3.6 子類對象實例化過程
new 出一個子類對象的時候,必須先 new 出一個父類對象。子類在調用構造方法時,會優先調用父類的構造方法。(默認)
    
若是不寫構造方法,編譯器默認加上一個無參構造器,若是寫了構造器,編譯器就不會添加。
    
若是寫了有參構造器,子類就必須調用父類的構造方法。super(參數)。
    
若是同時有有參和無參構造器,那麼會默認調用無參。也能夠自定義調用有參。
複製代碼

3.4 簡單小結

1、抽象會使複雜的問題更加簡化。
2、從之前面向過程的執行者,變成了面向對象的指揮者。
3、面向對象更符合人類的思惟,面向過程則是機器的思想。
複製代碼

4. Object 類

4.1 Object 類

1、Object 類時全部 Java 的根父類
    
2、若是在類的聲明未使用 extends 關鍵字指名其父類,則默認父類爲 java.lang.Object 類(任何類均可以調用 Object 的方法)

Object 的主要組成:
    1public native int hashCode() 取得 hash 碼 二、equals(Object obj) 比較對象 三、clone() 可用於複雜對象的深拷貝 複製代碼

4.2 == 與 equals 的區別

== 既能夠比較基本類型也能夠比較引用類型。對於基本類型就是比較值,對於引用類型就是比較內存地址。
    
equals 方法用來比較的是兩個對象的內容是否相等,因爲全部的類都是繼承自 java.lang.Object 類的,因此適用於全部對象,若是沒有對該方法進行覆蓋的話,調用的仍然是 Object 類中的方法,而 Object 中的 equals 方法返回的倒是 == 的判斷。
複製代碼

5. static 關鍵字

在 Java 類中,可用 static 修飾屬性、方法、代碼塊、內部類。

被修飾後的成員具有如下特色:
	1、修飾的成員,被全部對象所共享;
	2、訪問權限容許時,可不建立對象,直接用類名.屬性或方法調用。

在 static 方法內部只能訪問類的 static 修飾的屬性或方法,不能訪問類的非 static 的結構。

static 修飾的方法不能被重寫。
        

static 修飾成員變量
	1、保存在內存的數據區;
	2、生存週期是從類文件加載開始,到程序退出結束,生存週期是遠遠超過類對象的;
	3、一處修改,全部使用到該成員變量的位置都會被影;
	4、調用格式:
		類名.靜態成員變量 √
		
static 修飾成員方法
	1、調用格式:
		類名.靜態成員方法(); √
	2、生存週期是從類文件加載開始,到程序退出結束,整個生存週期超過了類對象;
	3、在靜態成員方法中不容許使用非靜態成員;
		a. 不能直接使用非靜態成員變量
		b. 不能直接使用非靜態成員方法
		c. 不能直接使用 this 關鍵字
	4static 修飾的靜態成員方法通常用於封裝成工具類。
	
static 修飾靜態代碼塊
	類文件加載時,必定會執行 static 修飾的靜態代碼塊 
	1、程序加載過程當中,完成一些啓動必須項  MySQL JDBC 加載驅動;
	2、能夠在程序加載過程當中,完成一些程序運行必須的數據 MySQL 鏈接數據庫的必須數據。
複製代碼

6. 代碼塊

對類或對象進行初始化。
    
代碼塊可分爲靜態代碼塊和非靜態代碼塊。(有無 static 修飾)
    
靜態代碼塊:
    用 static 修飾的代碼塊。 
    1、能夠有輸出語句;
    2、能夠對類的屬性、類的聲明進行初始化操做;
    3、不能夠對非靜態的屬性初始化。即:不能夠調用非靜態的屬性和方法;
    4、如有多個靜態的代碼塊,那麼按照從上到下的順序依次執行;
    5、靜態代碼塊的執行要先於非靜態代碼塊;
    6、靜態代碼塊隨着類的加載而加載,並且值執行一次。
  
        
非靜態代碼塊:
        沒有 static 修飾的代碼塊。
        1、能夠用輸出語句;
        2、能夠對類的屬性、類的聲明進行初始化操做;
        3、除了調用非靜態的結構外,還能夠調用靜態的變量和方法;
        4、如有多個非靜態的代碼塊,那麼按照從上到下的順序依次執行;
        5、每次建立對象的時候,都會執行一次。且先於構造器執行。
複製代碼

7. 抽象類和抽象方法

7.1 什麼是抽象類和抽象方法

abstract 關鍵字來修飾一個類,這個類叫作抽象類
    
用 abstract 關鍵字來修飾一個方法,該方法叫作抽象方法。
複製代碼

7.2 abstract 關鍵字

abstarct 關鍵字修飾的成員方法,是要求子類強制重寫的。

1abstract 修飾的方法沒有方法體;
2abstract 修飾的方法必須定義在 abstract 修飾的類內或者 interface 接口內;
3、一個普通類繼承於 abstract 修飾的類,須要完成實如今 abstract 類內的全部 abstract 方法;
4abstract 類沒有本身的類對象。
複製代碼

8. interface

1、用 interface 來定義;
2、接口中的全部成員變量都默認是由 public static final 修飾的;
3、接口中的全部抽象方法都默認由 public abstract 修飾的;


interface 關鍵字使用來聲明新的 Java 接口,接口是方法的集合。 接口是 Java 語言的一項強大功能。 任何類均可以聲明它實現一個或多個接口,這意味着它實現了在這些接口中所定義的全部方法。 實現了接口的任何類都必須提供在該接口中的全部方法的實現。 一個類能夠實現多個接口。 複製代碼

9. 接口和抽象類的區別

相同點:
    1、都不能建立對象;
    2、均可以定義抽象方法,而且必定在子類中重寫。

        
不一樣點:
    1、關鍵字不一樣 interfaceabstract;
    2、抽象方法中既能夠有抽象方法也能夠有普通方法;
    3、接口中全部的方法都是抽象方法;
    4、抽象類的方法能夠任意權限,接口中的方法只能是 public;
    5、抽象類只能單繼承,接口能夠多實現。

        
JDK 1.8 開始,接口中的方法再也不是隻能有抽象方法(普通方法會被隱式地指定爲 public abstract 方法),它還能夠有靜態方法和 default 方法。而且靜態方法與 default 方法能夠有方法體!
        
讓咱們來看一下下面的例子:
        
public interface NewInterface {
        
        static void staticMethod() {
            
            System.out.println("staticMethod");
        }
        
        default void defaultMethod() {
            
            System.out.println("defaultMethod");
        }
        
        public void getInfo();
    }

由上面的例子能夠看出給出一個接口,在 JDK 1.8 的環境下,他能夠擁有靜態方法和 default 方法,所謂 default 方法便是使用 default 關鍵字來修飾的方法。
    
一個接口能夠有多個靜態方法和 default 方法,沒有個數限制。實現類只須要實現它的抽象方法便可。
    
關於靜態方法和 default 方法的調用:
    1、對於靜態方法,直接由接口名調用,不須要由接口實現類的對象來調用;
    2、對於 default 方法,很明顯是須要實例對象來調用的。


import org.junit.Test;
 
public class NewInterfaceTest {
    
    @Test
    public void test() {
        
        NewInterface.staticMethod();
        new SimpleImpl().defaultMethod();
    }
}
        
並且 default 方法的調用有一點須要特別注意。咱們知道在 Java 中是單繼承的,可是是能夠實現多個接口的,因此,當一個類實現了多個接口以後,若是多個接口有着相同的 default 方法,即方法名和參數列表相同。那麼此時就會出現問題,沒法識別究竟是調用的哪一個接口的方法,這個時候就必需要在實現類裏面顯式重寫 default 的方法,而關於 default 的方法的重寫,咱們在實現類中不須要繼續出現 default 關鍵字也不能出現 default 關鍵字。
        

public class SimpleImpl implements NewInterface {
 
    @Override
    public void getInfo() {
        
        // TODO Auto-generated method stub
        System.out.println("INFO");
        defaultMethod();
    }
 
    public void defaultMethod() {
        
        System.out.println("Impl default Method");
    }
}

重寫的 default 方法必須的訪問權限必須是 public,由於 default 方法除了沒有顯式的訪問修飾符外,只能用 public 訪問限定符來修飾,而咱們知道在 Java 中,要重寫一個方法,訪問限定符必定要大於或等於父類或者接口指定的訪問限定符範圍,並且方法聲明處拋出的異常也要大於後者。因此訪問權限必須是 public。

最後,當 default 方法和實現類繼承的父類的方法同名時,優先調用父類的方法
複製代碼

10. 內部類

在 Java 中,容許一個類定義位於另外一個類的內部,前者稱爲內部類,後者稱爲外部類。
    
Inner class 的名字不能與包含它的外部類類名相同。 複製代碼

10.1 成員內部類(static 成員內部類和非 static 成員內部類)

class Outer {
    
    private int s;
    
    public class Inner {
    
        public void mb() {
            
        	s = 100;
        	System.out.println("在內部類Inner中s=" + s);
    	}
   }
    
    public void ma() {
        
        Inner i = new Inner();
        i.mb();
    } 
}

public class InnerTest {
    
    public static void main(String args[]) {
       
        Outer o = new Outer();
        o.ma();
	}
}
複製代碼

10.2 局部內部類(不談修飾符)

class 外部類 {
    方法() {
        class 局部內部類 {}
    }
    
    {
        class 局部內部類 {}
    }
}


1、只能在聲明它的方法或代碼塊中使用,並且是先聲明後使用,除此以外的任何地方都不能使用該類。

2、可是它的對象能夠經過外部方法的返回值返回使用,返回值類型只能是局部內部類的父類或父接口類型。
    
3、局部內部類可使用外部方法的局部變量,可是必須是 final 的。
複製代碼

10.3 匿名內部類

匿名內部類不能定義任何靜態成員、方法和類,只能建立匿名內部類的一個實例。
    
一個匿名內部類必定是在 new 後面,用其隱含實現一個接口或實現一個類。
    
匿名內部類的特色:
    1、匿名內部類必須繼承父類或實現接口;
    2、匿名內部類只能有一個對象;
    3、匿名內部類對象只能使用多態形式引用。
 
        
interface A {
        
    public abstract void fun1();
}

public class Outer {
    
    public static void main(String[] args) {
        
        new Outer().callInner(new A() {
            //接口是不能 new 但此處比較特殊是子類對象實現接口,只不過沒有爲對象取名
            public void fun1() {
                
                System.out.println("implement for fun1");
            }
        });// 兩步寫成一步了
    }

    public void callInner(A a) {
        
        a.fun1();
    }
}
複製代碼

11. 異常處理

異常:
    在 Java 語言中,將程序執行中發生"不正常狀況稱爲" "異常"。
    
(開發過程當中的語法錯誤和邏輯錯誤不是異常)
    
Java 程序在執行過程當中所發生的異常事件可分爲兩類:
    1、Error:
    	Java 虛擬機沒法解決的嚴重問題。如:JVM 系統內部錯誤、資源耗盡等嚴重狀況。通常不編寫針對性的代碼進行處理。
    
    2、Exception:其餘由於編程錯誤或偶然的外在因素致使的通常性問題,可使用針對性代碼進行處理。
複製代碼

11.1 異常處理機制一:try-catch-finally

在編寫程序時,常常要在可能出現錯誤的地方加上檢測的代碼,如進行 x/y 運算時,要檢測分母爲0,數據爲空,輸入的不是數據而是字符串等。過多的 if-else 分支會致使程序的代碼加長、臃腫,可讀性差。所以採用異常處理機制。
    
    a.Java 程序的執行過程當中如出現異常,會生成一個異常類對象,該異常對象將被提交給 Java 運行時系統,這個過程稱爲拋出(throw)異常。
    
    b.若是一個方法內拋出異常,該異常對象會被拋給調用者方法中處理。若是異常沒有在調用者方法中處理,它繼續被拋給這個調用方法的上層方法。這個過程將一直繼續下去,直到異常被處理。這一過程稱爲捕獲(catch)異常。
    
    c.程序員一般只能處理 Exception,而對 Error 無能爲力。
    
異常處理是經過 try-catch-finally 語句實現的。
    
try{
    //可能產生異常的代碼
} catch (ExceptionName1 e) {
    //當產生ExceptionName1型異常時的處置措施
} catch (ExceptionName2 e) {
    //當產生ExceptionName2型異常時的處置措施
}
[finally {
    //不管是否發生異常,都無條件執行的語句
}]

若是拋出的異常是 IOException 等類型的非運行時異常,則必須捕獲,不然編譯錯誤。也就是說,咱們必須處理"編譯時異常",將異常進行捕捉,轉化爲"運行時異常"複製代碼

11.2 異常處理機制二:throws

a.若是一個方法(中的語句執行時)可能生成某種異常,可是並不能肯定如何處理這種異常,則此方法應顯示地聲明拋出異常,代表該方法將不對這些異常進行處理,而"由該方法的調用者負責處理"。
    
b.在方法聲明中用 throws 語句能夠聲明拋出異常的列表,throws 後面的異常類型能夠是方法中產生的異常類型,也能夠是它的父類。
    
c.重寫方法不能拋出比被重寫方法範圍更大的異常。
複製代碼

12. 其餘關鍵字

12.1 class

class 關鍵字用來聲明新的 Java 類,該類是相關變量和 / 或方法的集合。 類是面向對象的程序設計方法的基本構造單位。 類一般表明某種實際實體,如幾何形狀或人。 類是對象的模版。 每一個對象都是類的一個實例。 要使用類,一般使用 new 操做符將類的對象實例化,而後調用類的方法來訪問類的功能。 複製代碼

12.2 new

new 關鍵字用於建立類的新實例。 

new 關鍵字後面的參數必須是類名,而且類名的後面必須是一組構造方法參數(必須帶括號)。 

參數集合必須與類的構造方法的簽名匹配。 

= 左側的變量的類型必須與要實例化的類或接口具備賦值兼容關係。
複製代碼

12.3 instanceof

instanceof 運算符的前一個操做符是一個引用變量,後一個操做數一般是一個類(能夠是接口),用於判斷前面的對象是不是後面的類,或者其子類、實現類的實例。若是是返回 true,不然返回 false複製代碼
相關文章
相關標籤/搜索