天天看點

黑馬程式員——Java語言--面向對象(二)多态

———–android教育訓練、java教育訓練、java學習型技術部落格、期待與您交流!————

繼承(上)

好處:

  1. 提高了代碼的複用性。
  2. 讓類與類之間産生了關系,給第三個特征多态提供了前提。

上個Demo和圖欣賞一下:

class Person 
{
	String name;
	int age;
}

class Student extends/*繼承*/ Person
{
	void study()
	{
		System.out.println(name+"..Student Study.."+age);
	}
}

class Worker extends/*繼承*/ Person
{
	void work()
	{
		System.out.println(name+"..Worker Work.."+age);
	}
}

public class ExtendsDemo
{
	public static void main(String[] args)
	{
		Student s = new Student();
		s.name = "xiaoming";
		s.age = 20;
		s.study();
		Worker w = new Worker();
		w.name = "aaaa";
		w.age = 31;
		w.work();
	}
}
           
黑馬程式員——Java語言--面向對象(二)多态

Java中支援單繼承,不直接支援多繼承,但對C++中的多繼承進行了改良。

單繼承:一個子類隻能有一個直接父類。

多繼承:一個子類可以有多個直接父類(Java中不允許,進行改良,不直接支援,因為多個父類有相同成員,會産生調用不确定性,在java中通過“多實作”解決這問題)。

Java支援多層(多重)繼承。比如,C繼承B,B繼承A。就會出現繼承體系。

當要使用一個繼承體系時:

  1. 檢視該體系中的頂層類,了解該體系的基本功能。
  2. 建立體系中的最子類對象,完成功能的使用。

什麼時候使用繼承呢?

當類與類之間存在所屬關系的時候,就定義繼承。比如,xxx是yyy的一種。那麼xxx extends yyy。所屬關系就是 is a 關系。

子父類中成員的特點展現:

1、成員變量。

  1. 當本類的成員和局部變量同名的時候,使用this區分。this:代表一個本類對象的引用。
  2. 當子父類中的成員變量相同的時候,使用super關鍵字進行區分。super:代表一個父類空間。
  3. this和super的用法很相似。
  4. class DemoA 
    {
    	int num = 5;
    }
    
    class DemoB extends DemoA
    {
    	int num = 6;
    
    	void show()
    	{
    		System.out.println(this.num+"..."+super.num); // 6...5
    	}
    
    }
               

2、成員函數。

當子父類中出現成員函數一模一樣的情況,會運作子類的函數。

這種現象,稱為覆寫操作。這是函數在子父類中的特性。

函數兩個特性:

  1. 重載。同一個類中。overload
  2. 覆寫。子類中。覆寫也稱為重寫,覆寫。override

覆寫注意事項:

  1. 子類方法覆寫父類方法時,子類方法權限必須大于等于父類的。
  2. 靜态隻能覆寫靜态的。
class Fu
{
	void show()
	{
		System.out.println("Fu show run");
	}
}

class Zi extends Fu
{
	// 覆寫,這裡的權限還可以是public
	void show()
	{
		System.out.println("Zi show run");
	}
}

class  ExtendsDemo2
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show(); // Zi show run
	}
}
           

什麼時候使用覆寫操作呢?

當對一個類進行子類的擴充時,子類需要保留父類的功能聲明,但是要定義子類中該功能的特有内容時,就使用覆寫操作完成。

class Phone
{
	void call(){}

	void show()
	{
		System.out.println("number");
	}
}

class NewPhone extends Phone
{
	void show()
	{
		System.out.println("name");
		System.out.println("piv");
		super.show();
	}
}
           

3、構造函數。

子父類中的構造函數的特點。

1)、在子類構造對象時,發現通路子類構造函數時,父類的構造函數也運作了。

為什麼呢?

原因是:在子類的構造函數中第一行有一個預設的隐式語句:super();。

子類的執行個體化過程:子類中所有的構造函數預設都會通路父類中的空參數的構造函數。

2)、為什麼子類執行個體化的時候要通路父類中的構造函數呢?

