天天看點

JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态

文章目錄

  • 抽象類
    • 概述&特點
    • 細節
    • 練習
  • 接口
    • 概述
    • 特點
    • 好處&作用
    • 避免單繼承的局限性
    • 接口多繼承
    • 抽象類是否可以不定義抽象方法
    • 接口的思想
    • 抽象類和接口的差別
  • 多态
    • 概述
    • 多态-向上向下轉型
    • 練習
    • 多态-USB接口練習
    • 多态中對成員的調用
      • 1.成員變量
      • 2.成員函數
      • 3.靜态函數
      • 練習

抽象類

概述&特點

描述 狗。吼叫。

描述 狼。吼叫。

兩個事物具備共性,向上抽取。犬科,具備吼叫功能。

描述一個事物,卻沒有足夠資訊。這時就将這個事物稱為抽象事物。

面向抽象的事物,雖然不具體,但是可以簡單化。

不用面對具有的事物。

特點:

1.抽象方法一定定義在抽象類中,都需要用abstract來修飾。

2.抽象類不能執行個體化,不能用new關鍵字建立對象。

3.隻有子類覆寫了所有的抽象方法後,子類具體化,子類就可以建立對象。

如果沒有覆寫所有的抽象方法,那麼子類還是一個抽象類。

抽象類也是不斷地向上抽取而來的。抽取了方法的聲明而不确定具體的方法内容。

由不同的子類來完成具體的方法内容。

abstract class 犬科//抽象類
//類也要辨別為abctract
{
  abstract void 吼叫(); //抽象函數
  //抽象關鍵詞abstract
}
           
class 狗 extends 犬科
{
  void 吼叫()
  {
     System.out.println("汪汪");
  }
}
           
class 狼 extends 犬科
{
  void 吼叫()
  {
     System.out.println("嗷嗷");
  }
}
           
class AbstractDemo
{
    public static void main(String[] args)
    {
     狗 x=new 狗();
     x.吼叫();
    } 
}
           

細節

問題:

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

有。抽象類的構造函數雖然不能給抽象類對象執行個體化。因為抽象類不能建立對象。

但是抽象類有子類,它的構造函數可以子類的對象執行個體化。

抽象類和一般類的異同點?

相同:都是用來描述事物的,都可以進行屬性和行為的描述。

不同:抽象類描述事物的資訊不具體,一般類描述事物的資訊具體。

代碼的不同:

抽象類中可以定義抽象方法,一般類不行。

抽象類不可以執行個體化,一般類可以。

2.抽象類一定是個父類麼啊?

是的。必須需要子類覆寫抽象方法後,才可以執行個體化。使用這些方法。

abstract class Demo extends XXX

{

}

class sub extends Demo

{

}

3.抽象類中可以不定義抽象方法嗎?

可以的。僅僅是讓該類不能建立對象。(技巧應用)

4.抽象關鍵字abstract和哪些關鍵字不能共存呢?

(非法修飾符子符)

final;子類不可以覆寫

private;子類不可以直接通路。

static;跑到靜态區,不需要建立對象,用類名就可以調用方法。抽象方法被調用沒有意義。

練習

需求:

公司中程式猿有姓名,工号,薪水,工作内容。

項目經理有姓名,工号,薪水,工作内容。還有獎金。

對給出需求進行資料模組化。

分析:問題領域中:

程式猿:

屬性:姓名,工号,薪水

行為:工作内容

項目經理:

屬性:姓名,工号,薪水,獎金

行為:工作内容。

兩者不存在所屬關系,但是有共性内容,可以向上抽取。

兩者的共性類型是什麼?雇員。

雇員:

屬性:姓名,工号,薪水

行為:工作内容

abstract class Employee {
    private String name;
    private String id;
    private double pay;
    
    Employee(String name,String id,double pay){
    	this.name=name;
    	this.id=id;
    	this.pay=pay;
    }
    //工作内容
    public abstract void work();
}
           
//描述程式猿
  class Programmer extends Employee
 {	
	  Programmer(String name,String id,double pay){
	       super(name,id,pay);
	    }
	  public void work(){
    	    System.out.print("code");
       }  
 }
           
//描述項目經理
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 work(){
 	    System.out.print("manager");
    }
}
           

接口

概述

抽象類中多有的方法都是抽象的。

這時,可以把抽象類用另一種形式來表示:接口。

初期可以了解為接口是特殊的抽象類。

abstract class AbsDemo{}
{
       abstract void show1();
       abstract void show2();
}
           

接口中的成員和class定義不同之處:

接口中常見的成員有兩種:1.全局常量;2.抽象方法。

