抽象类&接口(超详细)
一:抽象类和抽象方法
封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为
如果老师的work是教书,学生的work是学习。以前我们是在父类里面写一个work随便写一个方法体,然后子类重写。但是有一个弊端,如果子类不是你写的,是别人写的,别人写子类的时候忘了重写了,或者故意不重写,你拿他没办法。怎么办?抽象类就是为了这个存在的,如果一个类中不确定方法体,那么这个方法用abstract修饰,就不用写方法体了,这个方法就叫做抽象方法,抽象方法子类必须强制书写,否则代码报错。抽象方法所在的类就是抽象类。
(1)抽象方法
1、抽象方法:将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
2、抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
(2)抽象类和抽象方法的定义格式
1、抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
2、抽象类的定义格式:
public abstract class 类名{
}
abstract class Person {
public abstract void work();
}
(2)抽象类和抽象方法的注意事项
1、抽象类不能实例化
如果可以创建对象,是不是就可以用对象去调用方法了?调用抽象方法执行什么内容啊?没有东西能执行,所以在Java中规定抽象类不能创建对象。
2、抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
3、可以构造方法
抽象类不能创建对象,有构造方法有什么用?
加入现在有一个抽象类Person,里面有两个属性name和age,然后有一个Student继承Person,重写了里面的抽象方法work()。我们在创建学生对象对象的时候,姓名和年龄传递到全参构造里面,是不是在自己Student这个类里面赋值的?不是!!它是通过super关键字交给父类去赋值的。
所以抽象类中的构造方法的作用是:当创建子类对象的时候,给属性进行赋值的。
4、抽象类的子类
1)要么重写抽象类中的所有抽象方法
2)要么是抽象类
(3)抽象类和抽象方法的意义
疑问:把子类中共性的内容抽取到父类之后,由于方法体不确定,需要定义为抽象。子类使用时需要重写。那么我不抽取到父类,直接在子类写不是更节约代码?
现在有两个类Cat和Dog,都继承Animal,Cat里面有eat方法吃小鱼竿,drink方法喝水。Dog里面有eat方法吃骨头,drink方法喝水。按照以前所学,我们要把共同的drink抽取到父类,drink是一样的抽取上去没问题。如果eat方法没有抽取,问题就出现了:因为以后在一个公司里面是很多人一起来开发一个项目的。假如Cat是一个人来开发的,这个哥们方法名用的abc,还给加个参数。加入Dog是另一个人写的,方法名叫eating,还多了个返回值。现在第三个人来了想要调用吃东西的方法,他调用猫吃东西的方法他要过去看一看,他调用狗吃东西的方法他要过去看一看,如果很多JavaBean呢?所以这种方式会让我们的代码不统一。
如果把共性的内容放到父类了,方法体不一样的写成抽象的,子类在继承父类的时候强制重写这个方法。并且按照父类的格式。所以这种方式相当于有了一种统一的味道。
(4)小结
1、抽象类的作用是什么样的?
抽取共性时,无法确定方法体,就把方法定义为抽象的。 强制让子类按照某种格式重写。抽象方法所在的类,必须是抽象类。
2、抽象类和抽象方法的格式?
1)public abstract 返回值类型 方法名(参数列表);
2)public abstract class 类名{}
3、继承抽象类有哪些要注意?
1)要么重写抽象类中的所有抽象方法
2)要么是抽象类
二:接口
(1)为什么有接口?
假如有一个父类Animal,子类兔子类、青蛙类、狗类继承Animal。其中Animal定义了整个体系的共性,所以我们可以把共性的吃饭、喝水去定义在Animal中,没问题。如果要添加一个新的行为游泳,可以把游泳写在Animal里面吗?不可以!因为三个子类中,狗和青蛙会游泳,但是兔子不会。所以在这个体系中,游泳是不能写在父类的,如果写在里面,就代表所有的子类都拥有游泳的共功能,所以不合适。写在哪呢?写在青蛙和狗里面吗?这样会有一个弊端,无法限定子类中方法书写的格式。青蛙类里面写 public vois swim(){...}
没问题,狗类里面写public void swimming(){...}
也没问题,所以会造成方法不统一。怎么办?我们会定义一个接口叫做游泳swim,在里面就可以定义游泳的抽象方法,这个接口就可以看做是我自己定义的游泳的规则。你想让哪个动物拥有游泳的功能,就让这个动物和游泳这个接口发生关系就行了。最主要的是现在就可以强制要求青蛙类和狗类按照接口里面定义的规则来书写代码,这样代码就统一起来了。
是不是以后子类中所有的特有方法,都需要定义成一个接口?
没必要。比如狗的lookHome,看家这个行为是其他动物没有的,就算定义了看家的接口,也只有狗才能用,其他动物用不了。所以没必要。直接写在狗里面作为特有方法就行了。当我们需要给多个类同时定义规则的时候就要用到接口了,比如这里的swim,狗和青蛙都用到了。
接口:就是一种规则
(2)接口的应用
比如有一个方法,方法的功能是搬家,在以前我们写方法的时候,参数可以写Car,在调用方法的时候就可以传递很多车的对象,只要是Car的子类都可以传递过去,因为有多态是完全没有问题的。但是你要像,能搬家的不仅仅只有车啊,还有搬家公司也能搬家,但是在这个代码中,如果你传递一个搬家公司的对象,代码就会报错。因为在我们现实的逻辑中,搬家公司他不可能去继承Car,所以搬家公司的对象是不可能传递过去的。
public void 搬家(Car c) {
}
搬家(车的对象); // 没毛病
搬家(搬家公司); // 错
那该怎么去完美解决这个问题呢?
我现在需要的是什么?需要的是只要能搬家就行,不管你是黑车,搬家公司还是三轮车,只要有搬家这个行为就可以,所以我现在要的其实不是一个集成体系,而是侧重于要干的活,谁干?怎么干?不管。只要能干就可以。所以可以定义一个运输的接口,在搬家的方法中直接写接口的类型就可以了。那么在调用方法的时候,不管是车还是搬家公司,只要是按照运输规则编写的方法都可以传递过去,这个就是接口。接口不代表一类事物,接口就是一种规则,它是对行为的抽象。
抽象类是表示一类事物的,接口侧重于行为。
public void 搬家(运输的接口 c){
}
public interface 运输 {
...}
(3)接口的定义和使用
1、接口用关键字interface来定义
public interface 接口名{}
2、接口不能实例化
3、接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
4、接口的子类(实现类)
1)要么重写接口中的所有抽象方法
2)要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1, 接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口们1, 接口名2{}
(4)练习
不说了
三:接口的细节:成员特点和接口的各种关系
(1)接口中成员的特点
1、成员变量
1)只能是常量
2)默认修饰符:public static final
为什么?多个子类共有的属性是抽取到接口中的吗?不是!是抽取到父类中。而且接口是一种规则,规则是不能发生改变的,所以接口里面的变量都是常量,会用final去修饰。static是为了方便调用,public 表示在所有地方都能使用接口里面的常量。
2、构造方法
没有
因为接口接口不能创建对象,而且接口当中也不需要给子类的成员变量赋值,所以用不着,Java设计的时候就没有设计构造方法。
3、成员方法
1)只能是抽象方法
2)默认修饰符:public abstract
JDK7以前:接口中只能定义抽象方法
JDK8新特性:接口中可以定义有方法体的方法
JDK9新特性:接口中可以定义私有方法
(2)接口和类之间的关系
1、类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
2、类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
如果多个接口里面有同名的抽象方法怎么办?
重写一次就可以了,重写的这一次表示重写了所有同名的方法
3、接口和接口的关系
继承关系,可以单继承,也可以多继承
public interface Inner1 {
public abstract void method1();
}
interface Inner2 {
public abstract void method2();
}
interface Inner3 extends Inner1, Inner2{
public abstract void method3();
}
class InnerImpl implements Inner3{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
}
四:接口和抽象类的综合案例
public class Test {
public static void main(