黑馬程序員--Java基礎之面向對象總結(二)

------- android培訓java培訓、期待與您交流! ----------
      接口的概述:接口是一個特殊的抽象類,當抽象類中的方法都是抽象的,那麼該類可以通過接口的形式來表示。class用來定義類,interface用來定義接口,形式爲interface 接口名稱。定義接口時,接口中定義的成員變量是靜態終例,成員方法是靜態的抽象方法,並且成員都是公共的public,它們都是這樣固定的修飾符來修飾,成員變量:public static final int x=0;,成員方法:public  abstract void show();。接口不可以被實例化,因爲它具有抽象方法,只能被子類實現後,實例化這個子類來使用,子類實現接口時必須實現接口的所有抽象方法,否則子類是一個抽象類。接口可以被類多個實現,形式爲interface C implements A,B{}。不同的接口擁有相同的抽象方法時,任然可以被一個類同時實現,因爲接口中的抽象方法沒有方法主體,可以由實現接口的類實現方法,並且只有這一個方法,同時需要注意的是,這幾個相同的方法必須返回類型相同。幾個接口之間可以實現多繼承,例如interface A{},interface B{},interface C extends A,B{}
      接口的特點和示例:接口是對外暴露的規則,它是程序的功能的擴展。接口和類的關係是實現,類可以繼承一個類的同時實現多個接口。打比方說接口就像是筆記本電腦上的usb接口,不同的對象使用這個接口會實現不一樣的方法效果,使用該接口的對象實現了接口裏面的方法。
abstract class Students {// 抽象類Students,提供公共方法sleep和抽象方法study
	public abstract void study();// 抽象方法用於被繼承Students的子類實現,因爲每個子類有不同的學習方法

	public void sleep() {// 公共方法sleep,所有繼承Students的子類都擁有共同的睡覺方法。
		System.out.println("sleep...");
	}
}

interface Smoking {// 接口Smoking,定義的抽菸功能,不是所有人都抽菸,不能定義在抽象類Students中,誰抽菸誰實現這個接口
	public abstract void smoke();// 抽象方法smoke,誰抽菸抽什麼煙,它就自己實現這個接口
}

class Zhangsan extends Students implements Smoking {// 定義的Zhangsan這個人的類繼承自Students類,實現了Smoking接口,若其不抽菸則不實現抽菸的接口

	@Override
	public void study() {// 重寫父類Students的study方法,定義自己學習的內容
		System.out.println("study Java...");
	}

	public void smoke() {// 實現接口Smoking的smoke方法,定義自己抽菸的方法
		System.out.println("smoking NanJing...");
	}

}

class TestInterface {
	public static void main(String[] args) {
		Zhangsan zs = new Zhangsan();// 實例化Zhangsan對象
		zs.study();// 調用每個學生都有但是各不相同的study方法
		zs.smoke();// 調用某些抽菸同學纔有的smoke方法
		zs.sleep();// 調用所有學生都相同的sleep方法
	}
}
      多態的概述和擴展性:多態可以理解爲一種事物存在着多種表現形態,比如動物這種事物,它具有很多的不同形態的動物,有貓狗等,人這個事物有學生老師等。在java裏用面向對象的知識來表現可以這樣,動物 a=new 貓()。多態的體現是父類的引用指向了自己的子類對象,同時父類的引用也可以接受自己的子類對象,即Animal cat=new Cat();此時Animal這個父類的引用指向了它的子類 Cat對象,方法function(Animal animal){}的參數是父類Animal,在使用這個方法時可以傳遞它的子類,如function(new Cat()),這就是父類的引用可以接受自己的子類對象。多態的前提是類和類之間有關係,這個關係可以是繼承也可以是實現,同時它們之間存在覆蓋關係,即父類中存在方法被子類覆蓋,若類和類之間沒有關係則不能實現多態,若不存在方法的覆蓋那麼多態也沒有意義。多態的好處是極大程度地提高了程序的擴展性,但同時它的弊端就是隻能使用父類的引用訪問父類中的成員,如function(Animal animal){animal.eat();}只能使用父類animal對象的引用來訪問父類中的成員。
abstract class Animal {// 抽象的父類Animal
	abstract void eat();// 抽象方法eat
}

class Cat extends Animal {// 繼承父類Animal的子類Cat,Animal事物的一種多態表現
	@Override
	void eat() {// 覆蓋父類的eat方法
		System.out.println("吃魚...");
	}

