Java基础之抽象类、接口

抽象类

使用

定义
  • 抽象类不能实例化对象

    • 但成员变量、成员方法和构造方法的访问方式和普通类一样

    • 抽象类必须被继承才能被使用

      • 因此在设计阶段要决定要不要使用抽象类
    • 父类包含了子类集合的常见的方法,但由于父类本身是抽象的所以不能使用这些方法

      • 使用子类继承并实现自定义方法
  • 在Java中抽象类表示的是一种抽象关系

    • 一个类只能继承一个抽象类,但一个类可以实现多个接口
  • Java中使用abstract来定义抽象类

    • 其中定义的抽象方法,无方法体:为一种约束
  • 抽象类中可以有普通方法,但抽象方法必须在抽象类中

  • 抽象类不能实例化对象,但是其他功能依旧存在

    • 成员变量、成员方法和构造方法的访问方式和普通类一样
    • Employee 类虽然是抽象类,仍然有2个成员变量,3个成员方法和1个构造方法
public abstract class Employee {
    private String name;
    private String address;
    public Employee(String name, String address) {
        System.out.println("Employee 构造函数");
        this.name = name;
        this.address = address;
    }
    public void mailCheck() {
        System.out.println("邮寄支票给: " + this.name + " " + this.address);
    }
    public String getName() {
        return name;
    }
    public String toString() {
        return name + " " + address + " " ;
    }
}
继承
  • 抽象类不能被实例化,所以需要实例化其子类 Salery
    • Salary类对象 将从 Employee 类继承 3 个成员方法
    • 通过该方法可以设置或获取1个成员变量
public class Salary extends Employee {
    private double salary; 						// 全年工资
    public Salary(String name, String address, double salary) {
        super(name, address);					// 显式声明调用父类有参构造
        System.out.println("Salsry调用函数");
        this.salary = salary;					// 构造器初始化,赋值到成员变量
    }
    public void mailCheck() {
        System.out.println("Salary 类的 mailCheck 方法 ");
        System.out.println("邮寄支票给:" + getName() + " ,工资为:" + salary);		// 调用父类 getName() 方法和奔雷 salery 属性
    }
}

public class VirtualDemo {
    public static void main(String [] args) {
        Salary s = new Salary("员工 A", "北京", 3600.00);				// 实例化对象 Salary
        Employee e = new Salary("员工 B", "上海", 2400.00);				// 实例化父类对象
        System.out.println("使用 Salary 的引用调用 mailCheck -- ");
        s.mailCheck();		//调用s.mailCheck(),编译器编译时在 Salary 类中找到了mailCheck(),执行过程JVM就调用 Salary 类的 mailCheck()
        System.out.println("使用 Employee 的引用调用 mailCheck--");
        e.mailCheck();
    }
}
运行结果:
Employee 构造函数													// 实例化第一个对象;先调用父类有参构造
Salsry调用函数														// 实例化第一个对象;调用自身的有参构造
Employee 构造函数													// 实例化第二个对象;调用父类有参构造
Salsry调用函数														// 实例化第二个对象;调用自身有参构造
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 											// 子类对象调用子类方法
邮寄支票给:员工A ,工资为:3600.0
使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 											// 虽然是父类对象,但实际类型还是子类类型;所以调用子类方法
邮寄支票给:员工B ,工资为:2400.0
/**
e 是 Employee 的引用,但引用 e 最终运行的是 Salary 类的 mailCheck() 方法
在编译时编译器使用 Employee 类中的 mailCheck() 方法验证该语句(Employee类中是否存在该方法)
但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法
*/

规定

  • 抽象类不能被实例化(初学者很容易犯的错)
    • 如果被实例化会报错,编译无法通过
    • 只有抽象类的非抽象子类可以创建对象。
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
  • 抽象类中的抽象方法只声明,不包含方法体
    • 只定义方法模板,由实现子类自己对方法进行实现
  • 构造方法,类方法(static 修饰的方法)不能声明为抽象方法
    • 此类方法不能被继承重写
  • 抽象类实现子类必须实现父类的抽象方法
    • 除非该子类也是抽象类

抽象方法

定义
  • 类中包含的特别成员方法

    • 方法的具体实现由子类确定,在父类中声明该方法为抽象方法
  • Abstract 关键字用来声明抽象方法

    • 抽象方法只包含一个方法名,没有方法体
    • 抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号
  • public abstract class Employee{
       private String name;   						// 字段
       public abstract double computePay();  		// 抽象类中定义的抽象方法
    }
    
