Java面向對象

對象和類

引用類型變量java

爲了可以對實例化的對象進行訪問控制,需一個特殊的變量,即引用。對引用有兩點須要說明:
1. 引用類型變量能夠存儲該類對象的地址信息,一般稱爲「指向該類的對象」,當一個引用類型變量指向該類的對象,就能夠經過這個變量對對象實施訪問。
2. 除8種基本類型以外,用類、接口、數組等聲明的變量都稱爲引用類型變量,簡稱「引用」。

null和NullPointerException程序員

須要注意:當一個引用的值爲null的時候,若是經過引用訪問對象成員變量或者調用方法是不合邏輯的(因其沒有指向某對
象,天然不會有屬性和方法)。此時,會產生NullPointerException(空指針異常)。

方法的重載數組

  • 方法的簽名安全

    方法的簽名包含以下兩個方面:方法名和參數列表。
    
      Java語法規定,一個類中不能夠有兩個方法簽名徹底相同的方法,即:一個類中不能夠有兩個方法的方法
      名和參數列表都徹底相同,可是,若是一個類的兩個方法只是方法名相同而參數列表不一樣,是能夠的。
  • 方法重載及其意義數據結構

    在Java語言中,容許多個方法的名稱相同,但參數列表不一樣,此種方式稱爲方法的重載(overload)。函數

構造方法this

  • 構造方法語法結構操作系統

    構造方法是在類中定義的方法, 但不一樣於其餘的方法,構造方法的定義有以下兩點規則:線程

    1. 構造方法的名稱必須與類名相同。
    2. 構造方法沒有返回值,但也不能寫void。
  • 經過構造方法初始化成員變量指針

  • this關鍵字的使用

    this指代當前對象
  • 默認的構造方法

    JAVA語法規定,任何一個類都必須含有構造方法,假如源程序中沒有定義,則編譯器在編譯時將爲其添加
      一個無參的空構造方法(此方法稱之爲「默認的構造方法」)
  • 構造方法的重載

    不少時候,爲了使用的方便,能夠對一個類定義多個構造方法,這些構造方法都有相同的名稱(類名),只是方法的參數不一樣,稱之爲構造方法的重載。
      在建立對象時,Java編譯器會根據不一樣的參數調用來不一樣構造方法

數組

引用類型數組

  • 數組是對象

    在java中,數組屬於引用數據類型,數組對象存放在堆中存儲,數組變量屬於引用類型,存儲數組對象的地址信息,指向數組對象。 而數組的元素能夠當作數組對象的成員變量(只不過類型所有相同)

    數組的內存分配

  • 引用類型數組的聲明

    剛剛聲明的數組爲基本類型數組,除了基本類型數組之外,也能夠聲明引用類型數組。所謂引用類型數組,即數組元素的類型不是基本類型(int,char,float。。) , 而是引用類型,看以下代碼:

    Cell [ ]    cells  = new  Cell [ 4 ] ;

    其內存分配以下圖

    內存分配圖

    從上圖示能夠看出,new Cell[4]實際是分配了4個空間用於存放4個Cell類型的引用,並賦初始值爲null,而並不是是分配了4個Cell類型的對象。

  • 引用類型數組的初始化

    若是但願每個元素都指向具體的對象,則須要針對每個數組元素進行「new」運算。與基本類型數組同樣,也能夠採用靜態初始化的方式進行初始化操做。以下代碼所示:

    Cell[ ]  cells = new  Cell[4];
      cells[0] = new Cell(0,4);
      cells[1] = new Cell(1,3);
      cells[2] = new Cell(1,4);
      cells[3] = new Cell(1,5);

    等價於:

    Cell[ ] cells = new Cell[ ] {
          new Cell(0,4) ,
          new Cell(1,3) ,
          new Cell(1,4) , 
          new Cell(1,5) 
      } ;

    如上數組內存分配圖以下圖– 6 所示:

    內存分配圖

  • 數組的類型是基本類型數組

    int [ ][ ] arr = new int[3][ ];
     arr[0] = new int[2];
     arr[1] = new int[3];
     arr[2] = new int[2];
     arr[1][1] = 100;
    
     分析如上代碼能夠看出,變量arr指向一個數組,該數組有三個元素,每一個元素都是int類型數組,長度分別爲
     2,3,2,arr[1][1]=100表示將arr數組中的第2個元素(數組)的第2個元素賦值爲100, 其內存分配如圖所示:

    內存分配圖