	void catchMouse() {// 自己的catchMouse方法
		System.out.println("抓老鼠...");
	}
}

class Dog extends Animal {// 繼承父類Animal的子類Dog,Animal事物的一種多態表現
	@Override
	void eat() {// 覆蓋父類的eat方法
		System.out.println("吃骨頭");
	}

	void lookHome() {// 自己的lookHome方法
		System.out.println("看守家...");
	}
}

class AnimalDemo {
	public static void main(String[] args) {
		animalEat(new Cat());// 調用Cat子類的eat方法
		animalEat(new Dog());// 調用Dog子類的eat方法
	}

	// 使用面向對象的多態特性抽取的公共方法,提高程序的擴展性,無論後期增加多少Animal的子類,這個方法都可以調用相應的eat方法
	public static void animalEat(Animal animal) {// 抽取Animal的eat方法,傳遞參數爲Animal的多態形式下的子類,父類的引用接受子類對象
		animal.eat();
	}
}
      多態的轉型:多態轉型時分爲倆種情況,一種是類型自動轉換即類型提升,這是向上轉型,例如父類初始化時對象引用指向子類,Animal a=new Cat()此時就是cat類型向上轉型成Animal,a只有Animal中的eat方法,沒有Cat的catchMouse方法,另一種是類型的強制轉換即向下轉型,例如Cat c=(Cat)a,此時將Animal父類強制轉換成子類Cat,c有eat和catchMouse方法。當animalEat(Animal animal)方法中存在不同子類對象自己獨有的方法調用時,我們需要進行類型判斷再調用相應方法,使用關鍵字instanceof,父類 instanceof 子類,最後結果是boolean類型,判斷父類類型是否屬於這個子類類型,如果是那麼強制轉換成這個類型,如果不是強制轉換就會報類型轉換的異常。
	public static void animalEat(Animal animal) {
		animal.eat();
		if (animal instanceof Cat) {// 判斷animal是否屬於Cat類型
			Cat c = (Cat) animal;// 強轉換成Cat類型
			c.catchMouse();// 調用Cat中自己的catchMouse方法
		} else if (animal instanceof Dog) {// 判斷animal是否屬於Dog類型
			Dog d = (Dog) animal;// 強轉換成Dog類型
			d.lookHome();// 調用Dog中自己的lookHome方法
		}
	}
     多態中成員的特點:當實現多態的語句時,jvm先進行編譯,編譯成功後再運行,例如:父類Fu中有method1和method2方法,子類Zi中覆蓋了method1方法,具有自己的method3方法。實現以下多態代碼段,Fu f=new Zi();f.method1();f.method2();f.method3();首先編譯不通過,報f類中沒有method3這個方法的錯誤,的確父類Fu中沒有方法method3,當去掉調用method3這個方法後編譯成功,然後運行時,程序執行的是調用子類Zi的method1方法和調動的父類的method2方法。在編譯時期jvm參閱引用變量類型所屬類中是否有調用的方法,如果有則編譯通過,如果沒有則編譯不通過,這裏編譯時就先檢查父類Fu是否有所需要調用的方法。在運行時期jvm參閱對象所屬類型的類中是否有調用的方法,這裏運行時就檢查對象子類Zi中是否覆蓋了父類的方法,method1覆蓋了父類的方法所以調用子類的method1方法,method2沒有覆蓋父類的方法所以調用父類的method2方法。簡單對它們總結就是,成員函數在多態調用時,編譯看左邊,運行看右邊。在多態中成員變量的特點是,不管是編譯還是運行都看左邊,例如父類Fu{int num=5;},子類Zi{int num=8},Fu f=new Zi();Zi z=new Zi();此時f.num的結果是5,仍是父類Fu的成員變量,z.num的結果是8,是子類Zi本身的成員變量。多態中的靜態成員函數的特點是無論編譯和運行都看左邊,例如父類Fu中是static的method1,子類Zi中是static的method1,Fu f=new Zi();f.method1();此時調用結果是調用了父類的method1方法。因爲靜態方法的調用都是用類名直接調用,雖然使用了實例化出的對象來調用,但是存儲在內存中方法區的靜態方法都被綁定在所屬類上,使用時就是用類名調用的,非靜態方法自然就是使用該對象來調用,所以調用的是對象中的方法。
      動態綁定和靜態綁定:Fu f=new Zi();f.method1();Zi z=new Zi();z.method1();當父類中的method1方法是靜態時,子類繼承後覆蓋靜態method1方法,那麼方法加載在內存中的方法區時就是靜態綁定,靜態綁定形式爲Fu.method1(),使用時雖然形式上是f.method1()但實際就是引用類型的類名調用方法,即Fu.method1(),z.method1()實際就是引用類型的類名Zi調用方法method1。當method方法爲非靜態時,方法加載在內存中的方法區時就是動態綁定,動態綁定形式爲this.method1(),那麼f.method1()實際就是對象f調用它的方法,即new Zi().method1()。
      多態的主板示例:一個主板是電腦使用的核心,當使用電腦運行某些方法時需要使用主板來運行,主板先運行然後再運行你要執行的操作,比如上網和放音樂,那麼主板就需要執行網卡和聲卡,早期網卡和聲卡這種都是焊死在主板上,若需要更換或者升級等非常不方便,後期主板上出現了PCI接口,這個接口就是一種協議標準,它是連接到主板的核心,網卡和聲卡製造時按照這個接口標準實現該接口就可以和主板相連使用,那麼不管是什麼類型的設備,不管是同種設備的不同型號,它們只要插在主板的這個接口上就能被使用,降低了主板和各種設備的耦合性,易於設備的更新和維護。我們代碼實現時就在主板類中定義使用該接口的方法,將接口作爲參數傳遞過去,即插上網卡就使用上網功能,插上聲卡就能實現播放音樂功能。這時接口引用指向了實現該接口的子類對象,這裏就是多態的應用。
