1. 程式人生 > >JavaSE基礎復習---1---2018/9/27

JavaSE基礎復習---1---2018/9/27

單位 程序 進行 適用於 容易 print div 不同的 計算

2018/9/27

JavaSE學習筆記-1

目錄:

  1. Java的起源
  2. Java語言概述

1、Java的起源  

  現代編程語言的發展,大致可以理解為,機器碼語言---匯編語言---C語言---C++語言---Java語言。每一次新編程語言的誕生都會有背後的原因。例如從C語言發展到C++語言,就是因為復雜性(complexity)。由於C語言是面向過程編程,即使采用了結構化編程的方法,當程序大小達到25000-100000行時,就很難從整體上把握其復雜性。為解決這個問題,面向對象編程新方法誕生了。面向對象的編程是通過使用繼承性、封裝性和多態性來幫助組織復雜程序的編程方法。由此C++語言誕生,最初把這種新語言稱為“帶類的C”。1983年,改名為C++。C++通過增加面向對象的特性擴充了C。因為C++產生在C的基礎之上,因此它包括了C所有的特征、屬性和優點。這是C++作為語言成功的一個關鍵原因。C++的發明不是企圖創造一種全新的編程語言,而是對一個已經高度成功的語言的改進。C++在1997年11月被標準化,目前的標準是ANSI/ISO。

  那麽既然已經有了C++這種面向對象語言的誕生,還需要Java語言呢?最初的原因是因特網的誕生。由於因特網由不同的、分布式的系統組成,其中包括各種類型的計算機、操作系統和CPU。因此,同一個程序,用戶就希望其能夠運行在不同類型的平臺。而要用C++語言在不同的CPU上編寫程序,需要一個完整的以該CPU為目標的C++編譯器,而創建一個編譯器是一項既費錢又費力的工作,因此需要找到替代的辦法。此時Java語言嶄露頭角,其可移植,跨平臺而且獨立於平臺的特性使得Java能夠生成運行於不同環境、不同CPU芯片上的代碼。

  Java能夠實現其安全、可移植性的關鍵在於Java編譯器的輸出並不是可執行的代碼,而是字節碼(bytecode)。字節碼是一套設計用來在Java運行時系統下執行的高度優化的指令集,該Java運行時系統稱為Java虛擬機(JavaVirtual Machine,JVM)。在其標準形式下,JVM 就是一個字節碼解釋器。將一個Java程序翻譯成字節碼,有助於它更容易地在一個大範圍的環境下運行程序。原因非常直接:只要在各種平臺上都實現Java虛擬機就可以了。在一個給定的系統中,只要系統運行包存在,任何Java程序就可以在該系統上運行。記住:盡管不同平臺的Java虛擬機的細節有所不同,但它們都解釋同樣的Java字節碼。如果一個Java程序被編譯為本機代碼,那麽對於連接到Internet上的每一種CPU類型,都要有該程序的對應版本。這當然不是一個可行的解決方案。因此,對字節碼進行解釋是編寫真正可移植性程序的最容易的方法。

  對Java程序進行解釋也有助於它的安全性。因為每個Java程序的運行都在Java虛擬機的控制之下,Java虛擬機可以包含這個程序並且能阻止它在系統之外產生副作用。盡管被解釋的程序的運行速度通常確實會比同一個程序被編譯為可執行代碼的運行速度慢一些。但是對Java來說,這兩者之間的差別不太大。使用字節碼能夠使Java運行時系統的程序執行速度比你想象的快得多。盡管Java被設計為解釋執行的程序,但是在技術上Java並不妨礙動態將字節碼編譯為本機代碼。SUN公司在Java 2發行版中提供了一個字節碼編譯器——JIT(Just In Time,即時)。JIT是Java虛擬機的一部分,它根據需要、一部分一部分地將字節碼實時編譯為可執行代碼。它不能將整個Java程序一次性全部編譯為可執行的代碼,因為Java要執行各種檢查,而這些檢查只有在運行時才執行。記住這一點是很重要的,因為JIT只編譯它運行時需要的代碼。盡管如此,這種即時編譯執行的方法仍然使性能得到較大提高。即使對字節碼進行動態編譯後,Java程序的可移植性和安全性仍能得到保證,因為運行時系統(該系統執行編譯)仍然能夠控制Java程序的運行環境。不管Java程序被按照傳統方式解釋為字節碼,還是被動態編譯為可執行代碼,其功能是相同的。