對象內存管理

對象內存管理

  1. 在JAVA中,有java程序、虛擬機、操做系統三個層次,其中java程序與虛擬機交互,而虛擬機與操做系統交互。編譯好的java字節碼文件運行在JVM中。
  2. 程序中不管代碼仍是數據,都須要存儲在內存中,而java程序所需內存均由JVM進行管理分配,開發者只需關心JVM是如何管理內存的,而無需關注某種操做系統是如何管理內存的,這就保證了java程序的平臺無關性。
  3. JVM會將申請的內存從邏輯上劃分爲三個區域:堆、棧、方法區。這三個區域分別用於存儲不一樣的數據。

堆內存

  • 對象存儲在堆中

    JVM在其內存空間開闢了一個稱爲「堆」的存儲空間,這部分空間用於存儲使用new關鍵字所建立的對象。請看以下代碼:

    Cell   c  = new  Cell ();

    其內存分佈如圖所示:

    內存分佈

    從圖中能夠看到右側的堆內存,new Cell()所建立的對象在堆中分配,同時成員變量亦在此分配,並賦初始值爲零。引用類型變量c在棧內存中分配,其中保存的數據,爲對象在堆內存中的地址信息,假設對象在堆內存的地址爲40DF,則c中保存的便是40DF。

  • 成員變量的生命週期

    當聲明好對象以後,對該對象(堆中的Cell)的訪問須要依靠引用變量(棧中的c),那麼當一個對象沒有任
      何引用時,該對象被視爲廢棄的對象,屬於被回收的範圍,同時該對象中的全部成員變量也隨之被回收。
      能夠這樣認爲,成員變量的生命週期爲:從對象在堆中建立開始到對象從堆中被回收結束。
    
      Cell   c  =  new  Cell();
      c = null ;
    
      當將c賦值爲null時,表示c再也不指向剛剛分配的對象空間,此時成員變量失效。
  • 垃圾回收機制

    1. 垃圾回收器(Garbage Collection,GC)是JVM自帶的一個線程(自動運行着的程序),用於回收沒有任何引用所指向的對象。
    2. GC線程會從棧中的引用變量開始跟蹤,從而斷定哪些內存是正在使用的,若GC沒法跟蹤到某一塊堆內存,那麼GC就認爲這塊內存再也不使用了,即爲可回收的。可是,java程序員不用擔憂內存管理,由於垃圾收集器會自動進行管理。
  • Java程序的內存泄漏問題

    1. 內存泄露是指,再也不被使用的內存沒有被及時的回收,嚴重的內存泄露會因過多的內存佔用而致使程序的崩潰。在程序中應該儘可能避免沒必要要的內存浪費。
    2. GC線程判斷對象是否能夠被回收的依據是該對象是否有引用來指向,所以,當肯定該對象再也不使用時,應該及時的將其引用設置爲null,這樣,該對象即再也不被引用,屬於可回收的範圍。
  • System.gc()方法

    1. GC的回收對程序員來講是透明的,並不必定一發現有無引用的對象就當即回收。通常狀況下,當咱們須要GC線程即刻回收無用對象時,能夠調用System.gc()方法。此方法用於建議JVM立刻調度GC線程回收資源,但具體的實現策略取決於不一樣的JVM系統。