public class MainBord {// 主板類,電腦運行時的核心
	public void run() {// 主板運行的方法
		System.out.println("MainBord run...");
	}

	public void usePCI(PCI p) {// 主板使用PCI接口槽,若有設備插入則運行,若沒有則不運行
		if (null != p) {// 判斷有設備運行的方法,沒有設備則不執行方法,非空判斷保證沒有設備電腦也能正確運行不報錯
			p.open();
			p.close();
		}
	}
}

interface PCI {// PCI接口類,實現該接口的設備都可以插在電腦上運行
	public void open();

	public void close();
}

class NetCard implements PCI {// 製造的網卡設備,實現PCI接口
	public void open() {
		System.out.println("NetCard open...");
	}

	public void close() {
		System.out.println("NetCard close...");
	}
}

class ComputerDemo {// 模擬的電腦類
	public static void main(String[] args) {
		MainBord m = new MainBord();// 電腦運行核心是運行主板,實例化主板對象
		m.run();// 主板運行
		m.usePCI(null);// 沒有設備也能運行
		m.usePCI(new NetCard());// 主板執行PCI接口,插的是什麼設備則運行什麼設備,若有多個設備則一起運行
	}
}
      Object類和equals()方法:Object類是所有類的超類,有一種說法叫做上帝類,它沒有父類,所有的類都是繼承於它,具備它所有的方法。equals方法就是Object類中用來比較倆個對象是否相等的方法,它是比較兩者的內容,如果比較的倆者都是基本數據類型則比較倆者的內容,如果比較對象是類對象則是比較倆者的存儲地址。當定義一個類後若實例化2個對象,例如Demo d1=new Demo();Demo d2=new Demo();d1.equals(d2);那麼它的比較結果是false,因爲他們都是類對象,比較的是對象的地址,必然不同時false,若類Demo中定義int num;屬性,希望使用equals比較時是比較這個屬性,那麼就需要在定義Demo類時重寫它繼承子超類Object的equals方法,這樣就能實現自定義的比較方法。
class Demo {// 定義Demo類
	int num;

	Demo(int num) {
		this.num = num;
	}

	@Override
	// 重寫Object類的equals方法,實現它的自定義比較方法
	public boolean equals(Object obj) {
		if (!(obj instanceof Demo)) {// 當傳遞的對象不屬於Demo類時直接返回false
			return false;
		}
		Demo d = (Demo) obj;// 將傳遞的obj對象向下轉型成Demo,這樣它才具備num屬性,否則obj沒有改屬性
		return num == d.num;// 將當前對象的num屬性和傳遞的obj的屬性比較
	}
}

class People {
}