而且都有固定的修飾符。

共性:成員都是public修飾的。

是以上述代碼可以改為:

//定義接口
interface Inter
{
      public static final int NUM=4;
      public abstract void show1();
      public abstract void show2();
}
           
class InterfaceDemo
{
     public static void main(String[] args){
        System.out.println("Hello World");
     }
}
           

特點

1.接口不可以執行個體化

2.需要覆寫了接口中的所有的抽象方法的子類,才可以執行個體化。否則,該子類還是一個抽象類。

3.接口是用來被實作的。

類與接口之間的關系是 實作關系。implements

實作方式如下:

interface Inter
{
      public static final int NUM=4;
      public abstract void show1();
      public abstract void show2();
}
           
class Demo implements Inter {
    public void show1() {};
    public void show2() {}; 
}
           
class InterfaceDemo {    
	public static void main(String[] args) {   	
		Demo d=new Demo();
    }
           

好處&作用

接口的好處或者解決了什麼問題?

多繼承:好處:可以讓子類具備更多的功能。弊端:調用的不确定性。

弊端舉例如下:

class Fu1
{
    void show1()
    {
        sop("fu1 show");
    }
}
           
class Fu2
{
    void show2()
    {
        sop("fu2 show");
    }
}
           
class Zi extends Fu1,Fu2
{
}
           
Zi z=new Zi();
z.show1();
z.show2();
           

這時候Zi同時繼承show1和show2,但如果将上述代碼中的show1和show2均改為show,就會産生調用的不确定性。

原因在于方法主體内容不同。

Java中不直接支援多繼承,而是對該機制進行改良。

通過接口來解決問題。将多繼承轉換成了多實作。

interface InterA
{
  void show1();
}
           
interface InterB
{
  void show2();
}
           
class SubInter implements InterA,InterB//多實作
{
      public void show1()
      {
          sop("inter show1");
      }
        public void show2()
      {
          sop("inter show2");
      }
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          subInter in=new subInter();
          in.show1();
          in.show2();
      };
}
           

如果把上述代碼中的show1和show2改為show呢?

interface InterA
{
  void show();
}
           
interface InterB
{
  void show();
}
           
class SubInter implements InterA,InterB//多實作
{
      public void show()
      {
          sop("inter show");
      }
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          subInter in=new subInter();
          in.show();
      };
}
           

直接實作有實體的部分,而不會産生不可調用的不确定性。

避免單繼承的局限性

一個類繼承一個類的同時,還可以實作多個接口。避免了單繼承的局限性。

繼承是為了擷取體系的基本功能。

想要擴充功能就可以通過實作接口來完成。

class Fu
{
    void show()
    {
        sop("fu show");
    }
}
           
class Zi extends Fu//因為繼承Zi具備所屬的體系具備了該體系的基本功能
{
    public void method()
    {
          sop("zi show");
    }
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          Zi z=new Zi();
          z.show();
          z.method();
      };
}
           

但如果還想擴充一些功能,具備show1功能。

class Fu
{
    void show()
    {
        sop("fu show");
    }
}
           
interface Inter
{
   void show1();
}
           
class Zi extends Fu implements  Inter
{
    public void method()
    {
          sop("zi method");
    }
    public void show1()
    {
         sop("zi show1");
    }
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          Zi z=new Zi();
          z.show();
          z.method();
          z.show1();
      };
}
           

接口多繼承

類與類之間 繼承關系. is a

類與接口之間 實作關系. like a

接口與接口之間關系:是繼承關系,而且可以多繼承。

interface Inter1
{
  void show1();
}
           
interface InterA
{
  void showA();
}
           
interface Inter2 extends Inter1,InterA//接口與接口之間可以多繼承,類不可以多繼承是因為主體調動不明确,而接口根本沒有主體。
{
  void show2();
}
           
class Demo implements Inter2//覆寫兩個才能執行個體化
{
     public void show1(){}
     public void showA(){}
     public void show2(){}
}
           

抽象類是否可以不定義抽象方法

interface Inter
{
    void show1();
    void show2);
    void show3();
    void show4();
}
           

//DemoA需要使用接口中的部分方法,比如使用show1.

class DemoA implements Inter
{
   public void show1(){
              sop("Demo show1");
   }
   void show2(){}
   void show3(){}
   void show4(){}
}
           

//DemoB需要使用接口中的部分方法,比如使用show3.

class DemoB implements Inter
{
   public void show3(){
              sop("Demo show1");
   }
   void show2(){}
   void show1(){}
   void show4(){}
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          DemoA a=new  DemoA();
          a.show1();
      };
}
           

這個代碼存在的問題就是:

隻需要部分功能,但是為了執行個體化,必須要全部覆寫。代碼的複用性很差。

是以上述代碼改成:

interface Inter
{
    void show1();
    void show2();
    void show3();
    void show4();
}
           

為了友善建立Inter接口的子類對象。

可以用一個類先把接口中的所有方法都空實作。該類建立對象沒有意義,是以可以将該類抽象。

這就是傳說中的沒有抽象方法的抽象類。

abstract class Demo implements Inter//Demo是空實作,那麼就可以用抽象類。即為沒有抽象方法的抽象類。友善建立接口的對象。
{
    void show1(){}
    void show2(){}
    void show3(){}
    void show4(){}
}
           

//DemoA需要使用接口中的部分方法,比如使用show1.

class DemoA extends Demo
{
   public void show1(){
              sop("Demo show1");
   }
}
           

//DemoB需要使用接口中的部分方法,比如使用show3.

class DemoB extend Demo
{
   public void show3(){
              sop("Demo show1");
   }
}
           
class InterfaceDemo
{
      public static void main(String[],args[])
      {
          DemoA a=new  DemoA();
          a.show1();
      };
}
           

接口的思想

筆記本電腦的USB接口

1.接口的出現擴充了功能。

2.接口其實就是暴露出來的規則。

3.接口的出現降低了耦合性。即解耦。

class Mouse
{
}
           
interface USB
{
}
           
class newMouse extends Mouse implements USB
{
}
           

接口的出現,一方在使用接口,一方在實作接口。

interface Inter
{
  void show();
}
           
class Demo implements Inter
{
  void show(){}
}
           
class Demoa implements Inter
{
       void show(){}
}
           

抽象類和接口的差別

例子:

描述犬:裡面有吃,叫,都是抽象的,具體由子類完成。

問題:定義成抽象類呢?還是定義成接口?

abstract class 犬
{
    abstract void 吃();
    abstract void 叫();
}
           

還是

interface 犬
{
    abstract void 吃();
    abstract void 叫();
}
           

添加一個功能,緝毒。單獨描述一個緝毒功能的犬。

class 緝毒犬 extends 犬
{
     void 吃();
     void 叫();
     void 緝毒();
}
           

還是

class 緝毒犬 inplements 犬
{
     void 吃(){}   
     void 叫(){}
     void 緝毒(){}
}
           

緝毒犬是犬中的一種。is a關系。

犬用于描述所有功能犬的基本功能。用class定義。父類。

是以犬不适合定義成接口。

具備緝毒功能的有很多,緝毒功能需要抽取。抽取到類中?還是抽取到接口中?

都試一下,先定義成類。

abstract class 緝毒
{
    abstract  void 緝毒();
}
           

不行,緝毒犬繼承犬類,就不能繼承其他類。因為類不能多繼承。

定義成接口試試。

interface 緝毒
{
    abstract  void 緝毒();
}
           
class 緝毒犬 extends 犬 implements 緝毒 
{
     void 吃(){}   
     void 叫(){}
     void 緝毒(){}
}
           

這是可行的。

類用于描述的是事物的共性基本功能。

接口用于定義的都是事物的額外功能。

抽象類和接口的差別?

1.類與類之間是繼承關系,is a.

類與接口之間是實作關系,like a.

2.類中可以定義抽象和非抽象方法,子類可以直接使用,或者覆寫使用。

接口中定義都是抽象方法,必須實作才能用。

多态

概述

多态:多種形态。

重點說的是:對象的多态性。

class 動物
{
}
class 狗 extends 動物
{
}
//狗 x=new 狗();
動物 x=new 狗();//狗執行個體既是狗類型,又是動物類型。多态性。
           

多态在程式中的展現,父類的引用或者接口的引用指向了子類的對象。

多态的好處:提高了代碼的擴充性。

多态的弊端:不能使用子類的特有方法。

多态的前提:

1.必須有關系,繼承,實作。

2.通常有覆寫。

abstract class Animal
{
     abstract void eat();
}
           
class Dog extends Animal {
    void eat() {
    	System.out.println("骨頭");  
    }
    void lookHome() {
    	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) {   	
		//Dog d=new Dog();
		//d.eat();
		//多态形式
		Animal a=new Dog();
		a.eat();
		Animal a1=new Cat();//向上轉型
		a1.eat();
        Dog d1=new Dog();
		Dog d2=new Dog();
		Dog d3=new Dog();
		method(d1);
		method(d2);
		method(d3);
		
		Cat c1=new Cat();
		Cat c2=new Cat();
		Cat c3=new Cat();
		method(c1);
		method(c2);
		method(c3);
               
        method (new pig());
		}
//定義了狗和貓的調用方式。但是如果再出現一個子類比如豬,那麼還要定義一個調用豬的方法。
//不利于擴充

//當面對共性類型時,所有的子類對象都可以接受。
//說明提高了代碼的擴充性
    public static void method(	Animal a)   //a=new Dog();a=new Cat();a=new Pig();
    {
        a.eat();
    }
    
