天天看點

【java基礎】——java面向對象(下)—多态、内部類、異常、包

一、面向對象——多态

1、多态概述

①什麼是多态?某一事物在不同時刻表現出來的不同狀态。

如:貓可以是貓的類型。貓 m = new貓();同時貓也是動物的一種,也可以把貓稱為動物。

②多态的好處:提高了代碼的可擴充性,前期定義的代碼可以使用後期的内容。

③多态的弊端:前期定義的内容不能使用後期子類的特有功能。

④多态的前提:

  • 必須要有關系:繼承,實作。
  • 有覆寫。
  • 有父類引用指向子類對象。

2、多态時成員的特點

①成員變量:編譯時,參考引用型變量所屬的類中是否有調用的成員變量,有則編譯通過,沒有則編譯失敗。運作時,參考引用型變量所屬的類是否有調用的成員變量,并運作該所屬類中的成員變量。簡單的說,編譯和運作都參考等号的左邊。

②成員函數(非靜态):編譯時,參考引用型變量所屬的類中是否有調用的函數,有則編譯通過,沒有則編譯失敗。運作時,參考的是對象所屬的類是否有調用的函數,簡單的說,編譯看左邊,運作看右邊。

③靜态函數:簡單說,編譯和運作都看做左邊。其實對于靜态方法,是不需要對象的。直接用類名調用即可。

3、多态中的轉型問題

①向上轉型:從子到父,父類引用指向子類對象。

②向下轉型:從父到子,父類引用轉為子類對象。

在多台向下轉型中容易出現的問題,代碼示範如下:

/*
	ClassCastException:類型轉換異常
	一般在多态的向下轉型中容易出現
*/
class Animal {
	public void eat(){}
}

class Dog extends Animal {
	public void eat() {}
	
	public void lookDoor() {
	
	}
}

class Cat extends Animal {
	public void eat() {
	
	}
	
	public void playGame() {
		
	}
}

class DuoTaiDemo5 {
	public static void main(String[] args) {
		//記憶體中的是狗
		Animal a = new Dog();
		Dog d = (Dog)a;
		
		//記憶體中是貓
		a = new Cat();
		Cat c = (Cat)a;
		
		//記憶體中是貓
		Dog dd = (Dog)a; //ClassCastException
	}
}
           

二、面向對象——内部類

1、内部類(inner class)概述

①概念:内部類是指定義在另一個類中的類。當描述事物時,事物的内部還有事物,該事物就用内部類來描述。因為内部事物在使用外部事物的内容。

②内部類通路的特點:

  • 内部類可以直接通路外部類中的成員。
  • 外部類需要通路内部類,必須建立内部類的對象
  • 如果内部類中有靜态成員,那麼外部類也必須是靜态的。

2、内部類的分類

按照内部類在類中定義的位置的不同,可以分為如下兩種格式:

  • 成員位置——成員内部類
  • 局部位置——局部内部類

①成員内部類

a、外界如何建立對象?外部類名.内部類名  對象名 = 外部類對象.内部類對象;

b、成員内部類常見的修飾符

  • private 為了保證資料的安全性
  • static 為了讓資料通路更友善。被靜态修飾的成員内部類隻能通路外部的靜态成員。

内部類被靜态修飾後的方法有靜态和非靜态之分,它們的通路和不用靜态不是一樣的。

通路非靜态方法:外部類名.内部類名對象名 = new 外部類名.内部類名();

通路靜态方法:上面建立的對象通路,或者外部類名.内部類名.方法名();

成員内部類面試題:

class Outer {
	public int num = 10;
	
	class Inner {
		public int num = 20;
		
		public void show() {
			int num = 30;
			System.out.println(num);//30
			System.out.println(this.num);//20
			System.out.println(Outer.this.num);//10
		}
	}
}

class OuterDemo {
	public static void main(String[] args) {
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	} 
}
           

②局部内部類

a、局部内部類可以直接通路外部類的成員。

b、可以建立内部類對象,通過對象調用内部類方法,來使用局部内部類功能。

c、局部内部類通路局部變量的注意事項:

  • 必須被final修飾。為什麼?因為局部變量會随着方法的調用完畢而消失,這個時候,局部對象并沒有立馬從堆記憶體中消失,還要使用那個變量。為了讓資料還能繼續被使用,就用fianl修飾,這樣,在堆記憶體裡面存儲的其實是一個常量值。

如下面的代碼所示:

