Java第八課. 物件導向特徵3-多型&抽象類

裝14發表於2020-12-07

Java第八課. 物件導向特徵3-多型&抽象類

回顧

1. toString(): 返回物件資訊;  
列印:syso(物件名.toString()); toString()可以省略,syso(物件名);

2.繼承: 關鍵字 [extends]
    子類繼承父類的的特徵和行為(公共的特徵和行為);構造方法不不能被繼承;
	extends 繼承;擴充套件
    子類可以擴充套件父類  子類可以包含自己的特殊部分
        
3.重寫(覆寫): 發生在繼承父類的子類中,在子類的某個方法,方法的修飾符,返回值型別,方法名,引數列表和父類的某個方法完全一樣;(Object 是所有類的父類)

4.構造方法和繼承: 在子類物件例項化的時候,因為繼承的原因會預設先呼叫父類的無參構造,再呼叫子類的構造方法;

5.super 關鍵字: 子類可以用來呼叫父類的構造方法和普通方法;呼叫構造方法時要放第一行,普通方法時沒有要求;
	在呼叫構造方法時 thissuper 不能同時使用;

1. 多型性

1.1 多型的引入

模擬課堂上課:小張(JavaTeacher)來了,我們開始上正經的java課程;小李(Driver)來了,我們就開始開車;小王(OldDriver)來了,我們就上職業素養課,飆車;
步驟1:先建立父類Teacher
/**
 * 建立一個父類
 * @author Administrator
 *
 */
public class Teacher {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public void teach() {
		System.out.println(name+"來了");
	}
	
}
步驟2:再建立Teacher的各個子類
public class JavaTeacher extends Teacher {

	@Override
	public void teach() {
		//呼叫父類的teach方法
		super.teach();
		//自己內部的部分
		System.out.println("我們開始上正經的java課程");
	}
}

public class Driver extends Teacher {

	@Override
	public void teach() {
		//呼叫父類的teach方法
		super.teach();
		//自己內部的部分
		System.out.println("我們就開始開車");
	}
}

public class OldDriver extends Teacher {

	@Override
	public void teach() {
		//呼叫父類的teach方法
		super.teach();
		//自己內部的部分
		System.out.println("我們就上職業素養課,飆車");
	}
}
步驟3:建立一個教室,宣告一個上課的方法,這個方法指定特定的老師來上課
public class Classroom {
	//給這個班級派老師過來上課 
	public void showLesson(JavaTeacher javaTeacher) {
		javaTeacher.teach();
	}

	//給這個班級派老師過來上課
	public void showLesson(Driver driver) {
		driver.teach();
	}

	//給這個班級派老師過來上課 
	public void showLesson(OldDriver oldDriver) {
		oldDriver.teach();
	}

}
步驟4:測試
public class TestClassroom {

	public static void main(String[] args) {
		//先建立教室
		Classroom classroom=new Classroom();
		//再派老師上課
		JavaTeacher javaTeacher=new JavaTeacher();
		javaTeacher.setName("小張");
		//執行上課的方法
		classroom.showLesson(javaTeacher);//javaTeacher:實際引數->實參
		
		Driver driver=new Driver();
		driver.setName("小李");
		classroom.showLesson(driver);
		
		
		OldDriver oldDriver=new OldDriver();
		oldDriver.setName("小王");
		classroom.showLesson(oldDriver);

	}
    
}

小張來了
我們開始上正經的java課程
小李來了
我們就開始開車
小王來了
我們就上職業素養課,飆車
[問題]:
每增加一個老師,這個類中就要多一個方法;
public class TestClassroom {
    public void showLesson(JavaTeacher javaTeacher) {
		javaTeacher.teach();
	}
}
[改進]:
將父類作為一個方法的引數,定義方法的時候,並不確定這個引數的實際物件;[因為前面3種老師都是Teacher的子類,就可以把形式引數改成父類的引用,然後再呼叫的時候將子類的物件作為實參傳進去];
public void showLesson(Teacher teacher) {//形式引數->形參
		teacher.teach();
	}

在這裡插入圖片描述

對於不同的物件,JavaTeacher的物件和Driver的物件,OldDriver的物件;執行的過程和結果可能是不同的;[這就是多型]

1.2 多型性定義

Java中多型性指允許不同類的物件對同一訊息做出響應。即同一訊息可以
根據傳送物件的不同而採用多種不同的行為方式
• 傳送訊息就是方法呼叫;
• 現實中,關於多型的例子不勝列舉。比方說按下 F1 鍵這個動作,如果當前在IE介面下彈出的瀏覽器的幫助文件;如果當前在 Word 下彈出的就是office幫助;在 Windows 下彈出的就是 Windows 幫助和支援
• 可見,同一個事件發生在不同的物件上會產生不同的結果,因此,多型的
主要作用適用於[消除型別之間的耦合關係];

1.3 多型實現方式1-使用繼承

也是我們[物件導向的特徵之一]
    
概念:多型是同一個方法具有多個不同的表現形態。
多型其實就是同一個父類(介面)的引用,使用不同的子類(實現類)而執行不同的操作。
    