非堆---棧

  • 棧用於存放方法中的局部變量

    1. JVM在其內存空間開闢一個稱爲」棧」的存儲空間,這部分空間用於存儲程序運行時在方法中聲明的全部的局部變量,例如,在main方法中有以下代碼:

      Cell   c   =   new   Cell ( );
       int num  =  5;

      其內存分配如圖所示:

      內存分配

      說明:方法中的變量即爲局部變量,是在棧內存中分配,若變量爲值類型,則在棧中存儲的就是該變量的值。若變量爲引用類型,則在棧中存儲的是堆中對象的地址。

  • 局部變量的生命週期

    1. 一個運行的Java程序從開始到結束會有屢次方法的調用。JVM會爲每個方法的調用在棧中分配一個對應的空間,這個空間稱爲該方法的棧幀。一個棧幀對應一個正在調用中的方法,棧幀中存儲了該方法的參數、局部變量等數據。當某一個方法調用完成後,其對應的棧幀將被清除,局部變量即失效。
  • 成員變量和局部變量

    成員變量與局部變量的差異以下:
      局部變量:
      1) 定義在方法中;
      2) 沒有默認值,必須自行設定初始值;
      3) 方法被調用時,存在棧中,方法調用結束時局部變量從棧中清除;
      成員變量:
      1) 定義在類中,方法外;
      2) 由系統設定默認初始值,能夠不顯式初始化;
      3) 所在類被實例化後,存在堆中,對象被回收時,成員變量失效;

非堆---方法區

  • 方法區用於存放類的信息

    1. 方法區用於存放類的信息,Java程序運行時,首先會經過類裝載器載入類文件的字節碼信息,通過解析後將其裝入方法區。類的各類信息(包括方法)都在方法區存儲,看以下代碼:

      Cell   c = new  Cell();

      程序在執行這句話時,Cell類首先被裝載到JVM的方法區,其中包括類的基本信息和方法定義等,以下圖– 3 所示:

      方法區

      經過圖示能夠看出,在方法區中,包含Cell類的字節碼文件,及類的基本信息及方法drop等。

  • 方法只有一份

    1. 當類的信息被加載到方法區時,除了類的類型信息之外,同時類內的方法定義也被加載到方法區;

    2. 類在實例化對象時,多個對象會擁有各自在堆中的空間,但全部實例對象是共用在方法區中的一份方法定義的。意味着,方法只有一份。看以下代碼:

      JFrame f1 = new JFrame(); 
       JFrame f2 = new JFrame(); 
       f1.setSize(200, 300);
       f2.setSize(300,400);

      如上的代碼中,對象有兩個,可是setSize方法只有一份,分別針對f1指向的對象和f2指向的對象調用了兩次。

繼承的意義

繼承

  • 泛化的過程

    1. 前面的案例中定義了T類和J類, 經過分析能夠發現, 在這兩個類中存在着大量的重複代碼,像cells屬性、print方法、drop方法、moveLeft方法、moveRight方法,在這兩個類中都存在,而且實現上基本也是相同的,本着代碼重用的原則,可使用繼承的方式來實現。
    2. 首先,構建T類和J類的父類Tetromino類,將公共的(T類和J類公有的)信息存放在父類中, T類和J類繼承Tetromino父類。此時,子類便可以共享父類的數據。這個過程就是泛化的過程。
  • extends關鍵字

    1. 使用繼承能夠實現代碼的重用,在java語言中,須要經過extends關鍵字實現類的繼承。繼承完成後,子類(Sub class)能夠繼承父類(Super class)的成員變量及成員方法,同時子類也能夠定義本身的成員變量和成員方法。屆時,子類將具備父類的成員及本類的成員。
    2. 須要注意的是,Java語言不支持多重繼承,即:一個類只能繼承一個父類,但一個父類能夠有多個子類。
  • 繼承中構造方法

    1. 父類的無參構造方法之因此被執行,是由於java規定,子類在構造以前必須先構造父類。
    2. 事實上,子類的構造方法中是必需要經過super關鍵字來調用父類的構造方法的,這樣才能夠保證妥善的初始化繼承自父類的成員變量。
    3. 可是看上一個案例中的代碼並無super調用父類構造方法,那是由於,若是子類的構造方法中沒有調用父類的構造方法,則java編譯器會自動的加入對父類無參構造方法的調用。
    4. super();爲編譯器自動加入的,而且super關鍵字必須位於子類構造方法的第一行,不然會有編譯錯誤。
    5. 另一點須要注意的是,若父類沒有提供無參的構造方法,則會出現編譯錯誤。
    6. 若是在子類構造方法中沒有寫super調用父類構造方法,這時編譯器會默認添加super()來調用父類的無參構造方法,可是父類中又沒有定義無參的構造方法,所以會發生編譯錯誤。
  • 父類引用指向子類的對象

    一個子類的對象能夠向上造型爲父類的類型。

