全部章節 >>>>
本章目錄
4.1 抽象類
如何定義圖形類計算周長的通用方法?
- 不同的圖形子類圖形計算周長的方式大相徑庭,所以導致Shape類的calcPerimeter()方法無法運用某個固定的計算圖形周長的公式。
- 可以將calcPerimeter()方法定義爲抽象方法,抽象方法沒有具體的方法實現,該方法必須由其繼承的子類重寫,這樣該方法就起到了約束規範的作用,又不影響類最初的設計思路。
4.1.1 抽象方法和抽象類
定義抽象類語法
[訪問修飾符] abstract class 類名
定義抽象方法語法
[訪問修飾符] abstract 返回類型 方法名([參數列表])
抽象類和抽象方法規則:
一個抽象類中可以不定義抽象方法,但是隻要類中有一個抽象方法,則該類一定是抽象類。
抽象類不能被實例化,即不能被new創建一個實例對象。
如果一個子類繼承一個抽象類,則子類需要通過覆蓋的方式來重寫該抽象類中的所有抽象方法。如果子類沒有完全重寫抽象父類中所有的抽象方法,則子類仍是抽象的。
抽象方法可以與public、protected複合使用,但不能與final、private和static複合使用。
abstract 不能用於修飾屬性,不能用於修飾局部變量,也不能用於修飾構造器。
示例:在圖形繼承關係中使用抽象類和抽象方法
// 抽象類 圖形Shape :
public abstract class Shape { // 抽象類 圖形
private String color;
// 定義一個計算周長的抽象方法
public abstract double calcPerimeter();
// 定一個返回圖形子類型的方法
public abstract String getType();
// 有參構造方法
public Shape(String color){
this.color=color;
System.out.println("---執行了Shape類的構造方法---");
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
// 子類 三角形Triangle :
public class Triangle extends Shape{ //子類 三角形
private double x,y,z; //定義三角形邊
public Triangle(String color,double x,double y,double z) {
super(color); //調用父類構造器
this.setSide(x, y, z);
}
public void setSide(double x,double y,double z){//設置三角形三邊
if(x>y+z || y>x+z || z>x+y){
System.out.println("三角形兩邊之和必須大於第三邊");
return ;
}
this.x=x, this.y=y,this.z=z;
}
public double calcPerimeter() {//重寫父類計算周長方法
return x+y+z;
}
public String getType() {//重寫父類獲取圖形類型方法
return "三角形";
}
}
//子類 圓形Circle :
public class Circle extends Shape { //子類 圓形
private double r;//半徑
public Circle(String color,double r) {
super(color);
this.r=r;
}
public double calcPerimeter() {//重寫父類計算周長方法
return 2*Math.PI*r;
}
//重寫父類獲取圖形類型方法
public String getType() {
return "圓形";
}
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
main方法
public static void main(String[] args) { // 測試
Shape triangle=new Triangle("紅色", 3, 4, 5);
Shape circle=new Circle("紅色", 4);
System.out.println(triangle.getType()+" 周長="+triangle.calcPerimeter());
System.out.println(circle.getType()+" 周長="+circle.calcPerimeter());
}
4.1.2 抽象類的作用
避免子類設計的隨意性:
- 抽象類不能被實例化,只能作爲父類繼承。
- 從多個具有相同特徵的類中抽象出一個抽象類,以該抽象類作爲其子類的模板。
體現了模板模式的設計理念:
- 抽象類作爲多個子類的通用模板。
- 子類在抽象類的基礎上進行擴展、改造,但子類總體上會保留抽象類的行爲方式。
- 編寫一個抽象父類,父類提供多個子類的通用方法,並且將一個或多個方法留給其子類實現,這就是一種模板模式。
示例:在速度表應用上使用模板設計模式
// 抽象父類速度表 SpeedMeter ,該抽象類爲模板類
public abstract class SpeedMeter { //抽象父類速度表,該抽象類爲模板類
private double trunRate; //轉速
//將返回車輪半徑的方法定義爲抽象方法
public abstract double getRedius();
//定義計算速度的通用算法
public double getSpeed(){
//速度=車輪半徑*2*π*轉速
return 2*3.14*getRedius()*getTrunRate();
}
public double getTrunRate() {
return trunRate;
}
public void setTrunRate(double trunRate) {
this.trunRate = trunRate;
}
}
// 子類汽車速度表 CarSpeedMeter
public class CarSpeedMeter extends SpeedMeter{ //子類汽車速度表
//重寫父類的獲取半徑的方法
public double getRedius() {
return 2.0;
}
public static void main(String[] args) {
//創建CarSpeedMeter對象
CarSpeedMeter csm=new CarSpeedMeter();
csm.setTrunRate(15);//設置轉速
System.out.println("當前汽車時速="+csm.getSpeed()+"公里/小時");
}
}
SpeedMeter類中提供了速度表的通用算法,但一些具體的實現細節則推遲到其子類CerSpeedMeter類中實現。這是一種典型的模板模式。
模板模式基本規則:
抽象父類僅定義使用的某些方法,將不能實現的部分抽象成抽象方法,留給其子類實現。
父類中包含需要調用其他依賴的方法,這些被調方法既可以由父類實現,也可以由其子類實現。父類中提供的方法僅定義了一個通用算法,需要具體子類輔助實現。
4.1.3 實踐練習
4.2 final修飾符
- final關鍵字可以用於修飾類、變量和方法。
- final修飾變量時,表示該變量一旦獲得了初始值就不可能被改變,final修飾的類不能被繼承,final修飾的方法可以被繼承但不能被重寫。
- final意味着終極。
4.2.1 final成員變量
- 對於final修飾的成員變量而言,一旦賦初始值,就不能被重新賦值。
- final修飾的類屬性只能在靜態初始化塊或聲明該屬性時指定初始值。
- final修飾的實例屬性只能在構造方法或聲明該屬性時指定初始值。
示例:演示final成員變量使用
4.2.2 final局部變量
- 系統不會對局部變量進行初始化,局部變量必須由程序員顯式初始化。因此,使用final修飾局部變量時,既可以在定義時指定默認值,也可以不指定默認值。
- final修飾的局部變量在定義時沒有指定默認值,則應在後面的代碼中對該final變量賦初始值,但只能賦值一次,不能重複賦值。
- 如果final修飾的局部變量在定義時已經指定默認值,則後面代碼中不能對該變量賦值。
示例:演示final局部變量使用
4.2.3 final方法
- final修飾的方法不可以被重寫。
- 如果不允許子類重寫父類的某個方法,則可以使用final修飾該方法。
示例:演示final方法使用
public class Bank {
//將該方法定義爲final,不允許子類重寫該方法
public final void LowerInterestRates(){
System.out.println(“央行按照宏觀經濟情況進行統一降息,各銀行按照國家標準統一執行");
}
}
public class ChinaBank extends Bank{
//如果子類重寫父類的fianl()方法,程序在編譯時將報錯
public final void LowerInterestRates(){
}
}
4.2.4 final類
- final修飾符修飾的類稱爲最終類,最終類不能有子類。
- final類通常具有固定作用,用於完成某種標準功能。
- final不能被繼承以達到不能被修改的目的。
示例:演示final類使用
public final class Bank { // final類
public void LowerInterestRates(){
System.out.println(“央行按照宏觀經濟情況進行統一降息,各銀行按照國家標準統一執行");
}
}
public class ChinaBank extends Bank{ //報錯,final類不能有子類
}
}
4.2.5 實踐練習
4.3 接口
舉例:生活中的接口
Java中的接口同生活中的接口一樣,體現的是一種能力。
4.3.1 接口的定義
語法:
[訪問修飾符] interface 接口名 extends 父接口1,[父接口2,...]
說明:
接口的訪問修飾符可以是public和缺省訪問修飾符,如果省略public修飾符,系統默認使用缺省訪問修飾符。
接口中只能定義公有的、靜態的常量,並且這些常量默認都是公有的、靜態的。
接口中的方法只能是公有的抽象方法,並且這些方法默認都是公有的、抽象的。
接口只能繼承多個接口,接口不能繼承類,也不能實現其他接口。
4.3.2 接口的實現
語法:
[訪問修飾符] class 類名 implements 接口1,[接口2,...]
說明:
接口是一種標準的體現。
接口不能用於創建實例,接口的主要作用是在設計程序時對其實現類進行規範和約束。
接口的主要用途就是被實現類實現。
一個類可以實現多個接口,從而實現多繼承。
示例:設計一個具有輸入功能,並且防水、防塵功能的鍵盤,以及一個防水、防塵和防盜功能的防盜門
public interface Input { //輸入接口
//定義輸入的標準,由其實現類實現具體的實現細節
public abstract void input(); // abstract 可省略
}
public interface Function { //功能接口
//接口只能定義常量, 而且必須是靜態常量
public static final String DEEP="30米";//防水深度
//防塵指數
int INDEX=5; //接口中的常量默認是公有的、靜態、終極的
//防水功能
public abstract void waterproof();
//防塵功能
void dust(); //接口中的方法默認是公有的、抽象的
}
public interface ExtendsFunction extends Function { //擴展功能
void antiTheft(); //防盜
}
public class AntitheftDoor implements ExtendsFunction { //防盜門實現擴展功能接口
//實現防水功能
public void waterproof() {
System.out.println("我是防盜門採用高科技的防水技術,防水深度:"+DEEP);
}
//實現防塵功能
public void dust() {
System.out.println("我是防盜門採用納米防塵技術防塵,防塵指數:"+INDEX+"顆星");
}
//實現防盜功能
public void antiTheft() {
System.out.println("我是防盜門採用密碼匹配防盜技術");
}
}
public class Keyboard implements Function,Input{ //鍵盤類實現功能接口和輸入接口
//實現防水功能
public void waterproof() {
System.out.println("我是鍵盤採用特殊的密封屏蔽技術實現防水,防水深度:"+DEEP);
}
//實現防塵功能
public void dust() {
System.out.println("我是鍵盤採用全硅膠材料實現防塵功能,防塵指數:"+INDEX+"顆星 ");
}
//實現Input接口中的輸入功能
public void input() {
System.out.println("我是鍵盤可以將敲擊鍵盤上的數據輸入到計算機中");
}
}
public class Tset{
public static void main(String[] args) {
//創建鍵盤對象
Keyboard keyboard=new Keyboard();
keyboard.dust();//調用鍵盤的防塵方法
keyboard.waterproof();//調用鍵盤的防水方法
keyboard.input();//調用輸入方法
System.out.println("------------------------------------");
//創建防盜門對象
AntitheftDoor antitheftDoor=new AntitheftDoor();
antitheftDoor.antiTheft();//調用防盜門的防盜方法
antitheftDoor.dust();//調用防盜門的防塵方法
antitheftDoor.waterproof();//調用防盜門的防水方法
}
}
4.3.3 抽象類和接口的區別
相同點:
接口和抽象類都不能被實例化,它們都位於繼承樹的頂端,用於被其他類實現和繼承。
接口和抽象類都可以包含抽像方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。
不同點:
接口中只能包含抽象方法,而抽象類則完全可以包含普通方法。
接口中不能定義靜態方法,而抽象類中可以定義靜態方法。
接口中只能定義靜態常量,不能定義普通變量,或非靜態的常量,而抽象類中則可以定義不同的屬性,也可以定義靜態的屬性。
接口中不包含構造器,而抽象類中可以包含構造器,抽象類中的構造器並不用於創建對象,而是讓其子類調用這些構造器來完成抽象類的初始化操作。
一個類最多只能有一個直接父類,包括抽象類,而一個類可以實現多個接口。通過實現多個接口可以彌補Java單繼承的不足。
4.3.4 實踐練習
4.4 接口編程案例
什麼是面向接口編程?
面向接口編程就是先把客戶的業務邏輯線提取出來作爲接口,接口業務的具體實現通過該接口的實現類來完成。
當客戶需求變化時,只需編寫該業務邏輯的新的實現類,通過更改配置文件中該接口的實現類就可以完成需求,而不需要改寫現有代碼,從而減少對系統的影響。
面向接口編程的優點:
降低程序的耦合性。在程序中緊密的聯繫並不是一件好的事情,因爲兩種事物之間聯繫越緊密,更換其中之一的難度就越大。
易於系統的擴展。
易於系統的維護。
4.4.1 接口編程實例
使用面向接口編程實現一個簡易的用戶管理系統
要求用戶按照系統在控制檯中的提示信息,做出相應的選擇。
系統接收到用戶輸入的指令後,給出相應的提示信息。
創建dto包,在該包中定義類UserInfo用於封裝用戶信息。
public class UserInfo { //用戶實體類
private String name;
private int age;
private String birthday;
private String address;
//省略屬性的setter和getter方法
}
創建dao包,在該包中定義一個維護用戶信息的接口UserInfoDao,在該接口中定義3個抽象方法,它們的功能分別是刪除用戶、更新用戶信息和保存用戶。
public interface UserInfoDao { //維護用戶信息接口
public abstract void deleteUser(UserInfo user);
public abstract void updateUser(UserInfo user);
public abstract void saveUser(UserInfo user);
}
在該dao包中定義一個實現UserInfoDao的用戶信息維護UserInfoDaoImpl,該類分別實現接口中所有的抽象方法。
public class UserInfoDaoImpl implements UserInfoDao { //實現用戶信息維護接口
public void saveUser(UserInfo user) {
System.out.println("執行新增方法");
System.out.println("姓名:"+ user.getName() +" 年齡:"+ user.getAge() + " 生日:"+ user.getBirthday() + " 家庭地址:"+ user.getAddress());
}
public void updateUser(UserInfo user) {
System.out.println("執行更新方法");
}
public void deleteUser(UserInfo user) {
System.out.println("執行刪除操作");
}
}
創建business包,在該包中定義UserInfoAccess類,在該類定義UserInfoDao對象屬性,該值爲實現UserInfoDao接口的具體對象。
在UserInfoAccess類定義服務方法service()實現主體業務。
public class UserInfoAccess {
private UserInfoDao userInfoDao=new UserInfoDaoImpl();
public void service(){
System.out.println("請選擇操作【1】添加【2】修改【3】刪除");
Scanner input=new Scanner(System.in);
String state=input.next();
if("1".equals(state)){
userInfoDao.saveUser();
}else if("2".equals(state)){
userInfoDao.updateUser();
}else if("3".equals(state)){
userInfoDao.deleteUser();
}
}
}
public class UserMain{
public static void main(String[] args) {
new UserInfoAccess().service();
}
}
4.4.2 實踐練習
總結:
一個抽象類中可以不定義抽象方法,但是隻要類中有一個抽象方法,則該類一定是抽象類。
抽象類不能被實例化,只能作爲父類繼承。
final關鍵字可以用於修飾類、變量和方法。final修飾變量時,表示該變量一旦獲得了初始值就不可能被改變,final修飾的類不能被繼承,final修飾的方法可以被繼承但不能被重寫。
接口中只能定義公有的、靜態的常量,並且這些常量默認都是公有的、靜態的;接口中的方法只能是公有的抽象方法,並且這些方法默認都是公有的、抽象的。
接口只能繼承多個接口,接口不能繼承類,也不能實現其他接口。