那是因為子類繼承了父類,擷取到了父類中的内容(屬性),是以在使用父類内容之前,要先看父類是如何對自己的内容進行初始化的。

是以子類在構造對象時,必須通路父類中的構造函數。

為什麼完成這個必須的動作,就在子類的構造函數中加入super();語句。

如果父類中沒有定義空參數構造函數,那麼子類的構造函數必須用super明确要調用父類中的哪個構造函數。同時子類構造函數中如果使用了this調用本類的構造函數時,那麼super就沒有了。因為super和this都必須定義在第一行。是以隻能有一個。但是可以保證子類中肯定會有其他構造函數通路父類的構造函數。

注意:super語句必須要定義在子類構造函數的第一行,因為父類的初始化動作要先完成。

Demo:

class Fu
{
	int num;
	Fu()
	{
		num = 10;
		System.out.println("A fu run" + num);
	}

	Fu(int x)
	{
		System.out.println("B fu run" + x);
	}

}

class Zi extends Fu
{
	Zi()
	{
		// super(); // 預設調用的就是父類中的空參數構造函數
		System.out.println("C zi run");
	}

	Zi(int x)
	{
		// 預設是super();但也可以指明具體的父類構造函數(如果沒有空參數的構造函數,就必須指明具體的)
		super(x); 
		System.out.println("D zi run" + x);
	}
}

class ExtendsDemo3 
{
	public static void main(String[] args) 
	{
		new Zi();
		System.out.println("----------");
		new Zi(6);
	}
}
           
黑馬程式員——Java語言--面向對象(二)多态

一個對象執行個體化過程:

  1. Person p = new Person();
  2. JVM會讀取指定的路徑下的Person.class檔案,并加載進記憶體,并會先加載Person的父類(如果有直接的父類的情況下);
  3. 在堆記憶體中開辟空間,配置設定位址;
  4. 并在對象空間中,對對象中的屬性進行預設初始化;
  5. 調用對應的構造函數進行初始化;
  6. 在構造函數中,第一行會先到調用父類中的構造函數進行初始化;
  7. 父類的初始化完畢之後,再對子類的屬性進行顯式初始化;
  8. 在進行子類構造函數的特定初始化;
  9. 初始化完畢後,将位址值指派給引用變量。

final關鍵字

  • final是一個修飾符,可以修飾類,方法,變量
  • final修飾的類不可以被繼承,因為被final修飾了的類成為最終類
  • final修飾的方法不可以被覆寫
  • final修飾的變量是一個常量。隻能被指派一次 如:final double PI = 3.14159;
  • 内部類隻能通路被final修飾的局部變量

為什麼要用final修飾變量?其實在程式如果一個資料是固定的,那麼直接使用這個資料可以了,但是這樣閱讀性差,是以給該資料起個名稱。

而且這個變量名稱的值不能變化,是以加上final固定。寫法規範:常量所有字母都大寫,多個單詞,中間用下劃線(_)連接配接。

繼承(下)

抽象類:

抽象:籠統,模糊,看不懂!不具體!

特點:

  1. 方法隻有聲明沒有實作時,該方法就是抽象方法,需要被abstract修飾。抽象方法必須定義在抽象類中,該類必須被abstract修飾。
  2. 抽象類不可以被執行個體化。為什麼?因為調用抽象方法沒意思,沒意義。
  3. 抽象類必須有其子類覆寫所有的抽象方法後,該子類才可以執行個體化。否則,這個子類還是抽象類。

Demo:

abstract /*抽象,修飾符*/ class Demo 
{
	abstract void show();
}

class DemoA extends Demo
{
	void show()
	{
		System.out.println("DemoA run");
	}
}

class DemoB extends Demo
{
	void show()
	{
		System.out.println("DemoB run");
	}
}
           

關于抽象類的五個問題:

1、抽象類中有構造函數嗎?

      有,用于給子類對象進行初始化。