class Outer
{
	int x = 3;
	void method(final int a)
	{
		final int y = 4;
		//局部内部類
                class Inner
		{
			void function()
			{
				System.out.println(y);
			}
		}
		new Inner().function();//使用局部内部類中的方法。
	}
}
class  InnerClassDemo
{
	public static void main(String[] args) 
	{
		Outer out = new Outer();
		out.method(7);//列印7
		out.method(8);//列印8
	}
}
           

③匿名内部類

a、什麼是匿名内部類?匿名内部類其實就是内部類的簡寫格式。

b、匿名内部類的前提:存在一個類或接口。

c、匿名内部類的格式:new 類名或者接口名(){重寫方法}。

d、匿名内部類的本質:是一個繼承了類或者實作了接口的子類匿名對象。

e、匿名内部類的通常使用場景之一:當函數參數是接口類型時,而且接口中的方法不超過三個,可以使用匿名内部類作為實際參數進行傳遞。

匿名内部類在開發中的使用代碼執行個體:

/*
	匿名内部類在開發中的使用
*/
interface Person {
	public abstract void study();
}

class PersonDemo {
	//接口名作為形式參數
	//其實這裡需要的不是接口,而是該接口的實作類的對象
	public void method(Person p) {
		p.study();
	}
}

//實作類
class Student implements Person {
	public void study() {
		System.out.println("好好學習,天天向上");
	}
}

class InnerClassTest2 {
	public static void main(String[] args) {
		//測試
		PersonDemo pd = new PersonDemo();
		Person p = new Student();
		pd.method(p);
		System.out.println("--------------------");
		
		//匿名内部類在開發中的使用
		//匿名内部類的本質是繼承類或者實作了接口的子類匿名對象
		pd.method(new Person(){
			public void study() {
				System.out.println("好好學習,天天向上");
			}
		});
	}
}
           

匿名内部類面試題:按照要求補齊代碼 。