public class Object_equals {
	public static void main(String[] args) {
		Demo d1 = new Demo(2);
		Demo d2 = new Demo(2);
		System.out.println(d1.equals(d2));// 自定義的equals方法比較,實際是比較的是兩者的num屬性,若沒有覆蓋方法則是比較倆者的地址
		System.out.println(d1.equals(new People()));// 當比較對象不是Demo型時,通過定義方法裏的類型判斷,返回值爲false
	}
}
     內部類訪問規則:將一個類定義在一個類中,這個類就叫做內部類。內部類的訪問規則是:內部類可以直接訪問外部類的成員,包括私有的,之所以能夠訪問是因爲內部類中有一個外部類的引用,格式爲外部類名.this;外部類要訪問內部類中的內容必須要建立內部類的對象。
public class Outer {// 定義外部類
	private int x = 3;// private修飾的成員屬性x
	private int y = 8;

	class Inner {// 定義內部類
		private int y = 1;

		void show() {
			System.out.println("Inner:" + x);// 內部類可以訪問外部類的private修飾的屬性,它前面省略了外部類名.this,如Outer.this.x
			System.out.println("Inner:" + y);// 當內部類存在同名的成員時y是內部類的y,值爲1,它類似於普通類,省略了this,如this.y
		}
	}

	private class InnerMethod {
		// 內部類屬於外部類Outer的成員,所以內部類能夠被private修飾,注意,一般類是不能被private修飾的
	}
}

class InnerClassDemo {
	public static void main(String[] args) {
		Outer.Inner in = new Outer().new Inner();// 使用內部類時必須使用外部類的引用
		in.show();
	}
}
      靜態內部類的使用:在外部類中的成員位置上就可以被成員修飾符所修飾,所以內部類可以被private和static修飾,private是將內部類在外部類中封裝,static修飾的內部類具有static的特性,static靜態內部類只能訪問外部類的靜態成員。注意,內部內具有靜態方法時該內部類必須定義爲靜態內部類,當外部類的靜態方法訪問內部類時,該內部類也必須是靜態內部類。
public class OuterClass {
	static void methodOuter() {// 外部類的靜態方法
		System.out.println("OuterClass...methodOuter");
		// InnerClass.methodInner();外部類的靜態方法訪問的內部類必須是static靜態的
	}

	static class InnerClass {// 當存在靜態方法時,內部類必須定義成靜態的
		static void methodInner() {
			System.out.println("InnerClass...methodInner");
			methodOuter();// 靜態的內部類只能訪問外部類的靜態成員(變量、方法)
		}
	}

	public static void main(String[] args) {
		OuterClass.InnerClass.methodInner();// 訪問靜態內部類的靜態方法的形式
		// 若InnerClass爲非靜態方法,訪問形式爲new OuterClass.InnerClass().methodInner();
	}
}

      匿名內部類:內部類定義在局部時,不可以被成員修飾符修飾,如不可以被static修飾,可以直接訪問外部類中的成員,因爲還持有外部類中的引用,但是不可以訪問它所在的局部中的變量,只能訪問被final修飾的局部變量。匿名內部類其實就是內部類的簡寫格式,定義匿名內部類的前提是內部類必須是集成一個類或者實現一個接口,匿名內部類的格式:new 父類或者接口名{定義的子類內容},其實匿名內部類就是一個匿名子類的對象,可以理解爲帶內容的對象,並且爲了簡化代碼的書寫,內部類中定義的方法最好不要超過3個。

interface Demo {// 定義的Demo接口
	void method();
}

public class Outer {// 定義的外部類Outer
	static Demo speak() {// 外部類的靜態方法speak,返回類型爲Demo接口
		final int num = 5;// 局部變量num類型爲final
		return new Demo() {// 返回一個Demo接口對象,該對象是匿名內部類,實際是實現Demo接口的子對象
			public void method() {// 實現Demo接口必須重寫的方法method
				System.out.println("Demo:.." + num);// 局部定義的內部類訪問局部變量時只能訪問final修飾的變量,所以參數爲final,同時它默認可訪問外部類的對象
			}
		};
	}

	// 注意:局部定義的內部類不在外部類的成員位置,所以它不能有成員修飾符,例如private,static,因此它不能夠包含static的靜態方法
	static class InnerDemo implements Demo {// 實現以上類似方法,使用不匿名內部類,則需要先定義靜態的實現Demo接口的類
		int num = 5;

		public void method() {
			System.out.println("Demo:..." + num);
		}
	}

