JAVA笔记_(Day09)抽象&接口&多态

抽象类

概述&特点

描述 狗。吼叫。
描述 狼。吼叫。
两个事物具备共性,向上抽取。犬科,具备吼叫功能。
描述一个事物,却没有足够信息。这时就将这个事物称为抽象事物。

面向抽象的事物,虽然不具体,但是可以简单化。
不用面对具有的事物。

特点:
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;
	}
}
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;
	}
}
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

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();
	}
}

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();
	}
}

练习

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值