操作步驟:建立父類, 建立子類, 重寫父類的方法;特定的場景中,將父類作為形式引數,實際引數為子類,執行的過程和結果和子類物件有關;
不同的物件,對於同一個方法執行過程和響應是不同的.

1.4 練習

樂器(Instrument)分為:鋼琴(Piano),小提琴(Violin)
各種樂器的彈奏(play)方法各不相同。
編寫一個測試類InstrumentTest,要求:
編寫方法testPlay,對各種樂器進行彈奏測試,要依據樂器的不同,進行相應的彈奏。
在main()方法中進行測試
/**
 * 自定義一個樂器類(父類)
 * @author Administrator
 *
 */
public class Instrument {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * 父類的play
	 */
	public void play() {
		System.out.println("開始演奏:"+name);
	}
	
}
/**
 * 自定義一個子類
 * @author Administrator
 *
 */
public class Piano extends Instrument {

	@Override
	public void play() {
		super.play();
		System.out.println(getName()+"朵蕾咪發~");
	}
}

/**
 * 自定義一個子類
 * @author Administrator
 *
 */
public class Violin extends Instrument {

	@Override
	public void play() {
		super.play();
		System.out.println(getName()+"啦啦啦啦啦~");
	}
}
public class InstrumentTest {
	//編寫方法testPlay,對各種樂器進行彈奏測試,要依據樂器的不同,進行相應的彈奏。
	public void testPlay(Instrument ins) {//多型的體現
		ins.play();
	}

	public static void main(String[] args) {
		InstrumentTest test=new	InstrumentTest();
		Piano piano=new Piano();
		piano.setName("鋼琴");
		test.testPlay(piano);
		
		Violin violin=new Violin();
		violin.setName("小提琴");
		test.testPlay(violin);

	}

}

開始演奏:鋼琴
鋼琴朵蕾咪發~
開始演奏:小提琴
小提琴啦啦啦啦啦~

1.5 多型總結:

• 可替換性(substitutability)
	• 多型對已存在程式碼具有可替換性;
• 可擴充性(extensibility)
	• 多型對程式碼具有可擴充性。增加新的子類不影響已存在類的多型性、繼	 承性,以及其他特性的執行和操作。實際上新加子類更容易獲得多型功能;
• 介面性(interface-ability)
	• 多型是超類通過方法簽名,向子類提供了一個共同介面,由子類來完善	 或者覆蓋它而實現的;
• 靈活性(flexibility)
	• 它在應用中體現了靈活多樣的操作,提高了使用效率;
• 簡化性(simplicity)
	• 多型簡化對應用軟體的程式碼編寫和修改過程,尤其在處理大量物件的運	 算和操作時,這個特點尤為突出和重要;

2. 抽象類與抽象方法

2.1 抽象類

存在一個類,這個類不太好描述,但是確實是一個類;通常一個類中會有屬性和方法 ;而這種類內部的方法又不太好實現;

[引入例子]:形狀是否能定義成一個類,如果可以請問該類可以擁有什麼屬性和方法?邊長?求面積的方法? 子類:圓形:R 矩形:w h
    
[出現問題]:如果將形狀設定為父類,此時其實沒有共同的屬性;

[解決問題]:可以將這個類變成抽象;
[概念]:
在物件導向的概念中,所有的物件都是通過類來描繪,但是反過來,並不是所有的類都是用來描繪物件的。
    
[如果一個類中沒有包含足夠的資訊來描繪一個具體的物件,這樣的類就是抽象類]。
    
基本結構: 
abstract class  類名{} 
public abstract class Shape {
	/**
	 * 求面積
	 * 這個方法應該沒有辦法實現,所以沒有方法體,但是這樣寫語法又是錯誤
	 */
	public abstract void getArea() ;//沒有方法體
	public abstract void getLength() ;
	public void test() {
		System.out.println("普通方法");
	}
	
}
public class Circle extends Shape{
	private double r;

	public double getR() {
		return r;
	}

	public void setR(double r) {
		this.r = r;
	}

	@Override
	public void getArea() {
		//計算面積
		System.out.println("圓的面積:"+(Math.PI*r*r));
	}

	@Override
	public void getLength() {
		//計算周長
		System.out.println("圓的周長:"+(Math.PI*r*2));
	}
 
	public void testCircle() {
		System.out.println("這是子類特有的方法");
	}
}
/**
 * 如果一個類繼承了一個抽象類,那麼必須首先重寫這個類的所有抽象方法?對不對
 * 這種說法不準確,因為還可以繼承抽象,讓下一代去做
 * @author Administrator
 *
 */
public abstract class Square extends Shape{
 
}
public class TestShape {

