【JAVA入门】Day15 - 接口
文章目录
- 【JAVA入门】Day15 - 接口
- 一、接口是对“行为”的抽象
- 二、接口的定义和使用
- 三、接口中成员的特点
- 四、接口和类之间的关系
- 五、接口中新增的方法
- 5.1 JDK8开始接口中新增的方法
- 5.1.1 接口中的默认方法
- 5.1.2 接口中的静态方法
- 5.2 JDK9 开始接口中新增的方法
- 5.2.1 在接口中定义私有方法
- 六、接口的应用
- 七、适配器设计模式
为什么有接口?
众所周知,继承是将子类中的共性抽象提炼到父类中,获取整个体系的共性以节省代码,但是,也存在这样一种情况:某一共性,绝大部分子类都有,但唯独极少部分子类未拥有此特性,那么如果把这个共性写进父类,继承下来,对这些少数子类而言就是不合理的了。
比如:猫和狗都会游泳,但兔子不会,这个时候如果在父类中定义“游泳”的方法,兔子继承是不合理的,但如果猫和狗分别写自己的游泳方法,又有可能发生两个方法书写的格式、命名不一致的情况。因此我们需要定义一个新概念,来约束这两个子类中“游泳”方法的书写规范。此时,我们就可以定义一个“游泳”的接口,在接口里定义抽象方法 swim(),然后让猫和狗与这个接口连接,保证了代码的统一。
综上所述,接口就是一种规则,当我们需要给多个类同时定义规则时,就需要用到接口。
一、接口是对“行为”的抽象
接口不代表一类事物,接口代表的是一种规则,因此接口可以作为参数传递给方法。
“不论来搬家的是货拉,三轮,哪怕是人力,只要他能实现搬家,他就有用”。
搬家(车的对象);
搬家(搬家公司);
public interface 运输 {...
}
public void 搬家(运输的接口 c) {...
}
二、接口的定义和使用
- 接口用关键字 interface 来定义。
public interface 接口名 {}
- 接口不能实例化,也就是说接口不能用来创建对象。
- 接口和类之间是实现关系,通过 implements 关键字表示。
public class 类名 implements 接口名 {}
- 接口的子类(实现类)要么重写接口中的所有抽象方法,要么本身也是一个抽象类。
- 接口和类的实现关系,可以单实现,也可以多实现:
public class 类名 implements 接口名1 , 接口名2 {}
- 实现类可以在继承一个类的同时实现多个接口。
public class 类名 extends 父类 implements 接口名1 , 接口名2 {}
练习:编写带有接口和抽象类的标准 Javabean 类。
青蛙 属性:名字,年龄 行为:吃虫子,蛙泳狗 属性:名字,年龄 行为:吃骨头,狗刨兔子 属性:名字,年龄 行为:吃胡萝卜
先写父类,因为三个字类吃的东西不同,可以把 eat() 定义为抽象方法:
package oopInterface;public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = 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 abstract void eat();
}
写接口Swim:
package oopInterface;public interface Swim {public abstract void swim();
}
写青蛙类,注意继承父类重写父类的抽象方法,注意实现游泳接口:
package oopInterface;public class Frog extends Animal implements Swim {public Frog() {super();}public Frog(String name, int age) {super(name, age);}@Overridepublic void swim() {System.out.println("青蛙在蛙泳。");}@Overridepublic void eat() {System.out.println("青蛙在吃虫子。");}
}
写狗类,注意继承父类重写父类的抽象方法,实现游泳接口:
package oopInterface;public class Dog extends Animal implements Swim {public Dog(){super();}public Dog(String name, int age) {super(name,age);}@Overridepublic void swim() {System.out.println("狗在狗刨。");}@Overridepublic void eat() {System.out.println("狗在吃骨头。");}
}
写兔子类,注意只需要继承父类重写抽象方法,不需要实现游泳接口(不会游泳):
package oopInterface;public class Rabbit extends Animal {public Rabbit() {}public Rabbit(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("兔子在吃胡萝卜。");}
}
写测试类。
package oopInterface;public class Test {public static void main(String[] args) {Frog f = new Frog("小绿",23);f.eat();f.swim();System.out.println(f.getName() + ", " + f.getAge());Dog d = new Dog("大D", 24);d.eat();d.swim();System.out.println(d.getName() + ", " + d.getAge());Rabbit r = new Rabbit("兔子", 30);r.eat();System.out.println(r.getName() + ", " + r.getAge());}
}
三、接口中成员的特点
- 成员变量:接口中的成员变量只能是常量,默认使用 public static final 修饰(就是不写也自动认为是这样)。
- 构造方法:接口没有构造方法。
- 成员方法:JDK7以前只能是抽象方法,默认修饰符为 public abstract;JDK8以后,接口中可以定义有方法体的方法;JDK9以后,接口中可以定义私有方法。
四、接口和类之间的关系
- 类和类的关系:继承关系——只能单继承,不能多继承,但可以多层继承。
- 类和接口的关系:实现关系——可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
- 接口和接口的关系:继承关系——可以单继承,也可以多继承。
注意:
1.类在实现接口时,要么实现接口中所有的抽象方法,要么这个类本身也是一个抽象类。
2.类可以多实现接口,如果多实现,需要把所有要实现接口的抽象方法都实现。
3.接口可以多继承,如果一个子接口继承了多个接口,然后被一个实现类实现,那么这个实现类就要把这个子接口和他的所有父接口中的所有抽象方法全部实现。
练习:编写带有接口和抽象类的标准Javabean类。
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
//Person类
package oopInterExp;//因为直接创建顶层父类人的对象是没有意义的
//所以将其写为抽象类
public abstract class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}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;}
}
//Sporter类继承Person
package oopInterExp;public abstract class Sporter extends Person {public Sporter(String name, int age) {super(name, age);}public Sporter() {}public abstract void learn();
}
//Coach类继承Person
package oopInterExp;public abstract class Coach extends Person {public Coach() {}public Coach(String name, int age) {super(name, age);}public abstract void teach();
}
//SpeakEnglish接口
package oopInterExp;public interface SpeakEnglishInter {public abstract void speakEnglish();
}
//PingPongSporter继承Sporter,实现SpeakEnglish
package oopInterExp;public class PingPongSporter extends Sporter implements SpeakEnglishInter {public PingPongSporter(String name, int age) {super(name, age);}public PingPongSporter() {}@Overridepublic void learn() {System.out.println("学习乒乓球。");}@Overridepublic void speakEnglish() {System.out.println("乒乓球运动员在说英语。");}
}
//PingPongCoach继承Coach,实现SpeakEnglish
package oopInterExp;public class PingPongCoach extends Coach implements SpeakEnglishInter {public PingPongCoach() {}public PingPongCoach(String name, int age) {super(name, age);}@Overridepublic void teach() {System.out.println("教乒乓球。");}@Overridepublic void speakEnglish() {System.out.println("乒乓球教练在说英语。");}
}
//BasketballSporter继承Sporter
package oopInterExp;public class BasketballSporter extends Sporter {public BasketballSporter(String name, int age) {super(name, age);}public BasketballSporter() {}public void learn() {System.out.println("学篮球。");}
}
//BasketballCoach继承Coach
package oopInterExp;public class BasketballCoach extends Coach {public BasketballCoach() {}public BasketballCoach(String name, int age) {super(name, age);}public void teach() {System.out.println("教篮球。");}
}
五、接口中新增的方法
5.1 JDK8开始接口中新增的方法
JDK7 以前,接口中只能定义抽象方法。
JDK8新增的特性是:接口中可以定义有方法体的方法(可以定义默认方法或静态方法)。
JDK9新增的特性是:接口中可以定义私有方法。
5.1.1 接口中的默认方法
接口中定义有方法体的方法,主要是为了接口升级考虑的,接口不可能是一成不变的,需要往里面添加新的方法来升级,如果这些方法都是抽象方法,此时,实现类中就需要同时也实现这些方法,非常麻烦且不容易同步;倘若升级使用的是有方法体的方法,那么实现类就不需要额外进行修改了,如果需要修改,也可以利用重写进行修改。
- 在接口中定义默认方法,需要使用关键字 default 修饰。
- 格式:public default 返回值类型 方法名(参数列表) { }
- 范例:public default void show() { }
- 默认方法不是抽象方法,不强制需要被重写。但是如果被重写,重写的时候要去掉 default 关键字。
- public 可以省略,但是 default 不可以省略。
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,那么子类就必须对该方法进行重写了(不重写会引起冲突)。
接口Inter1:
package oopInterface5;public interface Inter1 {public abstract void method();public default void default_method() {System.out.println("Inter1接口中的默认方法");}
}
接口Inter2:
package oopInterface5;public interface Inter2 {public default void default_method() {System.out.println("Inter2接口中的默认方法");}
}
两个默认方法同名,如果实现类同时实现这俩接口,一定需要重写这个默认方法!
package oopInterface5;public class InterImpl implements Inter1, Inter2 {@Overridepublic void method() {System.out.println("抽象方法的实现");}@Overridepublic void default_method() {System.out.println("重写接口中的默认方法");}
}
测试类:
package oopInterface5;public class Test {public static void main(String[] args) {InterImpl ii = new InterImpl();ii.method(); //抽象方法的实现ii.default_method(); //重写接口中的默认方法}
}
5.1.2 接口中的静态方法
JDK8 以后允许在接口中定义静态方法,需要用 static 修饰。
接口中静态方法的定义格式为:
- public static 返回值类型 方法名(参数列表) { }
- 范例:public static void show() { }
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或对象名调用。
- public 可以省略,static 不能省略。
写一个接口:
package oopInterface6;public interface Inter {public abstract void method();public static void static_method() {System.out.println("接口中的静态方法");}
}
写一个实现类,类里面还有一个静态方法,与接口里面的方法同名,但这不是重写,因为静态方法是不能被重写的:
package oopInterface6;public class InteImpl implements Inter {@Overridepublic void method() {System.out.println("重写接口中的抽象方法");}//这不叫重写public static void static_method() {System.out.println("我不是重写的Inter接口中的静态方法");}
}
但其实,二者是不同的方法。
package oopInterface6;public class Test {public static void main(String[] args) {InteImpl ii = new InteImpl();ii.method(); //重写接口中的抽象方法Inter.static_method(); //调用接口中的静态方法InteImpl.static_method(); //调用实现类中的一个同名的静态方法}
}
5.2 JDK9 开始接口中新增的方法
5.2.1 在接口中定义私有方法
- 格式1:private 返回值类型 方法名(参数列表) { }
- 范例1:private void show() { }
- 用法:给默认方法服务。
package oopInterface7;public interface InterA {public default void show1() {System.out.println("show1开始执行");show3();}public default void show2() {System.out.println("show2开始执行");show3();}//普通的私有方法,给默认方法服务的private void show3() {System.out.println("记录程序在运行过程中的各种细节,这里有100行代码。");}
}
- 格式2:private static 返回值类型 方法名(参数列表) { }
- 范例2:private static void method() { }
- 用法:给静态方法服务。
package oopInterface7;public interface InterB {public static void show1() {System.out.println("show1开始执行");show3();}public static void show2() {System.out.println("show2开始执行");show3();}//普通的私有方法,给静态方法服务的private static void show3() {System.out.println("记录程序在运行过程中的各种细节,这里有100行代码。");}
}
六、接口的应用
1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2.当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
七、适配器设计模式
- 设计模式(Design Pattern) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
适配器可以用来简化代码,避免因为接口中抽象方法过多,而我们只需要使用其中一部分的时候造成的不便。
书写步骤一般为:
1.编写中间类XXXAdapter,实现对应的接口。
2.对接口中的抽象方法进行空实现。
3.让真正的实现类继承中间类,并重写需要用的方法。
4.为了避免其他类去创建适配器类的对象,中间的适配器类用 abstract 修饰。
接口:
package AdapterDesignPattern;public interface Inter {public abstract void method1();public abstract void method2();public abstract void method3();public abstract void method4();public abstract void method5();
}
适配器类:
package AdapterDesignPattern;public abstract class InterAdapter implements Inter {@Overridepublic void method1() {}@Overridepublic void method2() {}@Overridepublic void method3() {}@Overridepublic void method4() {}@Overridepublic void method5() {}
}
实现类:
package AdapterDesignPattern;public class InterImpl extends InterAdapter {//我需要用到哪个方法,就重写哪个方法就可以了@Overridepublic void method5() {System.out.println("只要用第五个方法");}
}