2、抽象類可以不定義抽象方法嗎?

      可以的。但是很少見,目的就是不讓改了建立對象。AWT的擴充卡對象就是這種類。

      通常這個類中的方法有方法體,但是卻沒有内容。

      abstract class Demo

      {

            void show1(){ }

            void show2(){ }

      }

3、抽象關鍵字不可以和哪些關鍵字共存?

  • private 因為子類要覆寫抽象方法,私有化的話,那就覆寫不了了
  • static   因為不需要對象就能類名調用抽象方法,抽象方法運作沒意義,是以static和abstract不能共存
  • final     很簡單,final修飾的類是最終類,不被繼承;final修飾的方法,不能被覆寫

4、抽象類和一般類的差別。

       相同點:

           抽象類和一般類都是用來描述事物的,都在内部定了成員。

       不同點:

           1、一般類有足夠的資訊描述事物;抽象類描述事物的資訊有可能不足。

           2、一般類中不可以定義抽象方法,隻能定義非抽象方法;抽象類既然定義抽象方法,也可以定義非抽象方法。

           3、一般類可以被執行個體化;抽象類不可以被執行個體化。

5、抽象類一定是一個父類嗎?

      是的。因為需要子類覆寫其方法後才可以對子類執行個體化。

練習:

/*
雇員示例:
需求:公司中程式員有姓名,工号,薪水,工作内容。
      項目經理除了有姓名,工号,薪水,還有獎金,工作内容。
	  對給出需求進行資料模組化。

分析:
在這個問題領域中,先找出涉及的對象。通過名詞提煉法。
程式員:
    屬性:姓名,工号,薪水。
	行為:工作。

經理:
	屬性:姓名工号,薪水,獎金。
	行為:工作。

程式員和項目經理不存在着直接繼承關系,但是程式員和經理卻又具有共性内容。
可以進行抽取,因為他們都是公司的雇員,可以将程式員和經理進行抽取,建立體系。
*/

// 描述雇員
class Employee
{
	private String name;
	private String id;
	private double pay;

	Employee(String name, Stirng id, double pay)
	{
		this.name = name;
		this.id = id;
		this.pay = pay;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public String getName()
	{
		return name;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getId()
	{
		return id;
	}

	public void setPay(double pay)
	{
		this.pay = pay;
	}

	public double getPay()
	{
		return pay;
	}
}

// 描述程式員
class Programmer extends Employee
{
	Programmer(String name, String id, String pay)
	{
		super(name, id, pay);
	}
}

// 描述項目經理
class Manager extends Employee
{
	private double bonus; // 獎金
	Manager(String name, String id, double pay, double bonus)
	{
		super(name, id, pay);
		this.bonus = bonus;
	}

	public void setBonus(double bonus)
	{
		this.bonus = bonus;
	}

	public double getBonus()
	{
		return bonus;
	}
}
           

接口

定義:

當一個抽象類中的方法都是抽象的時候,這是可以将抽象類用另一種形式定義和表示,就是接口——interface。定義接口使用的關鍵字不是class,而是interface.

對于接口當中常見的成員:而且這些成員都有固定的修飾符。

  1. 全局變量:public static final
  2. 抽象方法:public abstract

簡單小例子:

interface Demo

{

      public static final int NUM = 4;

      public abstract void show1();

      public abstract void show2();

}

接口的特點:

  • 接口是對外暴露的規則。
  • 接口是程式的功能擴充。
  • 接口的出現降低耦合性。
  • 接口可以用來多實作。
  • 類與接口之間的實作關系,而且類可以繼承一個類的同時實作多個接口。(下面有例子)
  • 接口與接口之間可以有繼承關系。(支援多繼承)

注意:其中全局變量的public static final和抽象方法的public abstract可以省略不寫,底層會自動加上,但是省略不寫的話,閱讀性極差,有可能誤導。是以建議不要省略。

另外,得出一個結論,接口的成員都是公共的權限。

作用:接口的出現避免了單繼承的局限性。

實作例子:

interface Demo
{
	public static final int NUM = 4;

	public abstract void show1();

	public abstract void show2();
}

//類與類之間是繼承關系,類與接口之間是實作關系。
/*
接口不可以執行個體化。
隻能有實作了接口的子類并覆寫了接口中是所有抽象方法後,該子類才可以執行個體化。
否則,這個子類就是一個抽象類。
*/
class DemoImpl implements Demo
{
	public void show1()
	{
		System.out.println("show1 run");
	}

	public void show2()
	{
		System.out.println("show2 run");
	}

}

class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		DemoImpl d = new DemoImpl();
		System.out.println(d.NUM);
		System.out.println(DemoImpl.NUM);
		System.out.println(Demo.NUM);
	}
}
           

