抽象类
使用
定义
-
抽象类不能实例化对象
-
但成员变量、成员方法和构造方法的访问方式和普通类一样
-
抽象类必须被继承才能被使用
- 因此在设计阶段要决定要不要使用抽象类
-
父类包含了子类集合的常见的方法,但由于父类本身是抽象的所以不能使用这些方法
- 使用子类继承并实现自定义方法
-
-
在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
全局常量- 只能是
public
,privat
修饰会编译错误 - 声明时不需要
public static final
关键字
- 只能是
-
接口中的方法一般不能在接口中实现的
- 只能由接口实现类来实现,且方法都是共有的
-
Java 8 新增了接口的默认方法和静态方法
-
默认方法:接口可以实现,不需要实现类去实现其方法
-
只需在方法名前面加
default
关键字即可实现默认方法 -
目的是为了解决接口的修改与现有的实现不兼容的问题
-
多个默认方法
- 可以创建自身的默认方法重写接口的默认方法
- 使用
super
调用指定接口的默认方法
-
-
静态方法类似与默认方法
-
-
Java 9 新增支持私有方法、私有静态方法
- 接口中
private
方法不能是abstract
抽象方法abstract
抽象方法是公开的用于给接口实现类实现的方法,所以不能是private
- 私有方法只能在接口内部的方法里被调用
- private 权限为本类可见
- 私有静态方法可以在其他静态和非静态接口方法中使用
- 接口中私有非静态方法不能在私有静态方法内部使用
ava 8 之前 Java 8 Java 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
接口的实例上调用Object
的clone
方法,会抛出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不支持多继承,子类不能够继承多个类,但可以实现多个接口
- 在两者皆可的情况下优先使用接口
- 基本功能不断改变,使用抽象类
- 不断改变基本功能并且强行使用接口,那么需要同时改变所有实现了该接口的类