抽象类与接口
一、抽象类
1.1.抽象类的概述:
我们把一个不是具体的功能称为抽象的功能,而一个类中如果有抽象的功能,该类必须是抽象类。
- 抽象类的特点:
- 抽象类和抽象方法必须用abstract关键字修饰
- 抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类 (即带有抽象方法的)
- 抽象类不能实例化
因为它不是具体的。
抽象类有构造方法,但是不能实例化,构造方法的作用是 用于子类访问父类数据的初始化 - 抽象的子类
a:被抽象类abstract修饰的子类,也是一个抽象类
b:不加abstract的话,通过多态,抽象类的子类可以被实例化,但是必须重写抽象类中所有的抽象方法.
抽象类的实例化其实是靠具体的子类实现的。是多态的方式。
Animal a = new Cat();(前提是cat已经被实例化)
1.2.抽象类的成员特点
- 成员变量:抽象类的成员方法既可以是常量,也可以是变量,但是不可以抽象化
- 构造方法:抽象类有成员方法,用于子类访问父类数据的初始化
- 成员方法:既可以是抽象的也可以是非抽象的
抽象类成员方法的特性:
1.抽象方法:强制要求子类必须做的事,即子类必须重写父类有的抽象方法
2.非抽象方法:子类可以从父类继承的,提高代码复用性
示例代码:
//这是一个抽象类
abstract class Animel{
public abstract void eat();//正确,不带主体的抽象方法
//public abstract void sleep() {}//报错,因为抽象方法不能有主体。即大括号中的代码块
public void sleep(){
System.out.println("睡觉");//这是抽象类中的一个非抽象方法,可以被子类继承的
}
}
abstract class dog extends Animel{}//如果加了abstract,抽象类的子类也是抽象类
class cat extends Animel{
public void eat() {
System.out.println("吃饭");
//不加abstract的话,通过多态,抽象类的子类可以被实例化,但是必须重写抽象类中所有的的方法
//如果没有全部重写,将会报错
}
}
public class AbstractTest {
public static void main(String[] args) {
//Animel a = new Animel();//错误,抽象类不能直接实例化
Animel a= new cat();//抽象类通过多态实现子类实例化
a.eat();
a.sleep();//未被抽象的方法
cat c= new cat();//具体对象调用
c.eat();
c.sleep();
}
运行结果:
吃饭
睡觉
吃饭
睡觉
1.3.抽象类的注意事项
抽象类的使用限制
- 不能与final (冲突)
- private(冲突)
- static(无意义) 共存
原因:首先因为被final和private修饰的方法不允许被重写或者覆盖,而被abstract修饰的方法又必须在子类中重写,所以这abstract与这两个关键字冲突,关于static关键字,因为被abstract修饰的方法不允许有方法体,而被static修饰的方法可以直接通过类名调用,而调用一个没有方法体的方法是没有意义的
1.4.抽象类总结
1、抽象类不能被实例化,实例化的工作应该交由它的子类来完成,它只需要有一个引用即可。
2、抽象方法必须由子类来进行重写。
3、只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。
4、抽象类中可以包含具体的方法,当然也可以不包含抽象方法。
5、子类中的抽象方法不能与父类的抽象方法同名。
6、abstract不能与final并列修饰同一个类。
7、abstract 不能与private、static、final或native并列修饰同一个方法。、
1.5.抽象类的三种调用方式
abstract class Animel{
private int age ;
private String name;
abstract void eat() ;
Animel(){};
public Animel(String name,int age) {
this.name=name;
this.age=age;
}
public void SetName(String name) {
this.name =name;
}
public void SetAge(int age) {
this.age=age;
}
public String GetName() {
return name;
}
public int GetAge() {
return age;
}
}
class dog extends Animel{
public dog() {};
public dog(String name,int age) {
super(name,age);
}
public void eat() {
System.out.println("狗吃肉");
}
}
public class CatDemo {
public static void main(String[] args) {
//调用方式一,构造方法调用
dog d = new dog("哈士奇",20);
d.eat();
System.out.println(d.GetAge()+"------"+d.GetName());
System.out.println("------------");
// 调用方式二,具体调用
dog d1= new dog();
d1.eat();
d1.SetAge(19);
d1.SetName("哈巴狗");
System.out.println(d1.GetAge()+"------"+d1.GetName());
System.out.println("------------");
//调用方式三.抽象调用
Animel d2 = new dog();
d2.eat();
d2.SetAge(99);
d2.SetName("萌犬");
System.out.println(d2.GetAge()+"------"+d2.GetName());
}
}
二、接口
2.1.接口的概述
-
接口用关键字interface表示 (注意,接口不需要加class)
interface 接口名 {} -
类实现接口用implements
class 类名 implements 接口名 {} -
接口不能直接实例化 ,如果需要实例化,必须按照多态的方式来实例化,即利用一个非抽象化的子类进行实例化
-
接口的子类 ,可以是抽象类。但是意义不大。也可以是具体类。但是必须要重写接口中的所有抽象方法。
-
接口并不是实际的一个类,而是功能的拓展,由此可见多态有三种实现方法:
A:具体类多态(很少用)
B:抽象类多态(常用)
C:接口多态(最常用)ps(1.接口名+Impl这种格式是接口的实现类惯用格式 ,2,在实现接口的时候需要注意调用的时候只能调用该接口有的方法 )
2.2.接口的成员特点
- 成员变量:接口中的变量默认是常量,默认带有 public static final ,不可被重新赋值,同时因为它是静态的, 因此即使另一个类处于不同包下,也可以通过接口名.成员变量名来访问接口里的成员变量。,但是建议自己手动给出public static final,
- 成员方法:接口中的方法只能是抽象方法,且默认为抽象方法,即使没有加抽象修饰符abstract,
因为接口中的成员方法默认加了public abstract,但是为了直观,依旧建议自己手动给出public abstract ,而且, 接口中的成员方法与抽象类类似,都不带有方法主体 - 构造方法:接口没有构造方法
2.3.接口与类之间的关系
- 与类:
继承关系,只能单继承,可以多层继承。(不允许存在son extends father, mother{}这种情况 ) - 类与接口:
实现关系,可以单实现,也可以多实现。(例如:class Son implements Father,Mother{})
并且还可以在继承一个类的同时实现多个接口。(class son extends father implements mother,sister {}(解释:子类继承父类并实现母和姐妹接口)) - 接口与接口:
继承关系,可以单继承,也可以多继承。(interface Sister extends Father,Mother { })继承的接口,可以继承功能
三、抽象类与接口的区别
3.1.成员区别
- 抽象类:
成员变量:可以变量,也可以常量
构造方法:有 构造方法
成员方法:可以抽象,也可以非抽象
- 接口:
成员变量:只可以常量 (即被static final)
成员方法:只有抽象 方法
没有构造方法
3.2.关系区别
- 类与类 继承,单继承
- 类与接口 实现,单实现,多实现
- 接口与接口 继承,单继承,多继承
3.3.设计理念区别
抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能,大致来说当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口,通俗的说,类用来抽象共性,而接口用于抽象功能,例如鸟和猫可以抽取一个动物的共性,但是鸟和飞机一般是抽象出一个fly()的行为的接口。