java接口
文章内容选自尚硅谷,jdk8,eclipse环境
java接口简介
java接口可以用来解决类的单继承问题,一般是把多个不同类共同的方法提取出来,形成一种规范、接口,只要用到了这些方法,都要调用该接口。并在实现类中重写接口定义的方法。
JDK7及以前,只能定义全局常量和抽象方法
全局常量:用static final修饰的变量
抽象方法:用abstract修饰的方法
JDK8及以后,接口内还可以定义静态方法和默认方法。
以下先介绍JDK7及以前的形式。
接口内的书写形式
- 接口和类是并列的两个结构,用interface关键字修饰。
- 接口中不能定义构造器,意味着接口不可以实例化。
JDK7以前方法必须是抽象方法,变量必须是全局常量,为了简便书写,编译器在遇见不加关键字修饰的变量和方法的时候,会默认将其视为全局常量和抽象方法。
public class InterfaceTest {
}
interface Flyable{
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
public abstract void fly();
void stop();
}
接口的变量MIN_SPEED其实也是全局常量,省略掉了public static final关键字
接口的方法stop方法其实也是抽象方法,省略掉了public abstract关键字
接口被类实现
因为接口是和类并列的结构,但是又不能实例化,那么接口就只能被类实现,有点类似于子类重写父类的抽象方法,用关键字implement来表示实现。实现接口方法的类叫做实现类。
- 若实现类实现了接口的所有抽象方法,则实现类可实例化,否则实现类也必须为抽象类。
- 对于重写父类的抽象方法,一般称之为实现,实现类实现接口的抽象方法,子类实现父类的抽象方法。
public class InterfaceTest {
public static void main(String[] args) {
Plane plane = new Plane();
plane.fly();
}
}
interface Flyable{
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
public abstract void fly();
void stop();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("飞机起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速");
}
}
接口的多实现
- 一个类可以实现多个接口,那么类中就要实现多个接口的所有抽象方法。接口的多实现能弥补类的单继承问题。
- 如果一个类同时存在继承和实现,先继承后实现。
- 接口与接口之间也存在继承的关系,而且是多继承。
interface Flyable{
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
public abstract void fly();
void stop();
}
interface Attackable{
void attack();
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
class Bullet extends Object implements Flyable,Attackable,CC{
@Override
public void attack() {
// TODO Auto-generated method stub
}
@Override
public void fly() {
// TODO Auto-generated method stub
}
@Override
public void stop() {
// TODO Auto-generated method stub
}
@Override
public void method1() {
// TODO Auto-generated method stub
}
@Override
public void method2() {
// TODO Auto-generated method stub
}
}
接口的使用
接口使用的多态性
前面已经讲到,接口定义的是一种规范,它统一了方法的名称,实现了面向接口编程。下面讲讲规范的思想。
假如说电脑与打印机,U盘传输数据,都用的是USB接口,那么就定义interface USB,USB接口就成为了传输数据的一种规范。规范表现为就是多个不同实现类都共同使用的抽象方法的名称。
以上图为例,interface USB定义的全局常量看作是接口的长,宽,高,抽象方法看成是外设与电脑传输数据的功能,但具体功能的实现在外设内定义的,接口只起一个功能声明作用。
创建U盘,printer两个类,并实现USB接口的方法,形成实现类。在实现类中实现的方法可以看作是printer、U盘本身自带的驱动。电脑通过驱动程序传输数据,我们就理解为电脑类通过实现类重写的方法传输数据,接口在这儿起到了一个多态的作用,通过接口的多态性调用了实现类重写的方法。
具体代码如下
package com.atguigu.java1;
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
Flash flash = new Flash();
computer.transmit(flash);
}
}
class Computer{
public void transmit(USB usb){
usb.start();
System.out.println("正在传输");
usb.end();
}
}
interface USB{
void start();
void end();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘传输开始");
}
@Override
public void end() {
System.out.println("U盘传输结束");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("printer start");
}
@Override
public void end() {
System.out.println("printer end");
}
}
运行结果为
U盘传输开始
正在传输
U盘传输结束
实际的方法体是写在实现类中的,类比这个例子,就好比是驱动程序写在U盘和printer内部,电脑在传输数据的时候,通过接口的多态性调用了U盘和printer内部的驱动。
如果不使用接口多态,当然也可以直接面向操作对象来编程,但缺点是
一是可能就会面临明明实现相同的功能,但功能名称却起得各不相同,不利于程序员编程。体现了接口的规范性。
二是直接面向对象编程,这样就可能得由开发者自己定义具体功能的实现,在有了接口以后,开发者只需调用接口,具体的功能实现则可以交给对象的厂商来负责。
java在调用数据库的时候,直接使用的是JDBC中定义的接口,而接口的具体方法实现,则交由数据库方面负责,数据库方面自己提供driver驱动程序。
项目的具体需求是多变的,接口就是以不变应万变来开发,这儿的不变指的就是接口(规范性)。面向接口(不变)优于直接面向对象(万变)。
匿名实现类
之前讲过非匿名子类的非匿名对象,非匿名子类的匿名对象,匿名子类的非匿名对象,匿名类的匿名对象
同理,实现类也具有类似的特征。
接口的非匿名实现类的非匿名对象
Computer computer = new Computer();
Flash flash = new Flash();
computer.transmit(flash);
接口的非匿名实现类的匿名对象
computer.transmit(new Printer());
Printer是实现类的名称,但对象是匿名的。
接口的匿名实现类的非匿名对象
USB mp3 = new USB(){
@Override
public void start() {
System.out.println("mp3传输开始");
}
@Override
public void end() {
System.out.println("mp3传输结束");
}
};
computer.transmit(mp3);
PS:由于接口是没有构造器的,这儿的new USB() {} 不能看成是构造器,而是创建了一个实现类。
接口的匿名实现类的匿名对象
computer.transmit(new USB(){
@Override
public void start() {
System.out.println("phone传输开始");
}
@Override
public void end() {
System.out.println("phone传输结束");
}
});
jdk8的接口特性
JDK8及以后,接口内还可以定义静态方法和默认方法。
jdk8接口方法的调用
- 静态方法和默认方法内都可以定义方法体。
首先在package中创建一个接口,接口可以用关键字public修饰
package com.atguigu.java8;
public interface CompareA {
public static void method1(){
System.out.println("CompareA :beijing");
}
public default void method2(){
System.out.println("CompareA:shanghai");
}
default void method3(){
System.out.println("CompareA:shanghai");
}
}
注意,默认方法声明时可以不写public,但编译器会自动视作是带有public的。
接着创建一个SubClassTest的测试类
package com.atguigu.java8;
public class SubClassTest {
}
class SubClass implements CompareA{
}
此时编译器不会报错,因为静态方法和默认方法都有方法体。
- 若类实现了接口,类中可以调用接口中的静态方法和默认方法
- 调用接口中的静态方法,只能通过接口名调用
- 调用接口中的默认方法,只能通过实现类的对象调用
package com.atguigu.java8;
public class SubClassTest {
public static void main(String[] args) {
CompareA.method1();
SubClass s = new SubClass();
s.method2();
s.method3();
}
}
class SubClass implements CompareA{
}
关于接口的运用,还可以用局部内部类实现接口java之内部类.
jdk8接口方法调用的优先级
假如说一个子类(实现类)既继承了父类,又实现了接口,在接口和父类以及子类中都有同名同参数的方法,那么调用该方法,优先级为子类重写的方法>父类的方法>接口的方法。
子类的方法看作是对父类方法和接口方法的重写,优先级最高
package com.atguigu.java8;
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
s.method3();
}
}
class SubClass extends SuperClass implements CompareA{
public void method3(){
System.out.println("override");
}
}
class SuperClass{
public void method3(){
System.out.println("SuperClass:beijing");
}
}
运行结果为
override
jdk8接口方法调用的细节
1.一个类可以继承多个接口,若多个接口都有同名同参数的方法,此时要分为两种情况
(1). 若子类继承了父类 与接口 同名同参数的方法,编译器不报错,运行父类方法;若子类重写过 与接口 同名同参数的方法 ,运行子类重写的方法,总之,遵循上述调用的优先级。
(2). 若实现类中没有继承父类,也没有重写 接口 方法,编译器会报错,接口冲突,不允许多个接口有同名同参方法。
解决接口冲突,一般是在实现类中重写冲突的方法。
2.在子类(实现类)、父类、接口中出现了同名同参数方法,又想调用父类或接口中的方法,也是有办法的。
- 调用父类就用 super.
- 调用接口方法就用 接口名.super.方法名();
class SubClass extends SuperClass implements CompareA{
public void method3(){
System.out.println("override");
}
public void myMethod(){
method3();
super.method3();
CompareA.super.method3();
}
}
运行结果为
override
SuperClass:beijing
CompareA:shanghai