多實作的例子:

在Java中不直接支援多繼承,因為會出現調用不确定性。

是以Java将多繼承機制進行改良,在Java中變成了多實作。

一個類可以實作多個接口。

interface A
{
	public abstract void show();
}

interface B
{
	public abstract void show();
}

class Test implements A, B	// 多實作
{
	// 這個show方法覆寫了A和B上的兩個抽象show方法
	public void show()
	{
		System.out.println("show run");
	}
}
           

繼承+實作:

/*
一個類在繼承另一個類的同時,還可以實作多個接口。
*/

class Q
{
	public void func(){}
}

interface A
{
	public abstract void show();
}

interface B
{
	public abstract void show();
}

class Test extends Q implements A, B	// 多實作
{
	public void func(){}

	public void show()
	{
		System.out.println("show run");
	}
}
           

接口支援多繼承:

interface AA
{
	public abstract void show();
}

interface BB
{
	public abstract void method();
}

interface CC extends AA, BB // 接口與接口之間是繼承關系,而且接口之間支援多繼承。
{
	public abstract void show();
}
           

接口和抽象類的差別:

共性:都是不斷抽取出來的抽象的概念(都是不斷向上抽取而來的)。

差別1:抽象類展現繼承關系,一個類隻能單繼承;接口展現實作關系,一個類可以多實作

差別2:抽象類是繼承,是“is a”關系;接口是實作,是“like a”關系。

差別3:抽象類中可以定義非抽象方法,供子類直接使用;接口的方法都是抽象,接口中的成員都有固定修飾符

多态

定義

         某一類事物的存在多種形态。

例子

        動物中貓,狗。

        貓這個對象對應的類型是毛類型:貓 c = new 貓();

        同時貓也是動物中的一種,也可以把貓稱為動物:動物 a  = new 貓();

        動物是貓和狗具體事物中抽取出來的父類型,父類型引用指向了子類對象。

簡單說:就是一個對象對應着不同類型。

多态在代碼中的展現:父類或者接口的引用指向其子類的對象。

abstract class Animal
{
	abstract void eat();
}

class Dog extends Animal
{
	void eat()
	{
		System.out.println("啃骨頭");
	}

	void seeHome()
	{
		System.out.println("看家");
	}
}

class Cat extends Animal
{
	void eat()
	{
		System.out.println("吃魚");
	}

	void catchMouse()
	{
		System.out.println("捉老鼠");
	}
}

class Pig extends Animal
{
	void eat()
	{
		System.out.println("飼料");
	}

	void gongDi()
	{
		System.out.println("拱地");
	}
}

class DuoTaiDemo 
{
	public static void main(String[] args) 
	{
		Animal aCat = new Cat();
		Animal aDog = new Dog();
		Animal aPig = new Pig();
		eatMethod(aCat);
		eatMethod(aDog);
		eatMethod(aPig);
	}

	public static void eatMethod(Animal a)
	{
		a.eat();
	}
}
           

多态的好處與弊端

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

弊端:前期定義的内容不能使用(調用)後期子類的特有内容。

多态的前提

  1. 必須有關系,繼承,實作。
  2. 要有覆寫。

轉型

例子:

abstract class Animal
{
	abstract void eat();
}

class Dog extends Animal
{
	void eat()
	{
		System.out.println("啃骨頭");
	}

