接口、多态

接口

基础性质

特征

  • 只要符合接口的规范,任何类都可以使用;
  • 是一种引用类型

构成

  • JDK8中可包含 常量 抽象方法 默认方法 静态方法;最重要的是其中的抽象方法
  • JDK9中还可包含私有方法 ;
  • 不能有构造方法!
构成内容声明格式注意事项调用方式备注
成员变量/常量修饰符可省略,默认且必须为 public static final必须赋值且值不可变通过接口名直接调用JDK8可用
抽象方法修饰符可省略,默认且必须为 public abstract(返回值和参数列表随业务需求);不可以有方法体必须被实现类重写通过实现类.方法名调用;JDK8可用
默认方法修饰符可省略,默认且必须为 public default(返回值和参数列表随业务需求);必须有方法体可以被(可以不被)实现类重写(若被重写则调用的是重写后的)通过实现类.方法名调用;JDK8可用
静态方法修饰符可省略,默认且必须为 public static(返回值和参数列表随业务需求);必须有方法体不可以被实现类重写通过接口名直接调用JDK8可用
私有方法修饰符可省略,默认且必须为 private (返回值和参数列表随业务需求);必须有方法体不可以被实现类重写实现类和实例不可调用;接口内部直接通过方法名调用仅JDK9
静态私有方法修饰符可省略,默认且必须为 private static(返回值和参数列表随业务需求);必须有方法体不可以被实现类重写实现类和实例不可调用;接口内部直接通过方法名调用仅JDK9

定义方式

以下几种方式均可定义接口和其中的抽象方法

package com.heima.study;

public interface demo {
	public abstract void abs1();
	public  void abs2();
	abstract void abs3();
	void abs4();
	String abs5();
}

  • 接口中的抽象方法默认且只能为public abstract,这两个关键字均可以省略;
  • 抽象方法必须没有方法体,但是返回值可以为任意类型;参数列表可以为任意类型和个数(根据业务需要);

基本使用步骤

接口不能直接使用,必须有实现类 实现接口(重写覆盖其全部抽象方法),然后实例化实现类的对象去使用;

  • 步骤1:定义接口类

      	public interface 接口名称{
      		//接口内容
      		public abstract void abs1();
      		public abstract void abs2();
      	}
    
  • 步骤2:定义实现类

      	public class 实现类名称 implements 接口名称{
      		//重写接口中的 所有 抽象方法
      		@override
      		public void  abs1(){
      			填入方法体;
      		}
      		public void  abs2(){
      			填入方法体;
      		}
      	}
    

如果不重写所有抽象方法,实现类本身也必须为抽象类;

  • 步骤3:测试类 将实现类实例化

      	public class 测试类名称{
      	//不能调用接口!!
      		实现类名 实例名称 =new 实现类();
      	//用实现类调用重写的接口的方法;
      		实例名称.abs1();
      		实例名称.abs2();
      		
      	}
    

默认方法

  • 默认方法必须有方法体
package com.heima.study;

public interface demo1 {
	public abstract void abs1();
	public  default void absA(){
		//方法体
	}
}

  • 默认方法可以解决接口升级的问题:
    接口升级时添加的新方法如果定义为 public default,则可以自动添加至实现该接口的所有实现类,可以直接被实现类的实例对象调用,避免因为新增的方法没被实现类重写而报错;
  • 默认方法可以不被重写(也可以被重写);
  • 使用方式:
    直接从实现类的实例调用默认方法,会向上→实现类重写的方法→接口的默认方法 层层查找去调用
package com.heima.study;

public class imp implements demo1 {
	public  void abs1() {
		//重写
	}
}
//无需重写默认方法absA

package com.heima.study;

public class testImp {
	imp test1= new imp();
	test1.abs1();
	test2.absA();//通过实现类的实例直接调用默认方法
}

静态方法

  • 静态方法必须有方法体
package com.heima.study;

public interface demo2 {
	public abstract void abs1();
	public  static void absB(){
		//方法体
	}
}

  • 默认方法不可以被重写;
  • 使用方式:
    直接 接口名.静态方法名
package com.heima.study;

public class imp2 implements demo2 {
	public abstract void abs1(){
	//重写抽象类,增加方法体
	}
	}
//不可重写默认方法absB
package com.heima.study;

public class testImp2 {
	imp2 test1= new imp2();
	test1.abs1();
	demo2.absB();//通过实现类的实例直接调用默认方法
}

私有方法

仅限JAVA9

问题场景:接口中的多个方法ABC间可能存在重复代码,需要抽取出来一个方法,直接在ABC中调用即可;但是如果直接定义成默认方法或者静态方法,会出现被实现类直接使用的问题;
解决方案:在接口中定义私有方法,仅供默认/静态方法调用使用;

  • 普通私有(解决默认方法间的重复)

      private 返回值类型 方法名称(参数列表){
      	方法体
      }
    
  • 静态私有(解决静态方法间的重复)

      private static 返回值类型 方法名称(参数列表){
      	方法体
      }
    