	static Demo speak1() {
		Demo d = new InnerDemo();// 先實例化實現Demo接口的對象
		return d;
	}
}

class InnerTest {
	public static void main(String[] args) {
		Outer.speak().method();// 調用Outer的靜態方法speak,它的返回值是Demo接口類型的對象,然後調用該對象的method方法
		Demo d = Outer.speak1();// 效果等同以下方式
		d.method();
		// 面試題要使用一個匿名內部類的任意方法,但這個類沒有繼承或者實現接口
		new Object() {// 所有類都父類object,所以該匿名內部類其實是object的子類對象
			void function() {
			}
		};
	}
}
      自定義異常:項目中可能會出現一些問題,這些問題沒有被java描述封裝,我們要解決這類問題就需要自定義異常類來封裝描述它們。當在函數出現了throw拋出的異常時,就必須給出對應的處理,這裏有倆種解決方法,一種是在函數內部使用try catch處理,一種是在函數申明上使用thorws向上拋出,讓調用者處理。在java運行的主函數內使用try語句處理異常,或者繼續向上拋異常,最終處理異常的是jvm。自定義異常時,必須繼承Exception類,因爲這個體系具有一個特點,異常類和異常對象都被拋出,他們都具有可拋性,這是Throwable體系的獨有特點。因爲父類把異常信息的操作都完成了,所以子類在構造時只需要將異常信息傳遞給父類,使用super語句傳遞參數並構造,通過getMessage方法就可以獲取自定義的異常信息。
public class MinusException extends Exception {// 自定義異常,繼承Exception類
	public MinusException(String msg) {// 帶參構造參數,使用super語句將異常信息傳遞給父類構造函數,便於使用Exception共有的方法獲取異常信息
		super(msg);
	}
}

class MinusDemo {// 測試負數方法的類
	public int div(int a, int b) throws MinusException {// 除法運算的函數,拋出了自定義異常
		if (b < 0) {
			throw new MinusException("除數出現負數了/ by minus");// 函數中手動拋出負數自定義異常
		}
		return a / b;
	}
}

class ExceptionDemo {
	public static void main(String[] args) {
		MinusDemo md = new MinusDemo();
		try {// 使用try語句塊解決div方法申明的異常
			md.div(4, -1);
		} catch (MinusException e) {
			System.out.println(e.toString());// 打印自定義異常處理的信息
		} finally {
			System.out.println("finally");// finally中存放的是一定會被執行的代碼,通常用於關閉資源
		}
	}
}
      throw和throws的區別:throw使用在函數上,throws使用在函數內,throws後面跟的異常類,可以跟多個,用逗號隔開,throw後跟的是異常對象。
      RuntimeException異常:Exception中有一個特殊的子類異常RuntimeException運行時異常,如果在函數內容內拋出該異常,函數上可以不用申明,編譯能夠通過,如果在函數上申明瞭該異常,調用者可以不用處理,編譯能夠通過。自定義異常時,如果該異常的發生,無法再繼續進行運算,就讓自定義異常繼承RuntimeException。對於異常分爲倆種:1,編譯時被檢測的異常。2,編譯時不被檢測的異常(運行時異常,RuntimeException及其子類)。
      覆蓋時異常的特點:1,子類在覆蓋父類的方法時,如果父類拋出異常,那麼子類的覆蓋方法只能拋出父類的異常或者該異常的子類。2,如果父類拋出多個異常,那麼子類在覆蓋方法時只能拋出父類異常的子集。例如父類拋出3個異常,子類只能拋出小於等於3個的異常。3,如果父類中沒有異常拋出,那麼子類覆蓋方法時也不可以拋出異常。如果子類發生了異常,就必須使用try語句進行處理,絕對不能拋出異常。
      包與包之間的訪問權限:類的訪問權限只有倆種,爲默認權限friendly和公共權限public。默認權限不同包是不能訪問的,必須改爲公共權限才能實現不同包中類的訪問。對於類中的成員(成員變量和成員方法)有四種訪問權限,不同的訪問權限則在不同情況下才能訪問。這四種訪問權限從小到大分別爲私有權限private,默認權限friendly,保護權限protected,公共權限public,下面用表格表示這4種權限的訪問情況。
四種訪問權限形式
  private friendly protected public
同一個類
同一個包 X
不同包中的子類 X X
不同的包 X X X

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章