	void seeHome()
	{
		System.out.println("看家");
	}
}

class Cat extends Animal
{
	void eat()
	{
		System.out.println("吃魚");
	}

	void catchMouse()
	{
		System.out.println("捉老鼠");
	}
}

class Pig extends Animal
{
	void eat()
	{
		System.out.println("飼料");
	}

	void gongDi()
	{
		System.out.println("拱地");
	}
}

class DuoTaiDemo 
{
	public static void main(String[] args) 
	{
		/*  Animal aCat = new Cat();
			Animal aDog = new Dog();
			Animal aPig = new Pig();
			eatMethod(aCat);
			eatMethod(aDog);
			eatMethod(aPig); */

		Animal a = new Cat();// 一個對象,兩種形态
				     // 自動類型提升,貓對象提升成了動物類型。但是特有功能無法通路。
				     // 作用就是限制對特有功能的通路
				     // 專業講:向上轉型。
		a.eat();

		// 如果還想用具體動物貓的特有功能,可以将該對象進行向下轉型。
		Cat c = (Cat)a;// 向下轉型的目的就是為了使用子類中的特有資料。
		c.eat();
		c.catchMouse();

		// 反例,注意:對于轉型,自始自終都是子類對象在做着類型的變化。
		// Animal a1 = new Dog();
		// Cat c1 = (Cat)a1; //ClassCastException異常 

	}

	public static void eatMethod(Animal a)
	{
		a.eat();
	}
}
           

instanceof-類型判斷

public static void eatMethod(Animal a)
{
	a.eat();
	// instanceof:用于判斷對象的具體類型,隻能用于引用資料類型判斷
	// 通常在向下轉型前用于健壯性的判斷
	if(a instanceof Cat)
	{
		Cat c = (Cat)a;
		c.catchMouse();
	}else if(a instanceof Dog)
	{
		Dog d = (Dog)a;
		d.seehome();
	}
}
           

多态成員的特點

1、成員變量:

  • 編譯時,參考引用型變量所屬的類中的是否有調用成員變量,若有,則編譯通過,否則,編譯失敗。
  • 運作時,參考引用型變量所屬的類中的是否有調用成員變量,并運作該所屬類中的成員變量。
  • 簡單而言,編譯和運作都參考等于号的左邊,that's all.
class Fu
{
	int num = 3;
}

class Zi extends Fu
{
	int num = 4;
}

class DuoTaiDemo2 
{
	public static void main(String[] args) 
	{
		// 參考左邊的 Zi z
		Zi z = new Zi();
		System.out.println(z.num); // 4

		// 參考左邊的 Fu f
		Fu f = new Zi();
		System.out.println(f.num); // 3
	}
}
           

2、成員函數

  • 編譯時,參考引用型變量所屬的類中的是否有調用的成員函數,若有,則編譯通過,否則,編譯失敗。
  • 運作時,參考的是對象所屬的類中的是否有調用的函數。
  • 簡單而言,編譯看左邊,運作看右邊。
class Fu
{
	void show()
	{
		System.out.println("fu show");
	}
}

class Zi extends Fu
{
	void show()
	{
		System.out.println("zi show");
	}
}

class DuoTaiDemo2 
{
	public static void main(String[] args) 
	{
		Fu f = new Zi();
		f.show(); // zi show
	}
}
           

3、靜态函數

  • 編譯時,參考引用型變量所屬的類中的是否有調用的靜态函數。
  • 運作時,參考引用型變量所屬的類中的是否有調用的靜态函數。
  • 簡單而言,編譯和運作都看左邊。
  • 其實對于靜态方法,是不需要對象的,直接用類名調用即可。

内部類

内部類通路的特點

  1. 内部類可以直接通路外部類中的成員。
  2. 外部類要通路内部類的資料,必須要建立内部類的對象。

内部類的用法

一般用于類的設計,分析事物時,發現該事物描述中還有其他事物,而且這個事物還要通路被描述事物的内容,這時就是還有的事物定義成内部類來描述。