    //既然調用的都是eat方法,而eat是動物共性行為,為什麼不面對動物呢?
    //直接定義共性類型的參數更合适。
/*	
	public static void method( d) {
		d.eat();
    }
	public static void method(Cat c) {
		c.eat();
    }
}
*/
           

多态-向上向下轉型

Animal a=new Cat();//向上轉型
           

向上轉型好處:隐藏了子類型,提高了代碼的擴充性。

弊端:隻能使用父類中的功能,不能使用子類特有功能,功能被限定住了。

如果不需要面對子類型,通過提高擴充性,或者使用父類的功能即可完成操作,就使用向上轉型。

class DuoTaiDemo {    
	public static void main(String[] args) {   	
		Animal a=new Dog();//向上轉型
		a.eat();
//如果想要使用子類的特有功能呢?比如看家。這時是不是就需要子類型。
       if(!(a instanceof Dog))
       {
           System.out.println("類型不比對");
           return;
       }
        Dog d=(Dog)a;//向下轉型
        d.eat();
        d.lookHome();
           

向下轉型

好處:可以使用子類型的特有功能。

弊端:面對具體的子類型。向下轉型有風險。容易發生ClassCastException.隻要轉換類型和對象類型不比對就會發生。

想要安全,必須要進行判斷。判斷一個對象是否比對某一類型,需要使用一個關鍵字 instanceof

如何使用:對象 instanceof 類型

什麼時候用向下轉型:需要子類型的特有方法時。但一定要判斷。

練習

畢外公

講課。

釣魚。

畢老師 extends 畢外公

講課。

看電影。

要求展現多态。

要看到向上轉型,向下轉型

//描述畢外公
class 畢外公
{
   public void 講課(){
        System.out.println("管理");
   }
    public void 釣魚(){
        System.out.println("釣魚");
   }
}
           
//描述畢老師
class 畢老師 extends 畢外公
{
    public void 講課(){
        System.out.println("技術");
     }
     public void 看電影(){
        System.out.println("看電影");
     }
}
           
Class DuoTaiTest
{
        public static void main(String[],args){
             //多态形式。
             畢外公 x=new 畢老師();//向上轉型
             x.講課();
             x.釣魚();
             
             //要想使用畢外公的特有方法 看電影
             畢老師 y=(畢老師)x;
             y.看電影();//向下轉型
             y.釣魚();
        }
}
           

轉型過程中,至始至終,之有子類對象在做着類型的變化。

多态-USB接口練習

階段一:

筆記本電腦運作。筆記本中有一個運作功能。

階段二:

想使用一個滑鼠。又有一個功能使用滑鼠。并多了一個滑鼠對象。

階段三:

還想使用一個鍵盤功能。又要多一個功能。

問題:每多一個功能就需要在筆記本對象中定義一個函數。不爽!擴充性差!

怎麼解決?

降低滑鼠,鍵盤等外圍裝置和筆記本電腦的耦合性。

階段一

//描述筆記本電腦
 class NoteBook
{
     void run() {
    	 System.out.println("book run");
     }
}
           
class USBTest {    
	public static void main(String[] args) {   	
		NoteBook book=new NoteBook();
		book.run();
	}
}
           

運作結果

book run
           

階段二

需要一個滑鼠。說明筆記本中多了一個使用滑鼠的功能。

多了一個對象:滑鼠。

class Mouse {
	  /**
	   * 開啟功能。
	   */
    public void open() {
    	System.out.println("Mouse open");  
    }
    /**
	   * 開啟功能。
	   */
    public void close() {
  	    System.out.println("Mouse close");  
    }
    
}
           
class NoteBook
{
    public void run() {
    	 System.out.println("book run");
     }
    /**
     * 定義使用滑鼠功能。滑鼠不确定,定義成參數。
     */
    public void useMouse(Mouse m) {
    	//判斷一下,m必須指向滑鼠對象才可以調用方法
    	if(m!=null) {
   	     m.open();
   	     m.close();
    	}
    }
}
           
class USBTest {    
	public static void main(String[] args) {   	
		NoteBook book=new NoteBook();
		book.run();
		book.useMouse(new Mouse());
	}
}
           

結果

book run
Mouse open
Mouse close
           

階段三:如果還想使用其他裝置呢?比如鍵盤,外置硬碟。

可以通過再筆記本對象中繼續定義useKey()等方法來完成。

但是出現問題:每增加一個裝置都需要不斷地改動筆記本類中的内容。擴充性太差,維護性也不好。

怎麼辦?

後期的裝置是不确定的,每多加一個裝置就加一個功能,就說明裝置和筆記本的耦合性太強。

我不要每次都面對具體的類型,隻要定義一個規則,讓後期的裝置都符合這個規則。這樣隻要面對規則就可以了。

java中可以通過接口的形式來完成規則的定義,進行解耦。

重新設計:

interface USB {
	  /**
	   * 開啟功能。
	   */
    public void open();
    /**
	   * 開啟功能。
	   */
    public void close();
    
}
           
class NoteBook
{
	 /**
	  * 運作
	  */
    public void run() {
    	 System.out.println("book run");
     }
    /**
     * 使用符合規則的外圍裝置。
     */
    public void useUSB(USB usb) {//定義了一個接口類型的引用。//USB usb=new MouseByUSB();多态,提高了筆記本的擴充性。
    	//判斷一下,m必須指向滑鼠對象才可以調用方法
    	if(usb!=null) {
   	     usb.open();
   	     usb.close();
    	}
    }
}
           
//添加滑鼠
 class MouseByUSB implements USB
 {	
	  public void open()
	  {
		  System.out.println("mouse open")	;	 
	  }
	  public void close(){
          System.out.println("mouse close");
       }      
 }
           
//添加鍵盤
 classKeyByUSB implements USB
 {	
	  public void open()
	  {
		  System.out.println("Key open")	;	 
	  }
	  public void close(){
          System.out.println("Key close");
       }      
 }
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		NoteBook book=new NoteBook();
		book.run();
		book.useUSB(null);
		book.useUSB(new MouseByUSB());
		book.useUSB(new KeyByUSB());
	}
}
           

結果:

book run
mouse open
mouse close
Key open
Key close
           

多态中對成員的調用

1.成員變量

當子父類中出現同名成員變量時,多态調用時,隻看調用該成員變量的引用所屬的類中的成員變量。

簡單地說:無論編譯或者運作,都看等号的左邊。

class Fu {
	 int num=4;
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Fu f=new Fu();
		f.num=8;
	}
}
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态
class Fu {
	 int num=4;
}
           