interface Inter { void show(); }
	class Outer { //補齊代碼 }
	class OuterDemo {
	    public static void main(String[] args) {
		      Outer.method().show();
		  }
	}
要求在控制台輸出”HelloWorld”
           

題目分析:Outer.method.show();相當于Inter.in = Outer.method();  in.show();

                  Outer.method():Outer類中有個靜态的方法method。

                  .show():method這個方法運算後的結果是一個對象。而且是一個Inter類型的對象。因為隻有Inter類型的對象,才能調用show方法。

是以,補齊後的代碼為:

/*
	匿名内部類面試題:
		按照要求,補齊代碼
			interface Inter { void show(); }
			class Outer { //補齊代碼 }
			class OuterDemo {
				public static void main(String[] args) {
					  Outer.method().show();
				  }
			}
			要求在控制台輸出”HelloWorld”
*/
interface Inter { 
	void show(); 
	//public abstract
}

class Outer { 
	//補齊代碼
	public static Inter method() {
		//子類對象 -- 子類匿名對象
		return new Inter() {
			public void show() {
				System.out.println("HelloWorld");
			}
		};
	}
}

class OuterDemo {
	public static void main(String[] args) {
		Outer.method().show();
		/*
			1:Outer.method()可以看出method()應該是Outer中的一個靜态方法。
			2:Outer.method().show()可以看出method()方法的傳回值是一個對象。
				又由于接口Inter中有一個show()方法,是以我認為method()方法的傳回值類型是一個接口。
		*/
	}
}
           

三、面向對象——異常

1、異常的概述

①什麼是異常?

a、異常是指在運作時期發生的不正常情況。在java中用類的形式對不正常情況進行了描述和封裝,描述不正常情況的類,就是異常類。

b、在以往的正常情況,流程代碼和問題處理代碼相結合。現在将正常流程代碼和問題處理代碼相分離,提高閱讀性。

c、其實異常就是java通過面向對象的思想将問題封裝成了對象,用異常類對其進行描述,不同的問題用不同的類進行具體描述。

②異常的由來

在理想狀況下,使用者輸入的資料永遠是正确的,打開的檔案一定存在,并且永遠不會出現bug。但是,在現實中我們往往會遇到許多的問題。如果一個使用者在運作程式期間,由于程式的錯誤或一些外部環境的影響造成資料的丢失,使用者就有可能不再使用這個程式了,為了避免這類事情的發生,至少應該做到以下幾點:

  • 向使用者通報錯誤。
  • 儲存所有的操作結果。
  • 允許使用者以适當的形式退出程式。

2、異常體系

對于程式出現的問題,可能會有很多種,這就意味着描述的類也很多,将其共性進行向上抽取,就形成了異常體系。

異常體系(Throwable):無論是error還是異常,都應該抛出,讓調用者知道并處理。該體系的優點就在于Throwable及其所有的子類都具有可抛性。可抛性是通過兩個關鍵字來展現的,throw和throws,凡是可以被這兩個關鍵字操作的類和對象都具有可抛性。

最終就将問題分成了兩大類:

①error:一般不可處理。java運作時系統的内部錯誤和資源耗盡錯誤。是由jvn抛出的嚴重性問題,這種問題一般不針對性處理,直接修改程式。

②Exception:可處理的。Exception體系。

a、該體系的特點:子類的字尾都是用其父類名作為字尾,閱讀性很強。

b、在java中沒有定義的異常就按照java異常的建立思想,面向對象,進行自定義描述,并封裝成對象。

注意:如果一個類稱為異常類,則必須要繼承異常體系,因為隻要繼承異常體系的子類才具有可抛性。才可以被兩個關鍵字操作。

異常體系如下圖所示:

【java基礎】——java面向對象(下)—多态、内部類、異常、包

3、異常的分類

①異常通常分為兩種,一種是編譯時被檢測異常,一種是編譯時不被檢測異常(運作時異常)。

  • 編譯時被檢測異常:隻要是Exception及其子類都是,除了RuntimeException體系。這種問題一旦出現,希望在編譯時就進行檢測,讓這種問題有對應的處理。
  • 編譯時不檢測異常(運作時異常):就是Exception中的RuntimeException及其子類。這種問題的發生,無法讓功能繼續,運算無法進行,更多的是因為調用者的原因導緻的,或者是引發了内部狀态的改變而導緻的,那麼這種問題一般不處理,直接編譯通過,在運作時,讓調用者調用時的程式強行停止,讓調用者修改程式代碼。“如果出現RuntimeException,那麼就一定是你的問題”。

4、異常的處理

①異常處理機制

在java應用程式中,異常處理機制為:抛出異常,捕獲異常。

  • 抛出異常:當一個方法出現錯誤引發異常時,方法建立異常對象并傳遞運作時系統,異常對象中包含了異常類型和異常出現時的程式狀态等異常資訊。運作時系統負責尋找處置異常的代碼并執行。
  • 捕獲異常:在方法抛出異常之後,運作時系統将轉為尋找合适的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法抛出的異常類型相符時,即為合适 的異常處理器。運作時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合适異常處理器的方法并執行。當運作時系統周遊調用棧而未找到合适 的異常處理器,則運作時系統終止。同時,意味着Java程式的終止。

②throws和throw的差別

  • throws使用在函數上,throw使用在函數内。
  • throws抛出的是異常類,可以抛出多個,用逗号隔開;throw抛出的是異常對象。

③在java中,提供了特有的處理異常的方式,格式如下:

try

{

    需要被檢測的代碼;

}

catch(異常類 變量)

{

    處理異常的代碼;(處理方式)

}

finally

{

    一定會執行的語句;

}

除此之外還有兩種格式,一種是沒有finally代碼塊,一種是沒有catch代碼塊。

注意:在finally中定義的通常是關閉資源代碼,因為資源必須釋放。finally隻有一種情況不會執行:當系統執行到System.exit的時候。

④自定義異常

定義類繼承Exception或者RuntimeException。目的有兩個:一是為了讓該自定義類具有可抛性,二是為了讓該類具備操作異常的共性方法。

當要定義自定義異常資訊時,可以使用父類已經定義好的功能。異常資訊傳遞給父類的構造函數。代碼執行個體如下:

class MyException extends Exception{

	MyException(String message){

		super(message);
	}
}
           

自定義異常:按照java面向對象的思想,将程式中出現的特殊問題進行封裝。

⑤異常處理原則:

  • 函數内容如果要抛出需要檢測的異常,那麼函數上必須要有聲明,否則必須在函數内用try  catch進行捕捉,否則編譯失敗。
  • 如果調用到了聲明異常的函數,要麼try catch,要麼throws,否則編譯失敗。
  • 什麼時候try,什麼時候throws呢?功能内容可以解決就用try…catch;解決不了,用throws告訴調用者,由調用者解決。
  • 一個功能如果抛出了多個異常,那麼調用時,必須有對應的多個catch進行針對性的處理,内部有幾個需要檢測的異常,就抛幾個,抛出幾個,就catch幾個。

⑥異常處理注意事項

a、子類在覆寫父類方法時,父類如果抛出了異常,那麼子類的方法隻能抛出父類的異常或者該異常的子類。

b、如果父類抛出了多個異常,呢嗎子類隻能抛出父類異常的子集。簡單的說,子類覆寫父類的異常或者子類或子集。

注意:如果父類的方法沒有抛出異常,那麼子類的方法絕對不能抛出異常。隻能進行捕獲處理。

⑦異常實際應用中的應用和總結

a、在處理運作時異常時,采用邏輯去合理規避同時輔助try—catch處理。

b、在多重catch塊後面。可以加上一個catch(Exception)來處理可能被一樓的異常。

c、對于不确定的代碼,也可以加上try—catch處理潛在的異常。

d、盡量去處理異常,切忌隻是簡單的調用printstack.Trance()去列印輸出。

e、具體如何處理異常,要根據不同的業務需求和異常類型來決定。

f、盡量添加finally代碼塊去釋放占用的資源。

⑧異常綜合練習代碼示範:

/*
畢老師用電腦上課。

開始思考上課中出現的問題。

比如問題是
	電腦藍屏。
	電腦冒煙。
要對問題進行描述,封裝成對象。

可是當冒煙發生後,出現講課進度無法繼續。

出現了講師的問題:課時計劃無法完成。
*/
//自定義電腦藍屏異常
class LanPingException extends Exception{
	LanPingException(String message){
		super(message);
	}
}
//自定義電腦冒煙異常
class MaoYanException extends Exception{
	MaoYanException(String message){
		super(message);
	}
}

//出現了問題,無法上課,課時計劃無法完成異常
class NoPlanException extends Exception{
	NoPlanException(String msg){
		super(msg);
	}
}

class Computer{
	private int state = 3;
	//電腦啟動
	public void run()throws LanPingException,MaoYanException{
		if(state==2)
			throw new LanPingException("藍屏了");
		if(state==3)
			throw new MaoYanException("冒煙了");

		System.out.println("電腦運作");
	}
	//電腦重新開機
	public void reset(){
		state = 1;
		System.out.println("電腦重新開機");
		
	}
}

class Teacher{
	private String name;
	private Computer cmpt;
	//對老師進行初始化
	Teacher(String name){
		this.name = name;
		cmpt = new Computer();
	}
//老師講課
	public void prelect()throws NoPlanException{
		try{
			cmpt.run();			
		}
		catch (LanPingException e){
			cmpt.reset();
		}
		catch (MaoYanException e){
			test();
			throw new NoPlanException("課時無法繼續"+e.getMessage());	
		}
		System.out.println("講課");
	}
	public void test(){
		System.out.println("練習");
	}

}

class ExceptionTest {
	public static void main(String[] args) {
		Teacher t = new Teacher("畢老師");
		try{
			t.prelect();
		}
		catch (NoPlanException e){
			
			System.out.println(e.toString());
			System.out.println("換老師或者放假");
		}
		
	}
}
           

四、面向對象——包(package)

1、什麼是包?

包其實就是檔案夾。在包中,我們通常存放的是類檔案,因為我們在編寫程式時,難免會出現類名相同的情況。為了友善于對類進行管理,java中就有了包的出現,在不同的包中可以有相同的類名,調用的時候連同包名一起調用即可。包也是一種封裝形式。

報名的書寫規則:

  • 包名必須寫在程式的第一行。
  • 類檔案的全稱:包名.類名。

2、包的作用

①對子類檔案進行分類管理。将一些相關的類放在同一個包中。

②給類提供多層命名(名稱)空間。

③避免多個類重命名,對于相同的類名,可通過包名将兩者區分。

④包的出現可以将java 的類檔案與源檔案相分離。

3、包與包之間的通路

①要通路其它包中的類,需要定義類的全稱。包名.類名。

②包如果不在目前路徑,需要使用classpath設定環境變量,為JVM指明路徑。

③被通路的包中的類權限必須是public的。被通路的包中的類的方法也必須是public的。

4、四種權限修飾詞

public protected default private
同一類中 ok ok ok ok
同一包中 ok ok ok
子類中 ok ok
不同包中 ok

5、import(導包)

①導包的原則:用到哪個類,就導哪個類,不要多導。(盡管多導也能運作)

②導包的作用:

  • 可以簡化書寫。
  • 一個程式檔案中隻有一個package,但可以有多個import。

③導包的注意事項:

  • 在導包時,用到哪個寫哪個,盡量不要import pack.*;一般是import pack.Demo;可以節省記憶體空間,也能提高點效率。
  • 定義包名不要重複。一般我們定義包名是用url的倒寫形式。如:package  cn.itheima.Demo
  • 定義包名時,包名都是小寫字母。

繼續閱讀