接口
一、接口概述
接口在生活中随处可见。例如:屋子里的五孔墙插。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YSyus1JD-1615184665396)(12_接口/西门子五孔墙插.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k71GSZZj-1615184665407)(12_接口/欧普五孔墙插.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nr6wZCJU-1615184665414)(12_接口/公牛五孔墙插.png)]
为什么不同厂商生产的面板装在你家里之后你都可以使用呢?因为他们是遵守同样的规范生产的。
其实接口就是一组规范或标准,只要符合规范,大家都可以使用。
除了生活中的接口,与计算机相关的接口也随处可见。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-giISJqo7-1615184665431)(12_接口/USB接口.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ndxXcTqt-1615184665434)(12_接口/USB鼠标.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPcalNd2-1615184665436)(12_接口/USB键盘.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vCIQmRKv-1615184665438)(12_接口/U盘.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q8Aub0to-1615184665441)(12_接口/USB移动硬盘.png)]
虽然我们往电脑上插的硬件各不相同,但是他们都能正常的工作,之所以能正常工作是因为他们都是遵守USB规范生产的。
再例如:某公司招聘Java开发工程师,要求能写代码,能写需求文档,能与客户沟通项目。这就是一个接口,它只规定要做什么事情,谁能胜任这些事,谁就可以入职。
接口就是一种公共的规范标准,只要遵守规范,谁都可以使用。Java中的接口更多是对行为的抽象,它只规定有什么方法,自身不提供实现,由遵守规范的类来提供实现。
接口的实现类:实现接口的类称为接口的实现类。
二、接口的定义
在Java中,使用interface关键字来定义接口。
接口定义的语法:
public interface 接口名{
//静态常量
//抽象方法、默认方法、静态方法
}
接口示例:
public interface Driving {
//开车
public abstract void drive();
}
三、接口的实现类
接口的作用是定义标准和规范,实现类负责实现接口中的方法。
(1)类实现接口的语法格式
类通过implements关键字实现接口。
语法格式:
public class 类名 implements 接口名{
//属性
//方法
}
或
public class 类名 extends 父类名 implements 接口名{
//属性
//方法
}
(2)接口的实现类示例
-
需求:设计教师、程序员类,教师能教课,程序员可以写代码,他们都会开车。—千万不要定义一个司机(Driver)类,让教师和程序员继承于Driver,他们不满足is a的关系,不能滥用继承。
-
代码:
父类Person
public class Person { private String name; //姓名 private int age; //年龄 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } public void showInfo() { System.out.println("姓名:" + name + ", 年龄:" + age); } }
教师Teacher
public class Teacher extends Person { public Teacher() { super(); } public Teacher(String name, int age) { super(name, age); } //教课 public void teach() { System.out.println(this.getName() + "正在授课"); } }
程序员Programmer
public class Programmer extends Person { public Programmer() { super(); } public Programmer(String name, int age) { super(name, age); } //写代码 public void writeProject() { System.out.println(this.getName() + "正在写项目"); } }
驾驶接口Driving
public interface Driving { //开车 public abstract void drive(); }
教师实现Driving接口
public class Teacher extends Person implements Driving { public Teacher() { super(); } public Teacher(String name, int age) { super(name, age); } //教课 public void teach() { System.out.println(this.getName() + "正在授课"); } @Override public void drive() { System.out.println("教师开车"); } }
程序员实现Driving接口
public class Programmer extends Person implements Driving{ public Programmer() { super(); } public Programmer(String name, int age) { super(name, age); } //写程序 public void writeProject() { System.out.println(this.getName() + "正在写项目"); } @Override public void drive() { System.out.println("程序员开车去兜风"); } }
测试代码:
public class ClassTest { public static void main(String[] args) { Teacher t = new Teacher("张三", 32); t.showInfo();//继承过来的方法 t.teach();//自己的方法 t.drive();//实现的接口中的方法 Driving d = new Teacher("李四", 25);//多态 d.drive(); Programmer p = new Programmer("王五", 24); p.showInfo();//继承过来的方法 p.writeProject();//自己的方法 p.drive();//实现的接口中的方法 } }
在设计类的时候,一旦一个类实现了接口中的方法,我们就认为这个类get了新的功能。例如:Teacher遵守了Driving接口,实现了接口中的方法,就可以认为教师具有开车的功能。Programmer遵守了Driving接口,实现了接口中的方法,就可以认为程序员具有开车的功能。
接口可以当成一种数据类型来看待。Teacher和Programmer都实现了Driving接口,都会开车,他们可以看成Driving类型的对象,即接口可以看作父类。
接口弥补了Java只能单继承的缺陷,变相实现了多继承。
在Java中多态一共2种:
- 父类引用指向子类对象的多态。------继承中的多态(继承关系)
- 接口引用指向实现类对象的多态。------接口中的多态(实现关系)
四、接口的成员特点
接口可以包含自己的成员,包括属性和方法。
(1)接口中的属性
有如下接口,MyInterface
public interface MyInterface {
int a = 10;
public int b = 20;
public static int c = 30;
public static final int d = 40;
}
有如下实现类,MyInterfaceImpl
public class MyInterfaceImpl implements MyInterface {
}
测试代码:
public class ClassTest {
public static void main(String[] args) {
MyInterface my = new MyInterfaceImpl();
System.out.println(my.a);
System.out.println(MyInterface.a);//说明a被static修饰了
//my.a = 100;//final修饰的变量不能改值
System.out.println(my.b);
System.out.println(MyInterface.b);//说明b被static修饰了
//my.b = 200;//final修饰的变量不能改值
System.out.println(my.c);
System.out.println(MyInterface.c);
//my.c= 300;//接口中的属性默认是 public static final修饰
System.out.println(my.d);
System.out.println(MyInterface.d);//因为d是static修饰的,所以可以用类名访问。
//my.d= 400;//final修饰的变量不能改值
}
}
通过测试,我们发现a隐含了修饰符public、final和static,b隐含了修饰符static、final,c隐含了修饰符final。
总结:接口中只能包含public static final修饰的属性,即只能包含静态常量。–注意常量一般使用纯大写字母命名。
(2)反编译工具查看接口中的属性
程序的运行流程:首先,源代码经过编译得到字节码文件;然后,运行字节码得到执行结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRjuKTjh-1615184665443)(12_接口/Java代码执行流程.png)]
反编译:把字节码文件还原成Java源文件。
**反编译需要借助反编译工具。**目前有很多反编译工具,例如:JD-GUI、JadX、CFR、luyten等
我们使用JD-GUI反编译MyInterface。JD-GUI工具如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YePim4xr-1615184665448)(12_接口/jd-gui.png)]
反编译后的代码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5eIKqKgD-1615184665451)(12_接口/接口反编译1.png)]
由此可见,接口中定义的属性,默认就是public static final修饰的。即接口中的属性只能是静态常量!
(3)接口中的方法
接口中不能有构造方法、初始化代码块、静态代码块!
接口不是类,它只声明接口中有什么方法,但自身不实现。只定义不实现的方法就是我们前面学的抽象方法。
public interface MyInterface {
int a = 10;
public int b = 20;
public static int c = 30;
public static final int d = 40;
// public MyInterface() {
//
// }
// {
//
// }
// static {
//
// }
//接口只定义包含什么方法,但是自身不实现。
public abstract void method1();
public void method2();
void method3() ;
}
实现类MyInterfaceImpl:
public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("method1");
}
@Override
public void method2() {
System.out.println("method2");
}
@Override
public void method3() {
System.out.println("method3");
}
}
测试代码:
public class ClassTest {
public static void main(String[] args) {
MyInterface my = new MyInterfaceImpl();
System.out.println(my.a);
System.out.println(MyInterface.a);//说明a被static修饰了
//my.a = 100;//final修饰的变量不能改值
System.out.println(my.b);
System.out.println(MyInterface.b);//说明b被static修饰了
//my.b = 200;//final修饰的变量不能改值
System.out.println(my.c);
System.out.println(MyInterface.c);
//my.c= 300;//接口中的属性默认是 public static final修饰
System.out.println(my.d);
System.out.println(MyInterface.d);//因为d是static修饰的,所以可以用类名访问。
//my.d= 400;//final修饰的变量不能改值
my.method1();
my.method2();
my.method3();
}
}
通过测试,我们发现method2隐含abstract,method3隐含了public abstract。
总结:接口中的方法默认修饰符是public abstract,即方法是抽象方法。
(4)接口中定义静态方法和默认方法
从Java8开始,接口中可以定义静态方法和默认方法。静态方法和默认方法不再是方法的声明,而是有方法体。
public interface MyInterface {
int a = 10;
public int b = 20;
public static int c = 30;
public static final int d = 40;
// public MyInterface() {
//
// }
// {
//
// }
// static {
//
// }
//接口只定义包含什么方法,但是自身不实现。
public abstract void method1();
public void method2();
void method3() ;
//从Java8开始,接口中除了抽象方法以外,还可以包含2种方法
//1. 静态方法。
public static void staticMethod() {
System.out.println("我是接口中的静态方法");
}
//2. 默认方法
public default void defaultMethod() {
System.out.println("我是接口中的默认方法,默认方法必须要加default关键字");
}
}
MyInterfaceImple类:
public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("method1");
}
@Override
public void method2() {
System.out.println("method2");
}
@Override
public void method3() {
System.out.println("method3");
}
public static void staticMethod() {
System.out.println("子类的静态方法");
}
//重写接口中的默认方法
@Override
public void defaultMethod() {
System.out.println("重写了接口中的默认方法");
}
}
测试代码:
public class ClassTest {
public static void main(String[] args) {
MyInterface my = new MyInterfaceImpl();
System.out.println(my.a);
System.out.println(MyInterface.a);//说明a被static修饰了
//my.a = 100;//final修饰的变量不能改值
System.out.println(my.b);
System.out.println(MyInterface.b);//说明b被static修饰了
//my.b = 200;//final修饰的变量不能改值
System.out.println(my.c);
System.out.println(MyInterface.c);
//my.c= 300;//接口中的属性默认是 public static final修饰
System.out.println(my.d);
System.out.println(MyInterface.d);//因为d是static修饰的,所以可以用类名访问。
//my.d= 400;//final修饰的变量不能改值
my.method1();
my.method2();
my.method3();
MyInterface.staticMethod();
MyInterfaceImpl.staticMethod();
//my.staticMethod();//接口中的静态方法,只能用接口名调用,不能用对象调用。(类中的静态方法既可以用类名调用,也可以用对象调用,推荐使用类名调用)
my.defaultMethod();//默认方法是对象方法,用对象来调用。
}
}
接口中的静态方法和类中的静态方法不同。接口中的静态方法只能用接口名调用,不能用对象调用。类中的静态方法,既可以用类名调用,也可以用对象调用。
接口中的默认方法,必须用default关键字修饰。被default关键字修饰的方法不再是抽象方法,可以给出默认实现。之所以这么设定,是避免了接口中增加新方法影响已有实现类的使用。(如果没有默认实现,所有已有的实现类,都必须实现新增的方法。)
(5)接口的成员总结
**接口中可以包含属性:必须是public static final修饰的属性,即静态常量!**变量名命名的时候大写。
接口中可以包含方法:
- public abstract修饰的抽象方法。
- 静态方法(public static修饰的方法)
- 默认方法(public default修饰的方法)
接口中不能包含构造方法、初始化代码块、静态代码块。
五、类实现多个接口
一个类可以实现多个接口,接口与接口之间用逗号隔开。
语法格式
public class 类名 implements 接口名1,接口名2,接口名3{
//类的成员
}
或者
public class 类名 extends 父类名 implements 接口名1,接口名2,接口名3{
//类的成员
}
-
需求:设计教师、程序员类,教师能教课,程序员可以写代码,他们都会开车,都会做饭。提示:开车定义在Driving接口中,做饭定义在Cooking接口中,让教师和程序员实现2个接口。
-
代码:
父类Person:
public class Person { private String name; //姓名 private int age; //年龄 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } //toString方法 public String toString() { return "姓名:" + name + ",年龄:" + age; } }
接口Driving:
public interface Driving { //开车 public abstract void drive(); }
接口Cooking:
public interface Cooking { //做饭 public abstract void cook(); }
教师Teacher类:
public class Teacher extends Person implements Driving, Cooking{ public Teacher() { super(); } public Teacher(String name, int age) { super(name, age); } //教课 public void teach() { System.out.println(this.getName() + "正在授课"); } //Driving接口中的方法 @Override public void drive() { System.out.println("教师开车"); } //Cooking接口中的方法 @Override public void cook() { System.out.println("教师做饭"); } }
程序员Programmer类:
public class Programmer extends Person implements Driving, Cooking { public Programmer() { super(); } public Programmer(String name, int age) { super(name, age); } //写程序 public void writeProject() { System.out.println(this.getName() + "正在写项目"); } //Cooking接口中的方法 @Override public void cook() { System.out.println("程序员做饭"); } //Driving接口中的方法 @Override public void drive() { System.out.println("程序员开车"); } }
测试代码:
public class ClassTest { public static void main(String[] args) { Teacher t = new Teacher("马云", 56); //System.out.println(t.toString()); System.out.println(t);//当你打印对象的时候,默认调用对象的toString t.teach(); t.drive(); t.cook(); Cooking c = new Teacher("马云", 56); System.out.println(c); c.cook(); Driving d = new Teacher("马云", 56); System.out.println(d); d.drive(); System.out.println("------"); Programmer p = new Programmer("马化腾", 52); System.out.println(p); p.writeProject(); p.cook(); p.drive(); Cooking c2 = new Programmer("马化腾", 52); c2.cook(); Driving d2 = new Programmer("马化腾", 52); d2.drive(); } }
六、接口继承另外的接口
接口可以继承另外的接口,使用extends关键字继承别的接口,如果要继承多个接口,父接口之间用逗号隔开。
public interface 接口名 extends 接口1,接口2,接口3{
//方法
}
示例:
MyInterface接口:
public interface MyInterface {
public abstract void myInterfaceMethod();
}
MyInterface1接口:
public interface MyInterface1 {
public abstract void myInterface1Method();
}
MyInterface2接口:
public interface MyInterface2 extends MyInterface, MyInterface1{
public abstract void myInterface2Method();
}
实现类InterfaceImpl:
//要实现MyInterface2以及所有父接口中声明的方法
public class InterfaceImpl implements MyInterface2 {
@Override
public void myInterfaceMethod() {
System.out.println("MyInterface中的方法");
}
@Override
public void myInterface1Method() {
System.out.println("MyInterface1中的方法");
}
@Override
public void myInterface2Method() {
System.out.println("MyInterface2中的方法");
}
}
测试代码:
public class ClassTest {
public static void main(String[] args) {
InterfaceImpl impl = new InterfaceImpl();
impl.myInterfaceMethod();//MyInterface中的方法
impl.myInterface1Method();//MyInterface1中的方法
impl.myInterface2Method();//MyInterface2中的方法
MyInterface2 my = new InterfaceImpl();
my.myInterfaceMethod();//MyInterface中的方法
my.myInterface1Method();//MyInterface1中的方法
my.myInterface2Method();//MyInterface2中的方法
}
}
实现类不但要实现接口中声明的方法,还要实现接口的父接口中声明的方法。
七、接口和抽象类的区别
抽象类:用abstract修饰的类称为抽象类。
接口:是一组规范和标准,往往是对行为的抽象。
(1)相同点
- 接口和抽象类都不能实例化对象。
- 接口和抽象类都可以使用多态。
- 接口和抽象类都可以包含抽象方法。
- 一个类如果实现接口必须实现全部的抽象方法(或者自身定义为抽象类,不现实接口中的方法,由子类去实现),一个类继承于抽象类,必须实现全部的抽象方法(或者自身定义为抽象类,不现实接口中的方法,由子类去实现)。
(2)不同点
- 接口中的属性只能包含静态常量(public static final)。抽象类中的属性可以包含:实例变量、静态变量、常量(静态、非静态),而且访问修饰符可以是(public、缺省、protected、private)
- 接口中的方法通常是抽象方法(Java8开始,可以有默认方法和静态方法)。抽象类中的方法可以包含:实例方法(含getter、setter)、静态方法、抽象方法。
- 接口不能有构造方法(它不是类),抽象类可以包含构造方法。
- 接口中不能包含初始化块、静态代码块。抽象类中可以包含初始化块和静态代码块。
- 接口中的默认方法必须用default修饰。抽象类中不需要default就可以实现方法。
- 接口可以继承别的接口,而且可以继承多个。抽象类可以继承别的类,但只能继承一个类。
- 一个普通类,只能有一个父类(包括抽象类),但可以实现多个接口。
八、接口示例
-
需求:定义一个英雄类Hero,包含姓名(name)、血量(hp)、护甲(armor)。定义一个物理攻击英雄类ADHero,定义一个物理攻击的接口AD,定义一个魔法攻击英雄类APHero,定义一个魔法攻击的接口AP,定义一个既能物理攻击也能魔法攻击的英雄ADAPHero,并编写代码测试效果。
-
代码:
父类Hero:
public class Hero { private String name; //英雄的名字 private int hp; //英雄的血量 private int armor; //英雄的护甲 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHp() { return hp; } public void setHp(int hp) { this.hp = hp; } public int getArmor() { return armor; } public void setArmor(int armor) { this.armor = armor; } public Hero() { super(); } public Hero(String name, int hp, int armor) { super(); this.name = name; this.hp = hp; this.armor = armor; } @Override public String toString() { return "Hero [name=" + name + ", hp=" + hp + ", armor=" + armor + "]"; } }
接口AD:
public interface AD { //物理攻击 public abstract void physicAttack(Hero hero); }
接口AP:
public interface AP { //魔法攻击 public abstract void magicAttack(Hero hero); }
ADHero类:
public class ADHero extends Hero implements AD { public ADHero() { super(); } public ADHero(String name, int hp, int armor) { super(name, hp, armor); } @Override public void physicAttack(Hero hero) { System.out.println(this.getName() + "正在物理攻击" + hero.getName()); } }
APHero类:
public class APHero extends Hero implements AP { @Override public void magicAttack(Hero hero) { System.out.println(this.getName() + "魔法攻击" + hero.getName()); } public APHero() { super(); } public APHero(String name, int hp, int armor) { super(name, hp, armor); } }
ADAPHero类:
public class ADAPHero extends Hero implements AD, AP { public ADAPHero() { super(); } public ADAPHero(String name, int hp, int armor) { super(name, hp, armor); } @Override public void magicAttack(Hero hero) { System.out.println(this.getName() + "魔法攻击" + hero.getName()); } @Override public void physicAttack(Hero hero) { System.out.println(this.getName() + "正在物理攻击" + hero.getName()); } }
测试代码:
public class ClassTest { public static void main(String[] args) { ADHero hero1 = new ADHero("剑姬", 1000, 300); System.out.println(hero1);//System.out.println(hero1.toString()); APHero hero2 = new APHero("安妮", 700, 280); System.out.println(hero2); ADAPHero hero3 = new ADAPHero("卡莎", 800, 300); System.out.println(hero3); hero1.physicAttack(hero2); hero2.magicAttack(hero1); hero3.physicAttack(hero1); hero3.magicAttack(hero1); } }
九、总结
接口是一组规范和标准,往往是对行为的抽象。
使用interface关键字定义接口。
接口中可以包含:静态常量、抽象方法、默认方法、静态方法。
接口不能实例化对象。
接口和实现类主要服务于多态,接口作为数据类型,指向实现类创建的对象。
类通过implements实现接口(需实现全部的抽象方法)。如果不实现接口中定义的方法,可以定义成抽象类,由子类实现。
一个类可以实现多个接口。接口可以继承接口,父接口可以是多个。