2、Java語言概述

  首先,Java作為一門面向對象的編程語言,具有面向對象的三個基本原則,即封裝、繼承、多態。  

  1.封裝                                                                                                 封裝(Encapsulation)是將代碼及其處理的數據綁定在一起的一種編程機制,該機制保證了程序和數據都不受外部幹擾且不被誤用。理解封裝性的一個方法就是把它想成一個黑匣子,它可以阻止在外部定義的代碼隨意訪問內部代碼和數據。封裝代碼的好處是每個人都知道怎麽訪問它,但卻不必考慮它的內部實現細節,也不必害怕使用不當會帶來負面影響。

     java語言封裝的基本單位是類,類內部包括類成員與類方法。類中的成員與方法均可被聲明為私有private或者公有public。私有方法和數據僅能被一個類的成員代碼所訪問,其他任何不是類的成員的代碼都不能訪問私有的方法或變量。既然類的私有成員僅能被程序中的其他部分通過該類的公共方法訪問,那麽你就能保證不希望發生的事情就一定不會發生。

  2.繼承

     繼承是一個對象獲得另一個對象屬性的過程。繼承性與封裝性相互作用。如果一個給定的類封裝了一些屬性,那麽它的任何子類將具有同樣的屬性,而且還添加了子類自己特有的屬性。這是面向對象的程序在復雜性上呈線性而非幾何性增長的一個關鍵概念。新的子類繼承它的所有祖先的所有屬性。它不與系統中其余的多數代碼產生無法預料的相互作用。

  2.多態

     多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。假設類B、C、D均是類A的子類,有代碼如下:

    A a1 = new B();

    A a2 = new C();

    A a3 = new D();

     如上,實例化了三個B、C、D、對象,其引用變量並且向上轉型為父類的引用對象(向上轉型的缺點是,父類類型的引用可以調用父類中定義的所有屬性和方法,對於只存在與子類中的方法和屬性它就望塵莫及了)。對於多態,指向子類的父類引用由於向上轉型了,它只能訪問父類中擁有的方法和屬性,而對於子類中存在而父類中不存在的方法,該引用是不能使用的,盡管是重載該方法。若子類重寫了父類中的某些方法,在調用該些方法的時候,必定是使用子類中定義的這些方法(動態連接、動態調用)。對於面向對象而言,多態分為編譯時多態和運行時多態。其中編輯時多態是靜態的,主要是指方法的重載,它是根據參數列表的不同來區分不同的函數,通過編輯之後會變成兩個不同的函數,在運行時談不上多態。而運行時多態是動態的,它是通過動態綁定來實現的,也就是我們所說的多態性。

     實現多態的三個必要條件:繼承、重寫、向上轉型。

      繼承:在多態中必須存在有繼承關系的子類和父類。

      重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。

      向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法。

  只有滿足了上述三個條件,我們才能夠在同一個繼承結構中使用統一的邏輯實現代碼處理不同的對象,從而達到執行不同的行為。對於Java而言,它多態的實現機制遵循一個原則:當父類對象引用變量引用(指向)子類對象時,是由被引用對象的類型決定了調用誰的成員方法,而不由引用對象的類型決定,但是這個被調用的方法必須是在父類中定義過的,也就是說被子類重寫的方法。

     實現形式:繼承與接口。

       繼承實現:基於繼承的實現機制主要表現在父類和繼承該父類的一個或多個子類對某些方法的重寫,多個子類對同一方法的重寫可以表現出不同的行為。對於引用子類的父類類型,在處理該引用時,它適用於繼承該父類的所有子類,子類對象的不同,對方法的實現也就不同,執行相同動作產生的行為也就不同。

       接口實現:繼承是通過重寫父類的同一方法的幾個不同子類來體現的,那麽就可通過實現接口並覆蓋接口中同一方法的幾不同的類體現的。在接口的多態中,指向接口的引用必須是指定這實現了該接口的一個類的實例程序,在運行時,根據對象引用的實際類型來執行對應的方法。繼承都是單繼承,只能為一組相關的類提供一致的服務接口。但是接口可以是多繼承多實現,它能夠利用一組相關或者不相關的接口進行組合與擴充,能夠對外提供一致的服務接口。所以它相對於繼承來說有更好的靈活性。