重寫

  • 方法的重寫

    1. 在java語言中,子類能夠重寫(覆蓋)繼承自父類的方法,即方法名和參數列表與父類的方法相同,可是方法的實現不一樣。
    2. 當子類重寫了父類的方法後,該重寫方法被調用時(不管是經過子類的引用調用仍是經過父類的引用調用),運行的都是子類重寫後的版本。
  • 重寫中使用super關鍵字

    在子類重寫的方法中,能夠經過super關鍵字調用父類的版本

  • 重寫和重載的區別

    重載與重寫是徹底不一樣的語法現象,區別以下所示:
      · 重載: 是指在一個類中定義多個方法名相同但參數列表不一樣的方法,在編譯時,根據參數的個數和類型來決定綁定哪一個方法。
      · 重寫: 是指在子類中定義和父類徹底相同的方法,在程序運行時,根據對象的類型(而不是引用類型)而調用不一樣的方法。
    
      分析以下代碼的輸出結果:
      class Super {
      	public void f() {
      		System.out.println ("super.f()");
      	}
      }
      class Sub extends Super {
      	public void f() {
      		System.out.println ("sub.f()");
      	}
      }
      class Goo {
      	public void g(Super obj) { 
      		System.out.println ("g(Super)");  
      		obj.f();
      	}
      	public void g(Sub obj) {
      		System.out.println ("g(Sub) "); 
      		obj.f();
      	}
      }
      class Test{
          public static void main(String[] args){
              Super obj = new Sub();
      		Goo goo = new Goo();
      		goo.g(obj);
      	}
      }
    
      首先,重載遵循所謂「編譯期綁定」,即在編譯時根據參數變量的類型判斷應該調用哪一個方法, 由於變量
       obj爲Super類型引用, 因此,Goo的g(Super)被調用,先輸出g(Super)。
      重寫遵循所謂「運行期綁定」,即在運行的時候,根據引用變量所指向的實際對象的類型來調用方法,由於
       obj實際指向的是子類Sub的對象,所以,子類重寫後的f方法被調用,即sub.f()。

訪問控制

包的概念

  • package語句

    在Java語言中,命名衝突問題是用包(package)的概念來解決的,也就是說,在定義一個類時,除了定義類的名稱通常還要指定一個包的名稱,定義包名的語法以下所示:

    package 包名;

    須要注意的是,在定義包時,package語句必須寫在Java源文件的最開始處,即在類定義以前,以下面的語句將爲Point類指定包名爲「test」:

    package test;
      	class Point{
      	    ……
      	}

    在命名包名時,包名能夠有層次結構,在一個包中能夠包含另一個包

  • import語句

    能夠經過import語句對類的全稱進行聲明,import語句的語法以下所示:

    import 類的全侷限定名(即包名+類名);

    有時,在import語句中也可使用「*」符號,例如:

    import org.whatisjava.core.*;

訪問控制修飾符

  • 封裝的意義

    假設有水果店賣水果,分兩種方式進行管理,方式一爲須要店員,由店員實現取水果、包裝、找零等功
      能。方式二爲不須要店員,由顧客自行完成取水果、包裝、找零等功能。
      那麼想想,哪種方式更適合管理呢?通常認爲方式一更適合,由於方式二沒有人來進行管理,安全性
      較低,除非來的都是活雷鋒,徹底靠自覺。而方式一的安全性更高一些,並不是任何人均可以操做水果。
    
      在軟件系統中,經常經過封裝來解決上面的問題。即:將容易變化的、具體的實現細節(賣水果)封裝起
      來,外界不可訪問,而對外提供可調用的、穩定的功能(店員),這樣的意義在於:
      1. 下降代碼出錯的可能性,更便於維護。
      2. 當內部實現細節改變時,只要保證對外的功能定義不變,其餘的模塊不須要更改。
    
      在軟件系統中,封裝經常須要依靠一些訪問控制修飾符來實現。
  • 訪問控制修飾符修飾類

    1. 對於類的修飾可使用public和默認方式。 其中,public修飾的類能夠被任何一個類使用,而默認訪問控制的類只能夠被同一個包中的類使用。
    2. 而protected和private訪問修飾符是不能夠修飾類的,但其能夠修飾內部類(後面課程詳細介紹)。
  • 訪問控制符修飾成員

    4種訪問修飾(public、private、protected、默認),均可以修飾成員,其權限以下圖所示

    權限

    public 修飾符,在任何地方均可以訪問;protected能夠在本類、同一包中的類、子類中訪問,除此以外的其它類不能夠訪問;默認方式爲能夠本類及同一包中的類訪問,除此以外其它類不能夠訪問;private只能夠在本類中訪問,其它任何類都不能夠。