要求
  • 一个类包含抽象方法,该类必须是抽象类

  • 子类必须重写父类中的抽象方法

    • 除非自身也是抽象类

    • 最终必须有子类实现该抽象方法

      • 否则从最初的父类到最终的子类都不能用来实例化对象
public class Salary extends Employee{
   public double computePay(){			// 实现父类中的抽象方法
      System.out.println();
      return salary/52;
   }
}

接口

定义

  • 接口interface:本质是契约,制定规则

    • 接口需要实现类;命名一般为后加Impl
  • 接口并不是类,编写接口的方式跟类和相似;但是不同的概念

    • 类描述对象的属性和方法
    • 接口包含类要实现的方法
  • 接口无法被实例化,但可以被实现

    • Java中接口类型可用来声明变量,可以成为一个空指针
    • 或被绑定在一个以此接口实现的对象
      • 通过接口实现多态的关键
  • 权限修饰符 interface 接口名 (extends 其他接口名){
        public static final ... = ...;				// 声明变量;默认即是 公开的静态常量
        public abstract ...();  					// 抽象方法;默认即是 公开的抽象方法
    }
    
    public interface NameOfInterface{
    	String name = " 张三 ";					   // 默认为 public static final 类型,必须初始化
        public void getName();						// 默认是 public abstract;不需要显式声明
        // Java 8之后允许
        default void print(){}						// 默认方法;可以实现该方法
        static void blowHorn(){}					// 静态默认方法;可以实现方法
       //Java 9之后允许
        private void a(){}							// 私有方法,可实现
        private static void Test(){}				// 私有静态方法,可实现
    }
    

特性

  • 定义一些通用方法作为约束

  • 接口中方法都是隐式抽象的

    • 被隐式的指定为 public abstract
    • 声明时不需要abstract关键字,建议加上 public 关键字
  • 接口中可以有变量,会被隐式指定为 public static final 全局常量

    • 只能是 publicprivat 修饰会编译错误
    • 声明时不需要public static final关键字
  • 接口中的方法一般不能在接口中实现的

    • 只能由接口实现类来实现,且方法都是共有的
  • Java 8 新增了接口的默认方法和静态方法

    • 默认方法:接口可以实现,不需要实现类去实现其方法

      • 只需在方法名前面加 default 关键字即可实现默认方法

      • 目的是为了解决接口的修改与现有的实现不兼容的问题

      • 多个默认方法

        • 可以创建自身的默认方法重写接口的默认方法
        • 使用 super 调用指定接口的默认方法
    • 静态方法类似与默认方法

  • Java 9 新增支持私有方法、私有静态方法

    • 接口中private方法不能是abstract抽象方法
      • abstract抽象方法是公开的用于给接口实现类实现的方法,所以不能是private
    • 私有方法只能在接口内部的方法里被调用
      • private 权限为本类可见
    • 私有静态方法可以在其他静态和非静态接口方法中使用
    • 接口中私有非静态方法不能在私有静态方法内部使用
    ava 8 之前Java 8Java 9
    常量常量常量
    抽象方法抽象方法抽象方法
    默认方法(可实现)默认方法(可实现)
    静态方法静态方法
    私有方法(可实现)
    私有静态方法
  • 调用方法

    • 静态方法只能通过接口名调用

    • default 方法只能通过接口实现类的对象来调用

接口实现

语法
  • 当类实现接口的时候,要实现接口中所有的抽象方法
    • 否则必须声明为抽象类
  • 使用implements关键字实现接口
    • 在类声明中,implements类名
    • 一个类可以同时实现多个接口,接口名使用,分隔
public class 类名 implements 接口名(, 其他接口名, ...){}			// 基本语法

public class Dog implements Animal{								// Dog 类实现 Animal 接口
   public void eat(){
      System.out.println("Mammal eats");
   }
   public void travel(){
      System.out.println("Mammal travels");
   } 
   public int noOfLegs(){
      return 0;
   }
   public static void main(String args[]){
      Dog m = new Dog();
      m.eat();
      m.travel();
   }
}
运行结果:
Mammal eats
Mammal travels
方法实现
  • 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常
  • 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型
  • 如果实现接口的类是抽象类,那么就没必要实现该接口的方法
接口继承
原则
  • 一个接口能继承另一个接口,和类之间的继承比较相似
  • 继承使用extends关键字,子接口继承父接口的方法
  • 实现接口的类要实现接口自身的方法还有接口继承来的方法