class Zi extends Fu
{
	 int num2=6;
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Zi z=new Zi();
		z.num=9;
		z.num2=10;
	}
}
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态
class Fu {
	 int num=4;
}
           
class Zi extends Fu
{
	 int num=6;
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Zi z=new Zi();
		System.out.println(z.num)
	}
}
           

結果

6
           

把上述代碼中最後一個class改為如下:

class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Fu f=new Zi();
		System.out.println(f.num);
	}
}
           

結果:

4
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态

2.成員函數

出現一模一樣函數時,

多态調用。

編譯時,看的是引用變量所屬的類中的方法。

運作時,看的是對象所屬的類的方法。

簡單說:編譯看左邊,運作右邊。

成員方法動态綁定到目前對象上。

class Fu {
	 void show(){
	      System.out.println("fu show");
	 }
}
           
class Zi extends Fu
{
	 void show(){
	       System.out.println("zi show");
	 }
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Zi z=new zi();
		z.show();
	}
}
           

結果

zi show
           

把上述代碼中最後一個class改為如下:

class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Fu f=new Zi();
		f.show();
	}
}
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态

3.靜态函數

出現一模一樣函數時,

多态調用。

編譯和運作時看引用變量所屬的類中的方法。

簡單說:編譯運作看左邊。

其實大家要知道,真正調用靜态方法時不需要對象的。直接類名調用。因為靜态方法綁定到類上。

是以上述情況,更多用于面試。

靜态跟對象沒有關系,隻跟類有關。

class Fu {
	 static void staticMethod(){
	      System.out.println("fu static method");
	 }
}
           
class Zi extends Fu
{
	 static void staticMethod(){
	      System.out.println("zi static method");
	 }
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Fu f=new zi();
		f.staticMethod();
	}
}
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态

練習

class Fu {
     int num=5; 
	 void show(){
	      System.out.println("num="+this.num);
	 }
}
           
class Zi extends Fu
{
	int num=6;
}
           
class ExtendsDemo3 {    
	public static void main(String[] args) {   	
		Fu f=new zi();
		f.show();
	}
}
           

結果

num=5
           
JAVA筆記_(Day09)抽象&接口&多态抽象類接口多态