static和final

static關鍵字

  • static修飾成員變量

    1. static關鍵字能夠修飾成員變量,它所修飾的成員變量不屬於對象的數據結構,而是屬於類的變量,一般經過類名來引用static成員。
    2. 當建立對象後,成員變量是存儲在堆中的,而static成員變量和類的信息一塊兒存儲在方法區, 而不是在堆中,
    3. 一個類的static成員變量只有「一份」(存儲在方法區),不管該類建立了多少對象。
  • static修飾方法

  • static塊

    static塊爲屬於類的代碼塊,在類加載期間執行的代碼塊,只執行一次,能夠用來在軟件中加載靜態資源(圖像、音頻等等)。

final關鍵字

  • final修飾變量

    final關鍵字修飾變量,意爲不可改變。final能夠修飾成員變量,也能夠修飾局部變量,當final修飾成員變量時,能夠有兩種初始化方式:

    1. 聲明同時初始化
    2. 構造函數中初始化

    final關鍵字修飾局部變量,在使用以前初始化便可。

  • final修飾方法

    final關鍵字修飾的方法不能夠被重寫。使一個方法不能被重寫的意義在於:防止子類在定義新方法時形成的「不經意」重寫。

  • final修飾類

    final關鍵字修飾的類不能夠被繼承。使一個類不能被繼承的意義在於:能夠保護類不被繼承修改,能夠控制濫用繼承對系統形成的危害。在JDK中的一些基礎類庫被定義爲final的,例如:String、Math、Integer、Double 等等。

  • static final常量

    static final 修飾的成員變量稱爲常量,必須聲明同時初始化,而且不可被改變。常量建議全部字母大寫。

    實際應用中應用率較廣,由於static final常量是在編譯期被替換的,能夠節約沒必要要的開支,以下代碼演示了static final的用法:

    class Foo {
          public static final int NUM = 100;
      }
      class Goo {
          public static void main(String[] args) {
              Sytem.out.println(Foo.NUM);  
              // 代碼編譯時,會替換爲:System.out.println(100);
          }
      }

    說明:static final常量Foo.NUM會在編譯時被替換爲其常量值(100),在運行Goo類時,Foo類不須要被載入。這樣減小了沒必要要的開支。

抽象類、接口和內部類

使用抽象類

  • 抽象方法和抽象類

    1. 由abstract修飾的方法爲抽象方法,抽象方法即只有方法的定義,沒有方法體實現,用一個分號結尾。
    2. 若將抽象方法包含在類中,則該類也應該爲抽象的,能夠理解爲,該類也不完整。抽象類由abstract關鍵字 聲明。
    3. 抽象類是不能實例化對象的,而一個類不能實例化是沒有意義的,因此,須要定義類來繼承抽象類,而若是 一個類繼承了抽象類,則其必須重寫其抽象方法(變不完整爲完整),除非該類也聲明爲抽象類。
  • 抽象類不能夠實例化

    1. 即便一個類中沒有抽象方法,也能夠將其定義爲抽象類,一樣,該類不能夠實例化。
    2. 須要注意一點:abstract和final關鍵字不能夠同時用於修飾一個類,由於final關鍵字使得類不可繼承,而abstract修飾的類若是不能夠繼承將沒有任何意義。二者放在一塊兒,會起衝突。
  • 繼承抽象類

    1. 一個類繼承抽象類後,必須實現其抽象方法,不一樣的子類能夠有不一樣的實現。
  • 抽象類的意義

    定義抽象類的意義在於:
      1. 爲其子類提供一個公共的類型(父類引用指向子類對象);
      2. 封裝子類中的重複內容(成員變量和方法);
      3. 定義有抽象方法,子類雖然有不一樣的實現,但該方法的定義是一致的。(子類須要實現此抽象方法)。