class Outer
{
	private int num1 = 3;
	class Inner1
	{
		void show()
		{
			System.out.println("show run..."+num1);
		}
	}

	private static int num2 = 33;
	static class Inner2
	{
		void show()
		{
			System.out.println("show run..."+num2);
		}
	}

	private static int num3 = 333;
	static class Inner3
	{
		void show()
		{
			System.out.println("show run..."+num3);
		}
		// 如果内部類中定義了靜态成員,那麼内部類也必須是靜态的。
		static void func()
		{
			System.out.println("func run..."+num3);
		}
	}

	public void method()
	{
		Inner1 in = new Inner1();
		in.show();
	}
}

class InnerClassDemo 
{
	public static void main(String[] args) 
	{
		// 在外部類中的方法中執行個體化内部類對象來通路内部類内容
		Outer out = new Outer();
		out.method();

		// 直接通路外部類中的内部類中的内容
		Outer.Inner1 in1 = new Outer().new Inner1();
		in1.show();

		// 如果内部類是靜态的,相當于一個外部類
		Outer.Inner2 in2 = new Outer.Inner2();
		in2.show();

		// 如果内部類是靜态的,而且内部類的承成員也是靜态的
		Outer.Inner3.func();

	}
}
           

小細節

為什麼内部類能直接通路外部類中成員呢?

那是因為内部類持有了對外部類的應用。外部類名.this。

局部内部類

内部類可以存放在局部位置上。

内部類在局部位置上隻能通路局部中被final修飾的局部變量。

class Outer
{
	int num = 3;
	void method(final int Y)
	{
		final int X = 9;
		class Inner
		{
			void show()
			{
				System.out.println("show..." + X + "-" + Y);
			}
		}

		new Inner().show();
	}
}

class InnerClassDemo2 
{
	public static void main(String[] args) 
	{
		new Outer().method(8);
	}
}
           

匿名内部類

就是内部類的簡寫格式。

必須有前提:内部類必須繼承或者實作一個外部類或者接口。

簡單而言:匿名内部類其實就是一個匿名子類對象。

格式:new 父類or接口(){子類内容}

abstract class Demo
{
	abstract void show();
}

class Ouetr
{
	int num = 8;

	void method()
	{
		// 匿名内部類
		new Demo()
		{
			void show()
			{
				System.out.println("show...." + num);
			}
		}.show();
	}
}
           
interface Inter
{
	void show1();
	void show2();
}

class Outer
{
	int num = 6;

	void method()
	{
		Inter in = new Inter()
		{
			void show1()
			{
				System.out.println("show1..." + num);
			}

			void show2()
			{
				System.out.println("show2..." + num);
			}
		}

		in.show1();
		in.show2();
	}
}
           

内部類通常使用場景

1、當函數參數是接口類型時,而且接口中的方法不超過三個。可以用匿名内部類作為實際參數進行傳遞。

class InnerClassDemo2 
{
	public static void main(String[] args) 
	{
		// new Outer().method(8);

		show(new Inter()
		{
			void show1()
			{
				//...
			}
			void show2()
			{
				//...
			}
		});

	}

	public static void show(Inter in)
	{
		in.show1();
		in.show2();
	}
}
           

異常

定義

是在運作時期發生的不正常情況。

在Java中用類的形式對不正常情況進行了描述和封裝對象。

描述不正常情況的類,就稱為異常類。

以前正常流程代碼和問題處理代碼相結合,現在有了異常類之後,将正常流程代碼和問題處理代碼分離,提高閱讀性。

其實異常類就是Java通過面向對象的思想将問題封裝成了對象。用異常類對其進行描述。

不同的問題用不同的類進行具體的描述,比如角标越界,空指針異常等等等。

Demo

class ExceptionDemo
{
	public static void main(String[] args) 
	{
		sleep(-5);
		sleep(1000000);
	}

	public static void sleep(int time)
	{
		if(time < 0)
		{
			抛出 new FuTime();
		}

		if(time > 100000)
		{
			抛出 new BigTime();
		}

		System.out.println("睡覺...." + time + "分鐘");
	}
}

