JAVA學習筆記14——多態+內存分析

最近在看JAVA教學的視頻,覺得老師講的很好,同時借用源代碼還有筆記來撰寫本系列博客,記錄自己的學習內容,同時也供看到的人學習。

開始NO.14篇,這篇介紹的是JAVA面向對象的最後一個特徵:多態。

先來看看多態的具體含義:


多態最直接的表現就是多個子類重寫共有父類的一個方法,而在調用這個方法的時候只傳入父類的類型,通過向上轉型、強制轉型等操作又具體到某個子類重寫的方法的內容,轉而執行那個子類重寫的這個方法,節省了大量的代碼空間。具體實現見下面的代碼,都是關於細節部分的一些解釋和總結我在原有代碼的基礎上進行了補充說明。

代碼實現實例:

<span style="font-size:14px;">public class Animal {
	String str;
	public void voice(){
		System.out.println("普通動物叫聲!");
	}
}
class Cat extends Animal {
	public void voice(){
		System.out.println("喵喵喵");
	}
	public void catchMouse(){
		System.out.println("抓老鼠");
	}
}
class Dog extends Animal {
	public void voice(){
		System.out.println("汪汪汪");
	}
	public void seeDoor(){
		System.out.println("看門!");
	}
}
class Tiger extends Animal {
	public void voice(){
		System.out.println("哇哇哇");
	}
}
class Pig extends Animal {
	public void voice(){
		System.out.println("哼哼哼");
	}
}</span>
<span style="font-size:14px;">public class Test {
	public static void testAnimalVoice(Animal c){  //注意這裏傳的是動物animal,沒有具體的種類。多態通過傳參的多樣性會在這個方法裏面節省大量的代碼
		c.voice();
		if(c instanceof Cat){        //一個新的運算符:實例對象的判斷(此句意思:如果c是Cat的一個實例對象,則之後把c強轉爲cat類型,並調用catchmouse方法。)
			((Cat) c).catchMouse();
		}
	}
	/*   如果不採用多態,則下面的代碼要都寫上才能正常運行本程序。
	public static void testAnimalVoice(Dog c){
		c.voice();
	}
	public static void testAnimalVoice(Pig c){
		c.voice();
	}*/
	public static void main(String[] args) {
		Animal a = new Cat();       //父類引用指向子類對象
		Animal b = new Dog();
		Animal c = new Tiger();
		testAnimalVoice(a);	
		testAnimalVoice(b);
		testAnimalVoice(c);
		Cat a2 = (Cat)a;   //注意這裏。此句不能省略,省略了下面一句將會報錯,因爲在編譯的時候a是animal型,animal裏面沒有catchmouse方法,所以會報錯,想要成功執行必須重新轉型成貓型,才能正常運行。
		a2.catchMouse();
	}
}
</span>
接下來把上述代碼部分提取一部分,進行對應的內存分析,使我們更好地瞭解多態:

代碼部分:

<span style="font-size:14px;">public class Blog {
	public static void testAnimalVoice(Animal c){  
		c.voice();
		if(c instanceof Cat){        
			((Cat) c).catchMouse();
		}
	}
	public static void main(String[] args) {
		Animal a = new Cat();  
		Cat a2 = (Cat)a;      
		testAnimalVoice(a);	
	}
}
</span>
<span style="font-size:14px;">public class Animal {
	String str;
	public void voice(){
		System.out.println("普通動物叫聲!");
	}
}
class Cat extends Animal {
	public void voice(){
		System.out.println("喵喵喵");
	}
	public void catchMouse(){
		System.out.println("抓老鼠");
	}
}
class Dog extends Animal {
	public void voice(){
		System.out.println("汪汪汪");
	}
	public void seeDoor(){
		System.out.println("看門!");
	}
}
class Tiger extends Animal {
	public void voice(){
		System.out.println("哇哇哇");
	}
}
class Pig extends Animal {
	public void voice(){
		System.out.println("哼哼哼");
	}
}
</span>
對應的內存分析圖(圖中的test類是上述代碼中的Blog類~):


關於上述圖的一些內容的解釋:首先是在方法區裏面吧相關的類還有信息放進去,這個不多做解釋。接下來進行執行階段,首先是對象堆裏面由於繼承的原因,Cat對象包含了父類以及父類的父類的全部內容,再加上自己重寫的方法還有特有的方法,出現了這種抽象爲嵌套型的形狀,類似的內容可以參考筆記12,父類animal的引用a指向了子類Cat,所以指向堆中的首地址,接着強制轉型把啊由Animal型轉爲cat型再賦給a2,所以a2也指向首地址,接下來調用testAnimalVoice方法,傳參,把a傳給形參c,c也指向首地址,接下來,c調用voice方法,如果c是Cat的一個實例引用(即如果a是指向c的首地址)則把c轉爲Cat型,在調用catchmouse方法。

最後介紹的內容是利用JSP開發裏面一個很重要的類做一個多態的小例子:

代碼如下:

public class HttpServlet {
	public void service(){
		System.out.println("HttpServlet.service()");
		this.doGet();     //這裏指的是整個對象的doGet方法(在內存圖裏面就是組外層的那個類的方法,這裏this省略結果也是一樣的,不要弄錯)
		doGet();    //與上行效果一樣
	}
	public void doGet(){
		System.out.println("HttpServlet.doGet()");
	}
}
public class MyServlet extends HttpServlet {
	public void doGet(){
		System.out.println("MyServlet.doGet()");
	}
}
public class Test {
	public static void main(String[] args) {
		HttpServlet s = new MyServlet();
		s.service();
	}
}
需要思考的問題是回到父類的service方法的時候裏面涉及了調用doGet方法,那麼這個doGet方法到底用的是誰的方法呢?

先看內存圖:


從圖中還有代碼的解釋部分可以看到調用的應該是子類的(最外層類的對象)doGet方法而不是父類的。即:輸出結果如下圖:


好了,這篇到此結束,內容很重要哦~

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