使用接口

  • 定義一個接口

    接口能夠當作是特殊的抽象類。即只包含抽象方法和常量的抽象類。能夠經過interface關鍵字來定義接口。看以下代碼:

    interface Runner { 
          public static int DEFAULT_SPEED = 100
          public void run(); 
      }

    注意,run()方法,此處能夠省略public abstract。因其默認就是public abstract的。

  • 實現接口

    1. 與繼承不一樣,一個類能夠實現多個接口,實現的接口直接用逗號分隔。固然,該類須要實現這些接口中定義的全部方法;
    2. 一個類能夠經過implements關鍵字」實現」接口。一個類實現了某個接口後必須實現該接口中定義的全部方法。
    3. 接口能夠做爲一種類型聲明變量,一個接口類型的變量能夠引用實現了該接口的類的對象;經過該變量能夠調用該接口中定義的方法(具體的實現類提供了方法的實現)。
  • 接口的繼承

    接口間能夠存在繼承關係,一個接口能夠經過extends關鍵字繼承另一個接口。子接口繼承了父接口中定義的全部方法

  • 接口和抽象類的區別

    1. 一個類只能繼承一個抽象類,但能夠實現多個接口。
    2. 抽象類中能夠包含抽象方法和非抽象方法,而接口中的全部方法均爲抽象的。
    3. 子類繼承抽象類必須實現抽象類中全部抽象方法,不然子類也必須是抽象類。而子類實現接口則必須實現接口中的全部抽象方法。

多態

  • 多態的意義

    // 前面所講解的現象就是多態,多態即多種形態,主要有兩個方面的表現。
      // 首先,一個類型的引用在指向不一樣的對象時會有不一樣的實現,看以下的代碼:
    
    
      達內職員 emp1 = new 達內講師(); 
      達內職員 emp2 = new 達內項目經理();
      emp1.完成工做();
      emp2.完成工做();
    
      // 一樣是達內職員類型,當指向不一樣的對象時,能夠有不一樣的表現。
      // 其次,一樣一個對象,造型成不一樣的類型時,會有不一樣的功能,看以下代碼所示:
    
      達內講師 teacher = new 達內講師();
      企業技術顧問 consultant = teacher;
      技術圖書做者 author = teacher;
      consultant.培訓員工();
      author.編輯稿件();
    
      // 經過上面的代碼,能夠看出,一樣的達內講師對象,當將其造型爲企業技術顧問及技術圖書做者時,能夠實現不一樣的功能。
  • 向上造型

    父類的引用指向子類的對象。這個現象就是下面要給你們介紹的現象,叫作向上造型。

    一個類的對象能夠向上造型的類型有:父類的類型及其實現的接口類型。當發生向上造型時,Java編譯器會根據類型檢查調用方法是否匹配

  • 強制轉型

    在實際應用中,還能夠經過強制轉換將父類型變量轉換爲子類型變量,前提是該變量指向的對象確實是該子類類型。也可經過強制轉換將變量轉換爲某種接口類型,前提是該變量指向的對象確實實現了該接口。若是在強制轉換過程當中出現違背上述兩個前提,將會拋出ClassCastException。

  • instanceof關鍵字

    在強制轉型中,爲了不出現ClassCastException,能夠經過instanceof關鍵字判斷某個引用指向的對象是否爲指定類型。

內部類

  • 定義成員內部類

    一個類能夠定義在另一個類的內部,定義在類內部的類稱之爲Inner,其所在的類稱之爲Outer;Inter
      定義在Outer的內部,一般只服務於Outer,對外不具有可見性,Inter能夠直接調用Outer的成員及方法(包括私有的)。
  • 建立內部類對象

    通常狀況下,Inner對象會在Outer對象中建立(構造方法或其餘方法);Inner對象中會有一個隱式的引用指向建立它的Outer類對象。

  • 定義匿名內部類

    若是在一段程序中須要建立一個類的對象(一般這個類須要實現某個接口或者繼承某個類),並且對象建立後,這個類的價值也就不存在了,這個類能夠沒必要命名,稱之爲匿名內部類。

面向對象彙總

面向對象三大特徵:封裝、繼承、多態

相關文章
相關標籤/搜索