class Funtime extends Exception
{
	處理
}

class BigTime extends Exception
{
	處理
}
           

體系

問題很多,意味着描述的類很多,将其共性進行向上抽取,形成了異常體系。

最終問題(不正常情況)就分成兩大類。

Throwable:無論是error,還是Exception都是問題,問題發生就應可以抛出,讓調用者知道并處理。

                       該體系的特點就在于Throwable及其所有的子類都具有可抛性。

                       可抛性到底指的是什麼呢?怎麼展現可抛性呢?

                       其實是通過兩個關鍵字來展現的。

                       throws,throw,凡是可以被這兩個關鍵字所操作的類和對象都具有可抛性。

1、一般不可以處理的——Error。

      特點:是有JVM抛出的嚴重性的問題。

                  這種問題發生一般不針對性處理,直接修改程式。

2、可以處理的——Exception。

該體系的特點:

子類的字尾名都是用其父類名作為字尾名的,閱讀性很強。

黑馬程式員——Java語言--面向對象(二)多态

自定義異常

先看看一個例子

class FuShuIndexException extends Exception
{
	FuShuIndexException(){}

	FuShuIndexException(String msg)
	{
		super(msg);// 通過父類可以将msg顯示在控制台上
	}
}

class Demo
{

	public void method(int[] arr, int x) throws FuShuIndexException // 在次聲明異常
	{
		if(x < 0)
			throw new FuShuIndexException("角标可不能為負數喔!");
		System.out.println(arr[x]);
	}
}

class ExceptionDemo3 
{
	public static void main(String[] args) throws FuShuIndexException // 要在此進行聲明次異常,如果FuShuIndexException繼承RuntimeException,則次聲明不需要
	{
		int[] arr = new int[3];
		Demo d = new Demo();
		d.method(arr, -5);
	}
}
           
黑馬程式員——Java語言--面向對象(二)多态

對于角标是整數不存在,可以用角标越界來表示;對于角标為負數的情況,準備用負數角标異常來表示。

負數角标這種異常在Java中并沒有定義過,那就按照Java異常的建立思想,面向對象,将負數角标進行自定義描述,并封裝成對象。

這種自定義的問題描述稱為自定義異常。

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

異常的分類

1、編譯時被檢測異常:隻要是Exception和其子類都是,除了特殊子類RuntimeException體系。

      這種問題一旦出現,希望在編譯時就進行檢測,讓這種問題有對應的處理方式。這樣的問題都可以針對性的處理。

2、編譯時不檢測異常(運作時異常):就是Exception中的RuntimeException和其子類。

      這種問題的發生,無法讓功能繼續,運算無法進行,更多是因為調用者的原因導緻的而或者引發了内部狀态的改變導緻的。

      那麼這種問題一般不處理,直接編譯通過,在運作時,讓調用者調用時的程式強制停止,讓調用者對代碼進行修改。

是以自定義異常時,要麼繼承Exception,要麼繼承RuntimeException。

throw和throws的差別

1、throws使用在函數上,而throw使用在函數内。

2、throws抛出的是異常類,可以抛出多個,用逗号隔開。throw抛出的是異常對象。

異常捕捉

異常處理的捕捉形式:

這是可以對異常進行針對性處理的方式。

具體格式:

try

{

   // 需要被檢測異常的代碼

}

catch(異常類 變量)  // 該變量用于接收發生的異常對象

{

   // 處理異常的代碼

}

finally // 此處可以根據需求選擇要不要

{

   // 一定會執行的代碼

}

Demo:

class ExceptionDemo4 
{
	public static void main(String[] args) 
	{
		int[] arr = new int[3];
		Test t = new Test();
		try
		{
			int num = t.method(arr, -5);
			System.out.println("num = "+num);
		}
		catch (FuShuIndexException e)
		{
			System.out.println(e.getMessage());
			e.printStackTrace(); // jvm預設的異常處理機制就是調用異常對象的這個方法。
		}
		System.out.println("over");
	}
}