public class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}

public class C extends B{

}

public class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));//實際是this.show((super)O) 結果"A and A"       
System.
out.println("2--" + a1.show(c));//實際是this.show((super)O) ,C->B->A,結果"A and A" System.out.println("3--" + a1.show(d));//實際是this.show(O),結果"A and D" System.out.println("4--" + a2.show(b));//實際是this.show((super)O),同時由於滿足多態的三個條件,執行子類B的show(A obj)方法。 System.out.println("5--" + a2.show(c));//實際是this.show((super)O),同時由於滿足多態的三個條件,執行子類B的show(A obj)方法。 System.out.println("6--" + a2.show(d));//實際是this.show(O),結果"A and D" System.out.println("7--" + b.show(b));//實際是this.show(o),結果"B and B" System.out.println("8--" + b.show(c));//實際是this.show((super)O),執行類B的show(B obj)方法
System.
out.println("9--" + b.show(d));//實際是super.show(O),執行類A的show(D obj)方法 } }

     註:繼承鏈中對象方法調用的優先級:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O),this代表該引用變量對應類的類型

      1---//調用的是類A的show(A obj)方法,因為b對象是A的子類B的實例,滿足向上轉型(b是一種A),不能向下轉型為D

      2---//調用的是類A的show(A obj)方法,原因同上,不過是兩層繼承

      3---//調用類A的show(D obj)方法

      4---//a2是指向B類型的一個引用變量,調用show方法時,從被引用的對象方法中執行相應方法,條件是這個方法在父類中必須定義過,也就是被子類重寫的方法。在B類中有兩個方法,show(B obj)方法由於參數列表與父類A的show方法不一樣,故不算是重寫。show(A obj)方法滿足重寫條件,故執行的是show(A obj)方法。

      5---//a2是A類型的引用變量,所以this就代表了A,a2.show(c),它在A類中找發現沒有找到,於是到A的超類中找(super),由於A沒有超類(Object除外),所以跳到第三級,也就是this.show((super)O),C的超類有B、A,所以(super)O為B、A,this同樣是A,這裏在A中找到了show(A obj),同時由於a2是B類的一個引用且B類重寫了show(A obj),因此最終會調用子類B類的show(A obj)方法,結果也就是B and A。

      6---//a2.show(d),a2是A類型的引用變量,所以this就代表了A,在A中找到了show(D obj)方法,因此直接執行該方法。

      7---//b.show(b),首先this代表B,在B中找到了show(B b),因此直接執行該方法。

      8---//b.show(c),首先this代表B,在B中未找到show(C c),因此在super.show(O)中找,也就是A類中有沒有show(C c),當然是沒有。接下來在this.show((super)O),找到了show(B b),因此該方法得到執行。

      9---//b.show(d),首先this代表B,在B中未找到show(D d),因此在super.show(O)中找,也就是A類中有沒有show(D d),當然是有了,直接執行該方法。

     綜上所述,當父類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類重寫的方法,但是它仍然要根據繼承鏈中方法調用的優先級來確認方法,該優先級為:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

TIPS:重載與重寫

     重載是指在同一個類中,可以多個參數不同、方法體不同的方法共同用一個方法名,調用此方法時根據參數列表來選擇具體調用的方法。重載與返回值無關,因為只需要根據方法名與參數列表就知道要調用那一個函數了。具體的應用是類的構造函數,多個不同的帶參構造函數可以初始化對象中不同屬性的值。

public class A{
    
    private int i;
    public A(){

    }
    
    public A(int i){
        this.i = i;
    }
   public static void main(String[] args){
   A a1 = new A();
A a2 = new A(10);
   } }

     重寫在上文中多態已經談過,是子類對於父類中方法的一種改寫以適應子類自己使用,也叫子類的方法覆蓋父類的方法。重寫要滿足三個條件(三同一小一大):

      1、要求返回值、方法名和參數都相同。

       2、子類拋出的異常不能超過父類相應方法拋出的異常的範圍。(子類異常不能超出父類異常)

        3、子類方法的的訪問級別不能低於父類相應方法的訪問級別(子類訪問級別不能低於父類訪問級別)

JavaSE基礎復習---1---2018/9/27