成员变量(=接口的常量)

  • 没有默认值,必须赋值,且值不可变( final );

  • 使用 接口名.名称 调用;

  • 默认且只能是public static final,可以不写,但默认仍是;

      private interface 接口名称{
      	public static final 数据类型 名称 =数据值;
      	public abstract void abs1();
      	public abstract void abs2();
      }
    

形式!!!(不可变的变量命名都如此)

应用问题

实现多个接口

ublic class MyImpl implements  MyInterfaceA,MyInterfaceB{

    @Override
    public void absA() {
        System.out.println("实现A");
    }

    @Override
    public void absB() {
        System.out.println("实现B");
    }
}
  • 多个接口或接口与父类中存在重名方法
    • 若需要实现的多个接口中有重名 抽象 方法都需要被重写,重写一次即可;
    • 若需要实现的多个接口中有重名 默认 方法,则该默认方法必须被重写,重写一次即可;
    • 若继承的**父类的方法 和 实现的接口的默认方法**存在重名,则使用实现类.该名字调用的是父类方法!!即继承和接口之间优先级:继承优先。无需重写!
  • 如果实现类没有重写所有接口中的全部抽象方法,实现类本身也必须为抽象类;

接口继承接口

  • 接口与接口间可以多继承

  • 多个父接口之间抽象方法重名,没关系,因为没有方法体,不存在冲突;

  • 默认方法如果与默认方法、默认方法与抽象方法间重名,子接口必须对默认方法重写,并且声明为default

      public interface MyInterface extends MyInterfaceB,MyInterfaceA {
      public abstract  void abs();
      	}
      	//此时MyInterface这个子接口中包含MyInterfaceB,MyInterfaceA 中的所有抽象方法;
    

多态性

extends 类继承类、接口继承接口;implements 类实现接口等 关系的产生,是多态性的前提;

释义

如小红,既是女人(有女人的形态),也是教师(有教师的形态),也是人(有人的形态);—— 一个对象有多种形态,即对象的多态性!

代码体现多态性

父类引用(等号左侧)指向子类对象(等号右侧);

	   父类名称 对象名 =new 子类名称();
	    接口名称 对象名 = new 实现类名称();
public class Fu {
    int num=10;

    public void shownum() {             //父类方法
        System.out.println(num);
    }
     public void shownumFu() {             //父类专有方法
        System.out.println(num);
    }
}
public class zi extends Fu{
    int num=20;
    int age=20;
     @Override            //子类重写父类方法
    public void shownum() {
        System.out.println(num);
    }
    public void shownumZi() {            //子类专有方法
        System.out.println(age);
    }
}
public class Multi {
    public static void main(String[] args) {
        Fu test=new zi();          //多态性写法
        test.shownumZi();       //编译失败!访问不到子类专有方法(2.2)
        test.shownumFu();       //结果是10,运行看右边,从子类向上查找,在父类找到并使用(2.1.1)
        test.shownum();         //结果是20,运行看右边,使用子类的成员方法和变量(2.1.2)
        test.num;               //是10,编译运行都看等号左边,访问的是Fu类
        test.age;               //编译失败!编译运行都看左边,访问不到子类变量,父类向上都没有该变量;
          
        
    }
}
访问成员方法

父类与子类有 重名方法 时,优先 从子类向父类 查找调用;

访问成员变量

父类与子类有 重名成员变量 时:
通过对象名访问成员变量
编译运行都看左边
则从 等号左边(父类)再向上 查找该变量;(即多态写法下,直接通过对象名称是访问不到子类专有的成员变量的!!!)

② 通过成员方法访问成员变量:
编译看左边,运行看右边
2.1 重名方法:
2.1.1子类没有覆盖重写过方法,则查找方法时子类没有,向上查到父类,使用的也是父类的成员变量;
2.1.2子类覆盖重写过该方法,则从子类向上查找,子类有则调用子类的方法,使用的自然是子类的成员变量;
2.2 不重名子类方法
不可能存在通过子类专有方法访问;该实例根本访问不到子类的成员方法!编译失败!(编译看左,运行看右,左边父类里没有子类专有方法,故编译失败)

应用场景

????

对象的转型

对象的向上转型
  • 即多态写法:右侧创建一个子类对象把它当做父类看待使用;父类引用(等号左侧)指向子类对象(等号右侧);

         父类名称 对象名 =new 子类名称();
    
  • 类似于:基本数据类型的自动类型转换;(也可以自动转换:可以直接将实现类/子类 作为 接口类/父类类型的参数传递 )

  • 是否安全:是。因为是从小范围转向大范围!

  • 是否全面:否;因为无法调用子类的特有方法或变量!