class Test
{
	public int method(int[] arr, int x) throws FuShuIndexException
	{
		if(x < 0)
			throw new FuShuIndexException("角标出現負數啦!");
		return arr[x];
	}
}

class FuShuIndexException extends Exception
{
	FuShuIndexException(){}

	FuShuIndexException(String msg)
	{
		super(msg);
	}
}
           

多catch情況

如果有多個catch,且還有父類的catch,那麼父類的catch放在最後,否則編譯失敗。

class ExceptionDemo4 
{
	public static void main(String[] args) 
	{
		int[] arr = new int[3];
		Test t = new Test();
		try
		{
			int num = t.method(arr, -5);
			System.out.println("num = "+num);
		}
		catch (FuShuIndexException e)
		{
			System.out.println(e.getMessage());
			e.printStackTrace(); // jvm預設的異常處理機制就是調用異常對象的這個方法。
		}
		catch(NullPointerException e)
		{
			System.out.println(e.getMessage());
		}
		System.out.println("over");
	}
}

class Test
{
	public int method(int[] arr, int x) throws FuShuIndexException, NullPointerException
	{
		if(arr == null)
			throw new NullPointerException("沒有任何數組實體!");
		if(x < 0)
			throw new FuShuIndexException("角标出現負數啦!");
		return arr[x];
	}
}

class FuShuIndexException extends Exception
{
	FuShuIndexException(){}

	FuShuIndexException(String msg)
	{
		super(msg);
	}
}
           

異常處理的原則:

1、函數内容如果抛出需要檢測的異常,那麼函數上必須要聲明。否則必須在函數内用trycatch捕捉,否則編譯失敗。

2、如果調用到了聲明異常的函數,要麼trycatch要麼throws,否則編譯失敗。

3、什麼時候catch,什麼時候throws呢?

      功能内容可以解決,用catch。如果解決不了,用throws告訴調用者,由調用者解決。

4、一個功能如果抛出了多個異常,要麼調用時,必須有對應多個catch進行針對性的處理。

      内部又幾個需要檢測的異常,就抛出幾個異常,抛出幾個,就catch幾個。

finally代碼塊

例子:

finally代碼塊是很有用的。講講老畢說的例子:

連接配接資料庫

查詢

關閉連接配接

因為每一台裝置被連接配接的數目是有限的,不能一直占用着資源,因為别人也要用到。那麼伺服器會判斷一定的時間内沒有跟資料庫有資訊互動的話,會自動關閉連接配接,如果在查詢的時候出現了異常,那麼也需要将連接配接關閉,釋放資源。

try catch finally 代碼塊組合的特點:

1、第一種用法有 :try catch finally

2、try catch(多個) 當沒有必要的資源要釋放的時候,可以不用定義finally。

3、try finally 如果異常無法直接用catch解決不了,那麼就不要catch了,但是要抛出去給調用者進行異常處理。

包(package)

特點

1、對類檔案進行分類管理

2、給類提供多層命名空間

3、寫在程式檔案的第一行

4、類名的全稱的是 包名.類名

5、包也是一種封裝形式

Demo:

Package mypack;

class PackageDemo 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello Package!");
	}
}
           

編譯的時候,産生一個mypack的檔案夾,裡邊有PackageDemo.class檔案,在doc控制台上編譯:javac -d . PackageDemo.java

包-包之間的通路-protected

包與包之間的類進行通路,被通路的包中的類必須要public的,被通路的包中的類的方法也必須要public權限。

                  public                     protected                      default                             private

同一類中     ok                              ok                                 ok                                     ok   

同一包中     ok                              ok                                 ok

子  類  中     ok                              ok

不同包中     ok

導入import

Package mypack;

import packa.DemoA; // 導入了packa包中的DemoA類。
//import packa.*; // 導入了packa包中的所有的類。開發過程中,不建議使用此方法。

class PackageDemo 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello Package!");
	}
}
           

導包的原則:用到哪裡個類,就導入哪個類。