多继承
  • 类的多继承是不合法,但接口允许多继承
  • 接口的多继承中extends关键字只需要使用一次,后跟着继承的所有接口
public class a extends b,c,d{}

标记接口

作用
  • 最常用的继承接口是没有包含任何方法的接口

  • 标记接口没有任何方法和属性的接口

    • 仅仅表明它的子类属于一个特定的类型,供其他代码来测试允许做一些事情

    • 给某个对象打个标志,使对象拥有某个或某些特权

// java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义
package java.util;
public interface EventListener{}
目的
  • 建立一个公共的父接口

    • EventListener接口,由几十个其他接口扩展的Java API
    • 可使用标记接口建立一组接口的父接口
      • 例如:一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将被用于一个事件的代理方案
  • 向一个类添加数据类型

    • 标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(
      • 标记接口根本就没有方法,但该类通过多态性变成一个接口类型
  • 使用标记接口使得类实例对象可以用 instanceof 进行类型查询

    • java.io.Serializable
      • 未实现此接口的类将无法使其任何状态序列化或反序列化
      • 为保证 serialVersionUID 值跨不同 java 编译器实现的一致性
        • 序列化类必须声明一个明确的 serialVersionUID
    • java.lang.Cloneable
      • 表明 Object.clone() 方法可以合法地对该类实例进行按字段复制
      • 实现此接口的类应该使用公共方法重写 Object.clone()(受保护的)
      • 未实现 Cloneable 接口的实例上调用 Objectclone 方法,会抛出 CloneNotSupportedException 异常
    • java.util.RandomAccess
      • 表明支持快速(通常是固定时间)随机访问
      • 此接口的主要目的是允许一般的算法更改其行为
        • 从而在将其应用到随机或连续访问列表时能提供良好的性能。
    • java.rmi.Remote
      • 用于标识其方法可以从非本地虚拟机上调用的接口
      • 任何远程对象都必须直接或间接实现此接口
        • 只有在 远程接口 (java.rmi.Remote的接口)中指定的方法才可远程使用。

接口与类

相似点
  • 一个接口可以有多个方法
  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名
  • 接口的字节码文件保存在 .class 结尾的文件中
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中
区别
  • 接口不能用于实例化对象
  • 接口没有构造方法
  • 接口中所有方法必须是抽象方法
    • Java8之后接口中可使用default方法、静态方法,Java9 定义接口私有方法、私有静态方法
  • 接口不能包含成员变量,但能有 public static final 全局常量
  • 接口不是被类继承了,而是要被类实现
  • 接口支持多继承:一个类可以实现多个接口,一个接口可以继承多个接口

抽象类和接口

区别

语法层面上的区别
  • 抽象类实现方法,接口中只能存在public abstract 方法
    • JDK8 后接口的 default 、静态方法 和 JDK 9 之后接口的私有、私有静态方法可以实现
  • 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型的
  • 抽象类可以有静态代码块和静态方法;接口中不能
    • Java8之后接口支持静态方法,Java9之后支持私有静态方法
  • 一个类只能继承一个抽象类,但可以实现多个接口
设计层面上的区别
  • 抽象类是对一种事物的抽象,对类整体进行抽象,包括属性、行为;接口是对类局部(行为)进行抽象
    • 如果类继承了某个抽象类,则子类必定是抽象类的种类
    • 而接口实现则是有没有、具备不具备的关系
      • 比如鸟是否能飞(或者是否具备飞行这个特点)
      • 能飞行则可以实现这个接口,不能飞行就不实现这个接口
  • 设计层面不同,抽象类作为很多子类的父类,是一种模板式设计;接口是一种行为规范,是一种辐射式设计
    • 抽象类需要添加新的方法,可直接在抽象类中添加具体的实现,子类可以直接继承,不进行变更
    • 对于接口则不行,接口进行变更,所有实现这个接口的类都必须进行相应的改动
      • JDK8 后 接口可以实现默认、静态方法也无需子类进行变动

使用

  • 拥有一些方法且让其中的一些有默认实现,使用抽象类
  • 实现多重继承,必须使用接口
    • Java不支持多继承,子类不能够继承多个类,但可以实现多个接口
    • 在两者皆可的情况下优先使用接口
  • 基本功能不断改变,使用抽象类
    • 不断改变基本功能并且强行使用接口,那么需要同时改变所有实现了该接口的类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值