1. 程式人生 > >Java基礎知識(四)——面向物件(下)

Java基礎知識(四)——面向物件(下)

Java基礎知識(四)——面向物件(下)

Java8的增強包裝類:

為了解決8種基本資料型別不能當成Object型別變數使用的問題。

 

JDK1.5提供了自動裝箱(Autoboxing)和自動拆箱·(AotuUnboxing)功能,所謂自動裝箱,就是可以把一個基本型別變數直接賦給對應的包裝類變數,或者Object變數(子類物件可以直接賦給父類變數)。藉助包裝類,開發者可以把基本型別的變數“近似”地當成物件來使用(所以裝箱、拆箱系統會自動完成)。

此外,包裝類還可以實現基本型別變數和字串之間的轉換。把字串的值轉換為基本型別的值有兩種方式:

利用包裝類提供的parseXxx(String s)靜態方法

利用包裝類提供的Xxx(String s)構造器

String類提供了多個過載的valueOf()方法,用於將基本型別變數轉換成字串。(如果希望把基本型別變數轉換成字串,可以將其和””連線)

 

雖然包裝型別的變數是引用資料型別,但包裝類的例項可以與數值型別的值進行比較,這種比較是直接取出包裝類例項所包裝的數值來進行比較。

兩個包裝類的例項進行比較就會複雜,因為包裝類的例項實際上是引用型別,只有兩個包裝類引用指向同一個物件時才會返回。

 

系統把一個-128~127之間的同一個整數自動裝箱成·Integer例項時,並放入了一個名為cache的陣列中快取起來。如果以後把一個-128~127之間的整數自動裝箱成一個Integer例項時,實際上是直接指向對應的陣列元素,因此

-128~127之間的同一個整數自動裝箱成Integer例項時,永遠都是引用cache陣列的同一個陣列元素,所以他們全部相等,但每次不在該範圍內的整數自動裝箱成Integer例項時,系統總是重新建立一個Integer例項。

 

toString()方法是Object類裡的一個例項方法,所有的Java類都是Object類的子類,因此所有的Java物件都具有toString()方法。

而且,所有的Java物件都可以和字串進行連線運算,當Java物件和字串經學連線運算時,系統自動·呼叫Java物件toString()方法的返回值和字串進行連線運算。

toString()方法:

返回該物件實現類的“類名

[email protected]+hashCode”值。

toString()方法通常是一個自我描述的方法,通常會(被重寫)用於實現:自我描述。返回return 類名[field=值1,field=值2];

 

==和equals()方法:

當使用==判斷兩個變數是否相等時,如果兩個變數都是基本型別,且都是數值型別,則只要兩個變數的值相等,則返回true。

但對於兩個引用型別變數,只有他們指向同一個物件時,==才會返回true。==不可用於比較型別上沒父子關係的兩個變數。

“hello”和new String(”hello”)區別:

“hello”:JVM將會使用常量池(constant pool)管理字串。

new String(”hello”):會先使用常量池(constant pool)管理字串,在呼叫String類構造器建立一個新String物件,該物件儲存在堆記憶體中。

常量池,專門用於管理在編譯時被確定並被儲存在已編譯的.class檔案中的資料。

 

equals()方法是Object類提供的一個例項方法,因此所有的引用變數都可以呼叫該方法來判斷是否與其他引用變數相等。直接使用與==沒有區別,通常(被重寫)用於自定義判斷是否相等。

 

類成員:

Java類裡只能包含成員變數、方法、構造器、初始化塊、內部類(包括介面、列舉)5種成員。其中static不能修飾構造器,被static修飾的成員就是類成員。類成員屬於整個類,而不是單個物件。

類成員不能訪問例項成員:類成員的作用域比例項成員的作用域更大,完全可能出現類成員已經初始化完成,例項成員還未初始化。

單例類:

如果一個類只能建立一個例項,則這個類被稱為單例類。