	public static void main(String[] args) {
		//抽象類不能直接例項化
		//1.父類  引用名=父類物件???  X
		//Shape shape=new Shape();
		//2.子類  引用名=子類物件
		Circle circle=new Circle();

		circle.setR(2.5);
		circle.getArea();//自己做實現
		circle.test();//從父類繼承過來的
        
圓的面積:19.634954084936208
普通方法
抽象類能否包含普通方法? [可以 反之不行];
如果某個類中包含抽象方法,那麼這個類就必須定義成抽象類;
(有抽象方法一定是抽象類,但抽象類不一定只有抽象方法)

抽象類的普通方法,子類能不能繼承?   [可以]

2.2 抽象類和抽象方法的特點

①抽象方法:被 abstract 修飾的方法就是抽象方法,沒有方法體。{}

②抽象類:一個類中如果有至少1個抽象方法,那麼這麼類必須也是抽象類,抽象類中也可以包含普通方法。
    
③抽象類不能直接例項化,只可以被繼承,如果一個類繼承了抽象類,需要重寫抽象類中的所有抽象方法(如果不想重寫可以把這個子類設定成抽象類)

在這裡插入圖片描述

④抽象類中有構造方法,但是不能被 abstract 修飾(被修飾了就必須要重寫,但構造方法不能被繼承),因為子類構造的時候需要呼叫父類的構造方法。

2.3 物件向上造型

[向上轉型]:父類的引用指向子類物件
    
	父類 引用名=new 子類()
    
	//父類 引用名=子類物件
    Shape s=new Circle();//向上轉型 父類引用指向子類物件 
    s.test();
    circle.testCircle();
    
	普通方法
	這是子類特有的方法
        
[注意]:
只能呼叫["父類的所有屬性和方法+子類和父類共有的屬性和方法"],對於["只存在子類中的方法或屬性"]無法呼叫;

2.4 抽象類的作用

為什麼要用抽象類:
分析,設計,定規則規範;A(分配 告訴大家要做什麼 設計, 不做具體實現) B(要做具體實現) C(要做具體實現).實現之前做一個標準->定規範->抽象類;

2.5【面試題】普通類和抽象類的區別

1. 普通類中不能包含抽象方法,但抽象方法中可以包含普通方法;一個類中有抽象方法存在就會被定義為抽象方法;
2. 抽象方法必須用 publicprotected 修飾(因為如果為 private 則子類無法繼承,也就無法重寫該方法);
4. 普通類可以例項化物件,抽象類則不可以直接例項化;
5. 如果一個類繼承抽象類,則需要實現父類的抽象方法;如果子類沒有實現父類的抽象方法,則必須將子類也定義為 abstract;

2.6練習:

在這裡插入圖片描述

public abstract class Draw {
	private int x;
	private int y;
	private String color;
    
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	/**
	 * 繪製方法(抽象方法)
	 */
	public abstract String  drawing();
	
	/**
	 * 有參構造->建立物件時直接對屬性賦值
	 * @param x
	 * @param y
	 * @param color
	 */
	public Draw(int x, int y, String color) {
		super();
		this.x = x;
		this.y = y;
		this.color = color;
	}
    //父類無參構造
	public Draw() {
        
	}
	
}
public class Line extends Draw {
	private int endX;
	private int endY;

	public int getEndX() {
		return endX;
	}
	public void setEndX(int endX) {
		this.endX = endX;
	}
	public int getEndY() {
		return endY;
	}
	public void setEndY(int endY) {
		this.endY = endY;
	}
	
    //重寫父類抽象方法
	@Override
	public String drawing() {
		
		return "線條的繪製功能 [endX=" + endX + ", endY=" + endY + ", X=" + getX() + ", Y=" + getY() + ", Color="
				+ getColor() + "]";
	}

    //子類有參構造
	public Line(int x, int y, String color, int endX, int endY) {
		
		super(x, y, color);
		this.endX = endX;
		this.endY = endY;
	}
	
}
public class Rectangle extends Draw{
	private int width;
	private int height;
	
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public int getHeight() {
		return height;
	}
	public void setHeight(int height) {
		this.height = height;
	}

    //重寫父類抽象方法
	@Override
	public String drawing() {
		
		return "矩形的繪製功能: [width=" + width + ", height=" + height + ", X=" + getX() + ", Y=" + getY()
		+ ", Color=" + getColor() + "]";
	}

    //子類有參構造
	public Rectangle(int x, int y, String color, int width, int height) {
		super(x, y, color);
		this.width = width;
		this.height = height;
	}

}
public class TestDraw {

	/**
	 * 多型
	 * @param draw 資料型別->父類 父引用
	 */
	public String testDraw(Draw draw) {
		return draw.drawing();//因為子類方法裡用的時return返回值,所以也要用return
	}
	
	public static void main(String[] args) {
		TestDraw test=	new TestDraw();
		//String line=test.testDraw(new Line(0,0, "black", 10, 20));
		//System.out.println(line);
 
		System.out.println(test.testDraw(new Line(0,0, "black", 10, 20)));//等價於下面這兩句
		/*Line line=new Line(0,0, "black", 10, 20);
		System.out.println(test.testDraw(line)); */
		
		System.out.println(test.testDraw(new Rectangle(0, 0, "red", 80, 90)));
	}

}

線條的繪製功能 [endX=10, endY=20, X=0, Y=0, Color=black]
矩形的繪製功能: [width=80, height=90, X=0, Y=0, Color=red]

相關文章