面向對象編程 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 . this 和 super 不能同時出如今同一個子類構造方法中調用構造方法;
4 . Java 編譯器會自動選擇"隱式" 調用 super () 對應的就是父類的無參構造方法,用於初始化父類的成員變量內存空間。
複製代碼
3.3.4 this 關鍵字
this 關鍵字用於引用當前實例。
當引用可能不明確時,可使用 this 關鍵字來引用當前的實例。
表示調用當前方法的類對象。
能夠利用 this 調用類對象的成員變量和成員方法,能夠用於操做當前類對象。
this 關鍵字調用構造方法:
格式:
this (實際參數);
1 、有且只能用在 Constructor 構造方法內;
2 、this () 在類內調用其餘構造方法,是根據小括號內的實際參數類型來選擇的;
3 、兩個構造方法,不能經過 this 關鍵字,相互調用,出現語法錯誤;
4 、this (實際參數)調用其餘構造方法,有且只能是在當前構造方法代碼塊的第一行,而且只能調用一個。
複製代碼
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 的主要組成:
1 、public 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 關鍵字
4 、static 修飾的靜態成員方法通常用於封裝成工具類。
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 關鍵字修飾的成員方法,是要求子類強制重寫的。
1 、abstract 修飾的方法沒有方法體;
2 、abstract 修飾的方法必須定義在 abstract 修飾的類內或者 interface 接口內 ;
3 、一個普通類繼承於 abstract 修飾的類,須要完成實如今 abstract 類內的全部 abstract 方法;
4 、abstract 類沒有本身的類對象。
複製代碼
8. interface
1 、用 interface 來定義 ;
2 、接口中的全部成員變量都默認是由 public static final 修飾的;
3 、接口中的全部抽象方法都默認由 public abstract 修飾的;
interface 關鍵字使用來聲明新的 Java 接口,接口是方法的集合。 接口是 Java 語言的一項強大功能。 任何類均可以聲明它實現一個或多個接口,這意味着它實現了在這些接口中所定義的全部方法。 實現了接口的任何類都必須提供在該接口中的全部方法的實現。 一個類能夠實現多個接口。 複製代碼
9. 接口和抽象類的區別
相同點:
1 、都不能建立對象;
2 、均可以定義抽象方法,而且必定在子類中重寫。
不一樣點:
1 、關鍵字不一樣 interface 和 abstract ;
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 () {
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() {
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) {
} catch (ExceptionName2 e) {
}
[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 。
複製代碼