在一些特殊場景下,要求不允許自由建立該類的物件,而只允許為該類建立一個物件。為了避免其他類自由建立該類的例項,應該把該類的構造器使用private修飾,從而把該類的所有構造器隱藏起來。

根據良好封裝的原則:一旦把該類的構造器隱藏起來,就需要提供一個public方法作為該類的訪問點,用於建立該類的物件,且該方法必須使用static修飾

(因為呼叫該方法之前還不存在物件,因此呼叫該方法的不可能是物件,只能是類)。

除此之外,該類還必須快取已經建立的物件,否則該類無法知道是否曾經建立過物件,也就無法保證只建立一個物件。為此該類需要使用一個成員變數來儲存曾經建立的物件,因為該成員變數需要被上面的靜態方法訪問,固該成員變數必須使用static修飾。

 

final修飾符:

final關鍵字用於表示他修飾的類、方法和變數(類變數、例項變數、區域性變數、形參)不可改變。

 

final成員變數:

final成員變數隨類初始化或物件初始化而初始化。

final修飾的成員變數一旦有了初始值(由程式顯式的指定初始值),就不能被重新賦值。

類變數:必須在靜態初始化塊宣告該例項變數時其中之一指定初始值。

例項變數:必須在非靜態初始化塊、宣告該例項變數構造器其中之一指定初始值。

 

final區域性變數:

final區域性變數既可以在定義時指定(系統不會對其初始化,由程式顯式初始化,)預設值,也可以不指定預設值(後面程式碼中只能賦值一次)。

 

final修飾的基本變數和引用變數的區別:

基本型別變數不能重新賦值,但對於引用型別變數而言,他只是一個引用,final只保證這個引用型別變數所引的地址不會改變,即一直引用同一個物件,但這個物件完全可以發生改變。

 

可執行“巨集替換”的final變數:

對於一個final變數,只要滿足三個條件,他就不再是一個變數,而是一個直接量(巨集變數):

  1. 使用final修飾
  2. 定義final變數時指定了初始值
  3. 該初始值在編譯時就被確定下來

 

final方法:

被修飾方法不可被重寫。

Object類有一個final方法:getClass()。

使用final可以修飾一個private訪問許可權的方法,因為private方法僅在當類中可見,子類無法重寫,如果子類有一個與父類private修飾的方法有相同方法名、相同形參列表、相同返回值型別的方法,實際上是子類定義了一個全新的方法。

 

final類:

final修飾的類不可以有子類。

 

不可變(immutable)類:

建立該類的例項後,該實力的例項變數是不可改變。Java的八個包裝類和java.lang.String類都是不可變類。

自定義不可變類規則:

1.使用private和final修飾符來修飾該類的成員變數。

2.提供帶引數構造器,用於根據傳入引數來初始化類裡的成員變數。

3.僅為該類的成員變數提供getter方法,不要為該類的成員變數提供setter方法,因為普通方法無法修改final修飾的成員變數。

4.如果有必要,重寫Object類的hashCode()和equals()方法。equals()方法根據關鍵成員變數來作為兩個物件是否相等的標準,此外,還應該保證兩個用equals()方法判斷為相等的物件的hashCode()也相等。

 

快取例項的不可變類:

不可變類的例項狀態不可改變,可以很方便的被多個物件所共享。如果程式經常使用相同的不可變例項,則應該考慮快取這種不可變類例項。

 

 

抽象類:

抽象方法只是有方法簽名,沒有方法實現的方法。

 

抽象方法和抽象類:

抽象方法和抽象類必須使用abstract修飾符定義,有抽象方法的類只能被定義成抽象類,抽象類裡可以沒有抽象方法。規則:

  1. 抽象類必須使用abstract修飾符來修飾,抽象方法也必須使用abstract修飾符來修飾,抽象方法不能有方法體
  2. 抽象類不能被例項化,無法使用new關鍵字來呼叫抽象類的構造器建立抽象類的例項。即使抽象類裡不包含抽象方法,這個抽象類也不能創造例項、
  3. 抽象類可以包含成員變數、方法、構造器、初始化塊、內部類(介面、列舉)5種成分。抽象類的構造器不能用於建立例項,主要適用於被其子類呼叫。
  4. 含有抽象方法的類(包括直接定義了一個抽象方法;或繼承了一個抽象父類,但沒有完全實現父類包含的抽象方法;或實現了一個介面,但沒有完全實現介面包含的抽象方法三種情況)只能被定義成抽象類。

抽象類相比普通類:可以包含抽象方法,但不能用於建立例項。

抽象類只需在普通類上增加abstract修飾符即可。甚至普通類(不含抽象方法的類)增加abstract修飾符後會變成抽象類。

抽象方法和空方法不是同一概念:

public abstract void test();

public void test(){}

當使用abstract修飾類時,表明該類只能被繼承;當使用abstract修飾方法時,表明該方法必須由子類提供實現(即重寫)。而final修飾類不能被繼承,final修飾的方法不能被重寫,因此final和abstract不能一起使用。

static和abstract雖然不能同時修飾某個方法,但可以同時修飾內部類。

private和abstract不能同時使用,因為abstract必須被重寫才有意義。

 

抽象類的作用:

抽象類不能建立例項,只能當成父類來被繼承。抽象類體現的就是一種模板模式的設計,抽象類作為多個子類的通用模板,子類在抽象類的基礎上進行擴充套件、改造,但子類總體上會大致保留抽象類的行為方式。

 

 

Java8改進的介面:

抽象類是從多個類中抽象出來的模板,如果將這種抽象進行的更徹底,則可以提煉出一種更加特殊的“抽象類”——介面(interface)。

接口裡不能包含普通方法,接口裡所有方法都是抽象方法。Java8對介面進行了改進,允許在介面中定義預設方法,預設方法可以提供方法實現。

介面定義的是多個類的公共行為規範,這些行為是與外部交流的通道,這就意味著接口裡通常是定義一組公用方法。

 

[修飾符] interface 介面名 extends 父介面1, 父介面2{

}

修飾符:可以使public或省略,預設許可權即只有在相同包結構下才可以被訪問。

由於介面定義的是一種規範,因此接口裡不能包含構造器和初始塊定義。

接口裡的成員變數只能是靜態變數,方法只能是抽象方法,類方法和預設方法。

接口裡的所有成員可以省略訪問修飾符,如果指定訪問修飾符,則只能使用public訪問控制修飾符。

接口裡定義的靜態常量不管是否使用public static final修飾符,系統會自動給其修飾,而且靜態變數只能在定義時賦值。

接口裡定義的方法,如果不是預設方法,系統將自動為普通方法增加abstract修飾符;定義普通方法時不管是否使用public abstract修飾,系統自動為其修飾。普通方法不能有實現(方法體);但類方法、預設方法都必須有方法實現。

接口裡定義的內部類、內部介面、內部列舉不管是否有public static修飾,系統都自動為其修飾。

介面中允許定義預設方法(必須用default修飾),該方法不能用static修飾,預設方法如果沒有被public修飾,系統會自動為其修飾。由於預設方法沒有被static修飾,因此不能直接使用介面來呼叫預設方法,需要使用介面的實現類的例項來呼叫。

 

介面的繼承:

介面完全支援多繼承,即一個介面可以有多個直接父介面。

 

使用介面:

介面不能用於建立例項,但介面可以用於宣告引用變數型別。當使用來宣告引用變數時,這個引用變數必須引用到其實現類的物件。除此之外,介面的主要用途就是被實現類實現。歸納起來,用途如下:

1.定義變數,也可用於進行強制型別轉換。

2.呼叫介面中定義的常量。

3.被其他類實現。

一個類可以實現一個或多個介面,繼承使用extends關鍵字,實現則使用implements關鍵字。

