十一、JAVA接口的定义和使用
接口的基本概念:
我们知道知道java中只支持单继承,但如果我们想定义一些功能,想让一个子类都继承实现,显然没办法做到,所有Java提供了接口这个概念,这样我们就可以用一个子类去实现多个接口。我们可以理解为接口就是特殊的抽象类
在java8后,接口可以包括数据成员,但是数据成员必须是常量,其值一旦被初始化后,是不允许修改的,这些数据成员通常为全局变量。
为了避免在接口中添加新方法后要修改所有的实现类,接口中允许定义默认方法(default方法)。
定义接口需要使用关键字interface
定义接口实例:
interface A{ //定义一个接口
public static final String MESSAGE="HelloWorld"; //全局常量
public abstract void print(); //定义抽象方法
default public void otherprint(){ //定义可以带方法体的默认方法
System.out.println("默认方法");
}
}
*注意:接口中成员属性默认是public static final修饰,成员方法是public abstact修饰,所以上述定义可以简写
代码演示:
interface A{ //定义一个接口
String MESSAGE="HelloWorld"; //全局常量
void print(); //定义抽象方法
default void otherprint(){ //定义可以带方法体的默认方法
System.out.println("默认方法");
}
}
接口使用原则:
1.接口必须有子类,子类依靠implements关键字可以同时实现多个接口;
2.接口的子类(如果不是抽象类)必须实现接口之中的全部抽象方法;
3.接口可以利用对象多态性,利用子类实现对象的实例化
4.接口和普通的类一样,本身也有数据成员和方法,但数据成员一定要初始赋值,并且此值不能再有修改,定义的方法可以有抽象方法和默认方法,抽象方法abstact关键字可以省略,默认方法需要带上default关键字,默认方法可以带有方法体。
5.默认方法的调用和普通方法的调用一样
接口的使用:
我们来举个例子,定义一个抽象类People,一个普通子类Student,两个接口。子类Student继承父类People,并实现接口Study,Write
代码演示:
package demo;
//构建一个抽象类People
abstract class People{
//父类属性私有化
private String name;
private int age;
//提供父类的构造器
public People(String name,int age){
this.name = name;
this.age = age;
}
//提供获取和设置属性的getter()/setter()方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//提供一个抽象方法
public abstract void talk();
}
//定义一个接口
interface Study{
//设置课程数量为3
int COURSENUM = 3;
//构建一个默认方法
default void stu(){
System.out.println("学生需要学习"+COURSENUM+"门课程");
}
}
//再定义一个接口
interface Write{
//定义一个抽象方法
void print();
}
//子类继承People,实现接口Study,Write
class Student extends People implements Study,Write{
//通过super关键字调用父类的构造器
public Student(String name, int age) {
super(name, age);
}
//实现父类的抽象方法
public void talk() {
System.out.println("我的名字叫"+this.getName()+",今年"+this.getAge()+"岁");
}
//实现Write接口的抽象方法
public void print() {
System.out.println("学生会写作业");
}
}
public class InterfaceDemo{
public static void main(String[] args) {
//构建student对象
Student student = new Student("dodo", 22);
//调用父类的抽象方法
student.talk();
//调用接口Write中的抽象方法
student.print();
//调用接口Study中的默认方法
student.stu();
}
}
代码讲解:上述例子结合了抽象类和接口的知识,内容较多,同学们可以多看多敲一下,学习学习。
接口的实现:类名 implements 接口名,有多个接口名,用“,”隔开即可。
接口的作用——制定标准
接口师表尊,所谓的标准,指的是各方共同遵守一个守则,只有操作标准统一了,所有的参与者才可以按照统一的规则操作。
如电脑可以和各个设备连接,提供统一的USB接口,其他设备只能通过USB接口和电脑相连
代码实现:
package demo;
interface USB
{
public void work() ; // 拿到USB设备就表示要进行工作
}
class Print implements USB //实现类(接口类)
{ // 打印机实现了USB接口标准(对接口的方法实现)
public void work()
{
System.out.println("打印机用USB接口,连接,开始工作。") ;
}
}
class Flash implements USB //实现类(接口类)
{ // U盘实现了USB接口标准(对接口的方法实现)
public void work()
{
System.out.println("U盘使用USB接口,连接,开始工作。") ;
}
}
class Computer
{
public void plugin(USB usb) //plugin的意思是插件,参数为接收接口类
{
usb.work() ; // 按照固定的方式进行工作
}
}
public class InterfaceStandards { public static void main(String args[]) { Computer computer = new Computer() ; computer.plugin(new Print()) ; //实例化接口类, 在电脑上使用打印机 computer.plugin(new Flash()) ; //实例化接口类, 在电脑上使用U盘 }}代码讲解:上述例子,就给我们展示了接口制定标准的作用,怎么指定的呢?看下面代码
class Computer
{
public void plugin(USB usb) //plugin的意思是插件,参数为接收接口类
{
usb.work() ; // 按照固定的方式进行工作
}
}
我们可以看到,Computer类里面定义了一个方法plugin(),它的参数内写的是USB usb,即表示plugin()方法里,接收的是一个usb对象,而打印机和U盘对象可以通过向上转型当参数,传入方法里。我们来重新写一个main方法帮助大家理解
代码演示:
public class InterfaceStandards
{
public static void main(String args[])
{
Computer computer = new Computer() ;
USB usb = new Print();
computer.plugin(usb) ; //实例化接口类, 在电脑上使用打印机
usb = new Flash();
computer.plugin(usb) ; //实例化接口类, 在电脑上使用U盘
}
}
代码讲解:我们修改了主函数后,发现,使用了两次的向上转型给了USB,虽然使用的都是usb对象,但赋值的子类对象不一样,实现的方法体也不同,这就很像现实生活,无论我使用的是打印机,还是U盘,我都是通过USB接口和电脑连接的,这就是接口的作用之一——制定标准
我们来个图继续帮助大家理解一下:
上面的图:我们学习前面的章节多态可以知道对象的多态可以通过动态绑定来实现,即使用向上转型,我们知道类,数组,接口都是引用类型变量,什么是引用类型变量?引用类型变量都会有一个地址的概念,即指向性的概念,当USB usb = new Print(),此时usb对象是指向new Print()的,当usb = new Flash()后,这时候usb变量就会指向new Flash(),我们会说这是子类对象赋值给了父类对象usb,而在内存中,我们应该说,usb指向了new Flash();
这里我给大家推荐一个介绍堆和栈概念的博客,感兴趣的同学可以点击看一下
https://www.cnblogs.com/perfy/p/3820594.html点击打开链接
接口的作用——工厂模式
首先我们来认识一下什么是工厂模式?工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
其次,工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。
由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类C,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为C即可,而不需要找到所有的new B()改为newC()。
代码演示:
package demo;
import java.util.Scanner;
interface Fruit //定义一个水果标准
{
public abstract void eat();
}
class Apple implements Fruit
{
public void eat()
{
System.out.println("吃苹果");
}
}
class Orange implements Fruit
{
public void eat()
{
System.out.println("吃橘子");
}
}
class factory
{
public static Fruit getInstance(String className) //返回值是Fruit的子类
{
if("apple".equals(className))
{
return new Apple();
}
else if("orange".equals(className))
{
return new Orange();
}
else
{
return null;
}
}
}
public class ComplexFactory {
public static void main(String[] args)
{
System.out.println("请输入水果的英文名:");
Scanner sc = new Scanner(System.in);
String ans = sc.nextLine();
Fruit f = factory.getInstance(ans); //初始化参数
f.eat();
sc.close();
}
}
代码讲解:上述代码部分我们讲一下factory这个类,类中有一个getInstance方法,我们用了static关键字修饰,在使用的时候我们就在main中使用类名.方法名调用。
Fruit f = factory.getInstance(ans); //初始化参数
在Factory的getInstance()方法中,我们就可以通过逻辑的实现,将对象的创建和使用的过程分开了。
总结点评:在接口的学习中,大家可以理解接口是特殊的抽象类,java中类可以实现多个接口,接口中成员属性默认是public static final修饰,可以省略;成员方法默认是public abstract修饰,同样可以省略,接口中还可定义带方法体的默认方法,需要使用default修饰。利用接口我们还可以制定标准,还能够使用工厂模式,将对象的创建和使用过程分开。