对象的向下转型(还原
   子类名称 对象名 =(子类名称)父类对象名;
public class animal{
	public void hunt(){
		 System.out.println("捕猎");
	}
}
public class cat extends animal{
	@override
	public void hunt(){
	  System.out.println("捕鱼");
	}
	public void eat(){
	  System.out.println("吃鱼");
	}
}

public class test{
	 public static void main(String[] args){
	 	animal a1= new cat();
	 	a1.hunt();//正常编译运行 “捕鱼”
	 	a1.eat();//编译失败
	 	
	 	cat c1=(cat) a1;
	 	c1.eat();//正常编译运行 “吃鱼”
	 }
}

  • 类似于:引用类型的强制类型转换;

  • 应用场景:需要调用子类的专有方法和变量时;向下转型回去即可。

  • 是否安全:否,可能会出现转型错误(比如 dog d1=(dog) a1;强行把猫动物转成狗)编译可以通过但运行报错;

  • 判断还原是否安全:instanceof

     父类对象 instanceof 子类名称  //返回布尔值,true:是子类
    
 public static void main(String[] args) {
        Fu test=new zi();
        if(test instanceof zi) {
            zi test2 = (zi) test;
            test2.shownum2();
        }else{
            System.out.println("不要转,原类型不是zi");
        }
    }

应用场景

	举例
	宠物公司提供了animal类,其中具体有cat类和dog类(定义animal类,定义cat和dog类继承animal类)
	我家想有个宠物(ourpet类),随便什么宠物都行,于是让宠物公司送来“随便什么宠物”(传参父类即可animal 类型 的animal);
	送来之后根据具体类型可以陪我做不同的事(需要调用子类方法);
	所以送来之后我先看看是啥(instanceof),然后让他做该做的事(向下转型,调用子类方法)。
	
	* 即由于参数是个父类,接收到参数必须判断才知道参数所属的子类类型;参数可能是用户或其他人提供的~
public class animal{
	public void hunt(){
		 System.out.println("捕猎");
	}
}
public class cat extends animal{
	@override
	public void hunt(){
	  System.out.println("捕鱼");
	}
	public void play(){
	  System.out.println("陪玩");
	}
}
public class dog extends animal{
	@override
	public void hunt(){
	  System.out.println("捕鼠");
	}
	public void watch(){
	  System.out.println("看家");
	}
}

public class ourPet(Animal animal){
	 public static void main(String[] args){
	 	 if(animal instanceof cat) {
            cat c = (cat) animal;
            c.play();
        }
         if(animal instanceof dog) {
            dog d = (dog) animal;
            d.watch();
        }
	 }
}

举例2

	笔记本电脑:有开机功能、关机功能、使用USB设备功能
	USB设备:有插入使用、移除关闭功能
	键盘:是一种USB设备,除了USB设备都有的功能外,还有输入功能
	鼠标:是一种USB设备,除了USB设备都有的功能外,还有点击功能
	
	* 向上转型可以自动转换:可以直接将实现类/子类 作为 接口类/父类类型的参数传递 
//定义电脑类
public class laptop {
//定义开关机方法
    public void poweron(){
        System.out.println("开机");
    }
    public void poweroff(){
        System.out.println("关机");
    }
//定义使用usb设备方法
    public void usedevice(usb u){
//调用usb设备的通用方法(被子类重写过,执行的时候执行子类方法)
        u.open();
//由于点击和输入是实现类的方法,所以需要判断、向下转型、然后调用
        if(u instanceof mouse ){
            ((mouse) u).clickmouse();
//不建议直接用else,因为实际应用中可能有很多种,不是非A即B;
        }else if(u instanceof keyboard ){
           ((keyboard) u).input();
        }
//调用usb设备的通用方法(被子类重写过,执行的时候执行子类方法)
        u.close();
    }
}


//定义接口,包含打开关闭2种抽象方法
public interface usb {
    public  abstract void open();
    public  abstract void close();
}


//定义鼠标类,实现usb接口
public class mouse implements usb {
//重写打开关闭设备方法
    @Override
    public void open() {
        System.out.println("插入鼠标");
    }

    @Override
    public void close() {
        System.out.println("移除鼠标");
    }
//定义子类专有方法
    public void clickmouse(){
        System.out.println("点击鼠标");
    }
}

//定义键盘类,实现usb接口
public class keyboard implements usb {
//重写打开关闭设备方法
    @Override
    public void open() {
        System.out.println("打开键盘");
    }

    @Override
    public void close() {
        System.out.println("关闭键盘");
    }
//定义子类专有方法
    public void input() {
        System.out.println("键盘输入");
    }
}



//测试使用
public class testUse {
//准备一个电脑
    public static void main(String[] args) {
        laptop lap1= new laptop();
//开机
        lap1.poweron();
//***********
//准备一个鼠标和键盘,同时向上转型,多态写法
        usb m=new mouse();
        usb k =new keyboard();
//准备一个鼠标和键盘,也可以使用下方写法,直接创建接口的实现类的实例
        // mouse m=new mouse(); 
        // keyboard k =new keyboard(); 
//使用设备,传USB类型参数
        lap1.usedevice(m);
        lap1.usedevice(k);
        lap1.poweroff();//关机
    }
}



结果:	
	开机
	打开鼠标
	点击鼠标
	关闭鼠标
	打开键盘
	键盘输入
	关闭键盘
	关机
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值