[修飾符] class 類名 extends 父類 implements 介面1, 介面2…{

}

一個類實現了一個或多個介面之後,這個類必須完全實現這些接口裡所定義的全部抽象方法(重寫這些抽象方法);否則,該類將保留從父介面那裡繼承到的抽象方法,則該類必須被定義成抽象類。

可以把實現介面理解成實現一種特殊的繼承,相當於實現類繼承了一個徹底抽象的類(除預設方法外,所有方法都是抽象方法的類)。

實現介面方法時,必須使用public訪問控制修飾符,因為接口裡的方法都是public,而子類(實現類)重寫父類方法時,訪問許可權只能更大或相等。

 

介面不能顯式繼承任何類,但所有介面型別的引用變數都可以直接賦給Object型別的引用變數。

 

介面和抽象類:

相同點:

  1. 1.   都不能被例項化,他們都位於繼承樹頂端,用於被其他類實現和繼承。
  2. 2.   都可以包含抽象方法,實現介面類或繼承抽象類的子類都必須全部實現這些抽象方法。

不同點:

  1. 1.   介面只能包含抽象方法和預設方法,不能為普通方法提供方法實現;抽象類

則完全可以包含普通方法。

  1. 2.   接口裡不能定義靜態方法;抽象類裡可以定義靜態方法。
  2. 3.   接口裡只能定義靜態常量,不能定義普通成員變數;抽象類裡既能定義靜態常量,也能定義普通成員變數。
  3. 4.   接口裡不包含構造器;抽象類可以包含構造器,抽象類裡的構造器並不是用於建立物件,而是讓其子類呼叫這些構造起來完成屬於抽象類的初始化操作。
  4. 5.   接口裡不能包含初始化塊;抽象類可以包含初始化塊。
  5. 6.   一個類最多隻有一個父類,包括抽象類;但是一個類可以有多個父介面。

 

 

面向介面程式設計:

簡單工廠模式,命令模式。

 

 

 

內部類:

大部分時候,類被定義為一個獨立的程式單元在某些情況下,也會把一個類放在

另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類(巢狀類),包含內部類的類也叫外部類(宿主類)。

1.內部類提供更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類。假設需要建立Cow類,Cow類需要組合一個CowLeg物件,CowLeg類只有在Cow類才有效,離開Ciw類之後沒有任何意義。在這種情況下就可把CowLeg定義成Cow的內部類,不允許其他類訪問CowLeg。

2.內部類成員可以直接訪問外部類的私有資料,因為內部類被當成其他外部類成員,同一個類的成員之間可以互相訪問。但外部類不能訪問內部類的細節,如內部類的成員變數。

3.匿名內部類適合用於建立那些僅需要一次使用的類。

從語法角度來看定義內部類和定義外部類語法大致相同,內部類除了需要定義在其他類裡面之外還需要:

  1. 內部類比外部類可以多使用三個修飾符:private、protected、static。
  2. 非靜態內部內不能擁有靜態成員。

 

大部分時候,內部類都被作為成員內部類定義,而不是區域性內部類。成員內部類是一種與成員變數、方法、構造器、初始化塊相似的類成員;區域性內部類和匿名內部類不是類成員。

外部類的上一級程式單元是包,所以他只有兩個作用域:同一個包內和任何位置,因此只需要兩種訪問許可權:包訪問許可權和公開訪問許可權,正好對應省略訪問控制符和public訪問控制符。省略訪問控制符是包訪問許可權,即同一包中的其他類可以訪問省略訪問控制符的成員。因此,如果一個外部類不使用任何訪問控制修飾符,則只能被同一個包中其他類訪問。而內部類的上一級程式單元是外部類,他就具有4個作用域:同一個類、同一個包、父子類和任何位置,因此可以使用4種訪問控制權限。

成員內部內分為兩種,靜態內部類和非靜態內部類: