抽象方法和抽象类
简述
抽象方法:
使用 abstract 修饰的方法,没有方法体,只有声明。
定义的是一种 “规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类:
包含抽象方法的类就是抽象类。
通过 abstract 方法定义规范,然后要求子类必须定义具体实现。
通过抽象类,可以做到严格限制子类的设计,使得子类之间更加通用。
抽象类使用要点
- 有抽象方法的类只能定义成抽象类。
- 抽象类不能实例化,即不能用 new 来实例化抽象类。
- 抽象类可以包含属性、方法、构造方法。但是构造方法不能用 new 来实例,只能用来被子类调用
- 抽象类只能用来被继承,不能被直接调用进行实例化。。
- 抽象方法必须被子类实现。
代码示例:测试抽象类和抽象方法的基本用法
package cn.jungle.polymophism;
// 定义抽象类
abstract class Animal1{
// 定义抽象方法,规定子类必须实现的方法
abstract public void shout();
}
class Dog1 extends Animal1{
// 子类必须实现父类的抽象方法,否则编译错误
public void shout(){
System.out.println("子类必须实现父类的抽象方法,否则编译错误");
System.out.println("听到有狗的警告叫声!");
}
public void seeDog(){
System.out.println("发现一只狼狗正在看门!");
}
}
public class TestAbstract {
public static void main(String[] args) {
Dog1 a = new Dog1();
a.shout();
a.seeDog();
}
}
接口 interface
简述
接口:将设计与实现彻底分开。
接口即是规范,定义的是一组规则。思想为:“如果你是……则必须能……”,比如你是飞机,则必须能飞。
接口的本质是契约,如同国家规定的法律,必须去遵守。
面向对象的精髓,是对对象的抽象,接口最能体现这一点。
接口的作用
(1)为何需要接口?
接口是比 “抽象类” 还要 “抽象” 的 “抽象类”,可以更加规范的对子类进行约束。
全面且专业地实现了:规范和具体实现的分离。
(2)接口与抽象类的区别?
抽象类会提供某些具体的实现,接口不提供任何实现,接口中所有方法都是抽象方法。
接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能够提供哪些服务。
接口是两个模块之间通信的标准,通信的规范。
接口和实现类不是父子关系,是实现规则的关系。
(3)本质区别
- 普通类:具体实现
- 抽象类:具体实现,规范(抽象方法)
- 接口:规范
声明格式:
接口中只能定义方法和常量
[访问修饰符] interface 接口名 [extends 父接口1,父接口2……] {
常量定义:
方法定义
}
接口定义详细说明
- 访问修饰符:只能是 public 或默认。
- 接口名:和类名采用相同命名机制。
- extends:接口可以多继承。
- 常量:接口中的属性只能是常量,总是:public static final 修饰,不写也是如此。
- 方法:接口中的方法只能是:public abstract 。如果省略不写的话,默认也是 public abstract 。
使用要点
- 子类通过 implements 来实现接口中的规范。
- 接口不能创建实例,但是可用于声明变量类型。
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public 的。
- JDK1.8(不含8)之前,接口中只能包含静态常量,抽象方法,不能有普通属性、普通方法、构造方法。
- JDK1.8(含8)后,接口中可以包含普通的静态方法、默认方法。
代码示例:接口定义测试
两个文件:Volant.java(接口定义文件)、SuperMan.java(主文件)
(1)Volant.java
package cn.jungle.test.TestInterface;
/**
* 这里是一个飞行器的接口。
*
* */
public interface Volant {
/**
* 接口中定义一个 FLY_HEIGHT 常量。
* 这里表示飞行器在地球上的飞行最高高度,单位:公里。
*/
/* public static final 这里可写可不写*/ int FLY_HEIGHT = 1000;
/**
* 接口中定义一个 fly() 抽象方法。
* 定义飞行方法,表示飞行器可以起飞。
*/
/* public abstract */ void fly();
/**
* 接口中定义一个 stop() 抽象方法。
* 定义停止方法,表示可以让飞行器停止。如果在空中就是飞行,在地面就是静止。
*/
/* public static final */ void stop();
}
/**
* 一个行为接口,帮助他人
*/
interface Honest{
void helperOther();
}
(2)SuperMan.java
package cn.jungle.test.TestInterface;
// 定义超人的飞行类和行为类
public class SuperMan implements Volant,Honest {
// 重写父类方法
@Override
public void fly() {
System.out.println("从天而降");
}
// 重写父类方法
@Override
public void stop() {
System.out.println("空中悬停");
}
// 重写父类方法
@Override
public void helperOther() {
System.out.println("哪里需要帮助,哪里就有我马丁超人!");
}
// main 方法入口,可以定义在这里,也可以单独创建一个 java 文件来存放 main 方法
public static void main(String[] args) {
// 定义类对象
SuperMan m1 = new SuperMan();
// 类对象调用方法
m1.fly();
m1.helperOther();
m1.stop();
/**
* 另一种调用方式,定义一个多态对象,然后通过强转去调用方法,但是比较麻烦
*/
}
}
接口中定义静态方法和默认方法(JDK8 以后)
Java8 之前,接口里的方法要求全部是抽象方法。
Java8(含8) 之后,允许在接口里使用默认方法和类方法。
默认方法
Java8 及以上旧版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法并不是。
作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
调用接口的默认方法需要实现类的对象来调用。
代码示例:接口中默认方法的定义、重写与调用
package cn.jungle.test.TestInterface;
/**
* 测试接口中定义默认方法
*/
public class Test01 {
public static void main(String[] args) {
// 父类引用指向子类对象。调用接口的默认方法需要实现类的对象来调用。
A a = new Test_A();
a.moren();
}
}
// 定义接口
interface A{
// 用 default 关键字来定义默认方法
default void moren(){
System.out.println("这里是接口 A 中的默认方法。");
System.out.println("如果默认方法没有被重写,调用时则会执行默认方法中的语句。");
}
}
// 定义普通类
class Test_A implements A{
// moren 方法的重写
@Override
public void moren() {
System.out.println("接口中的默认方法重写以后,调用时会被执行,不会再去执行接口中的默认方法语句。");
}
}
静态方法
Java8 以后,可以在接口中直接定义静态方法的实现。
这个静态方法直接从属于接口(接口也是类,一种特殊的类),可以通过接口名调用。
如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。可以通过子类名直接调用。
如果调用静态方法,是用 “类名.静态方法名” 去调用。静态方法从属于类。
静态方法中不能调普通方法,普通方法中可以调静态方法。
接口多继承
接口完全支持多继承。接口可以有多个父类接口
和类的继承类似,子继承扩展某个父接口,将会获得父接口中定义的一切。
代码示例
package cn.jungle.test.TestInterface;
/**
* 测试接口的多继承
*/
public class Test02{
public static void main(String[] args) {
Test t = new Test();
t.testB();
t.testC();
t.testD();
}
}
interface B{
void testB();
}
interface C{
void testC();
}
/* 接口可以多继承,接口 D 继承 B 和 C*/
interface D extends B,C{
void testD();
}
// Test 类作为 C 的实现类。实现类需要实现各个接口中的方法,即重写
class Test implements D{
// 实现类中重写和实现接口的方法
@Override
public void testB() {
System.out.println("testB() 方法");
}
// 实现类中重写和实现接口的方法
@Override
public void testC() {
System.out.println("testC() 方法");
}
// 实现类中重写和实现接口的方法
@Override
public void testD() {
System.out.println("testD() 方法");
}
}