在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,一个类通过继承接口的方式,从而来继承接口的抽象方法
接口
- 接口不能被实例化
- 接口中没有构造方法
- 使用 implements 继承接口,支持多继承
- 实现某个接口的类必须在类中实现该接口的全部方法
- 接口中的变量必须是 public static final 类型的(常量)
// 此变量默认为public static final的
int count = 520;
public static final int num = 1314;
- 接口内的方法都是抽象的(不用abstrcat声明,默认就是抽象的)
- 接口中的抽象方法默认为 public abstract
// 抽象方法,默认为public abstract
public abstract void move();
abstract void move1();
public void move2();
void move3();
- 接口中不能含有静态代码块和静态方法(JDK 1.8 以后,接口里可以有静态方法和方法体)
- 所以接口中的方法体类型可以为default 和 static(默认都是public的)
default void run() { }
static void runs() { }
接口设计的目的:对类的行为进行约束,一种行为规范
抽象类
- 不能被实例化
- 使用extends继承抽象类,支持单继承
- 抽象类中的变量和普通类中的变量一样
- 抽象类可以有静态代码块和静态方法
- 如果类中有抽象方法,则必须为抽象类
- 抽象类中可以有非抽象方法
- 抽象方法的类型
public abstract void head();
abstract void foot();
- 非抽象方法的类型
public void hand() { }
protected void ear(){ }
void eye() { }
抽象类设计的目的:类的模板,为了代码复用
实例分析
创建一个接口
// 默认为abstract
public interface Behavior {
// 此变量默认为public static final的
int count = 520;
// 抽象方法,默认为public abstract
void move();
// 非抽象方法
default void run() {
System.out.println("run");
}
}
此接口定义了一个抽象方法 move() 和一个非抽象方法 run()
我们要记住,接口的目的是为了对类的行为进行约束,所以以后我们继承这些行为并重写接口里面的功能时,就只能用move和run这个名字,每个人的命名方式都是不同的,所以我们使用接口对代码进行了约束
创建一个抽象类
public abstract class Animal {
// 变量与普通类中的变量一样
String name = "robot";
Animal() {
// 构造方法
System.out.println("抽象类构造方法");
}
// 抽象方法
public abstract void head();
// 非抽象方法
public void body() {
System.out.println("body " + name);
}
}
此抽象类定义了一个变量、构造方法、一个抽象方法 head 和一个非抽象方法 body
在非抽象方法body中实现一些功能,这样以后我们定义类的时候,就不用每次都再去定义一些相同的功能了,只需继承这个抽象类即可,所以说设计抽象类的目的就是为了代码复用,省去了很多重复的代码
在具体的类中实现
public class Cat extends Animal implements Behavior{
@Override
public void head() {
// 重写抽象类的抽象方法
System.out.println("Override head");
}
@Override
public void body() {
// 重写抽象类的非抽象方法
super.body();
System.out.println("Override body");
}
@Override
public void move() {
// 重写接口的抽象方法
System.out.println("Override move " + count);
}
@Override
public void run() {
// 重写接口中的非抽象方法
System.out.println("Override run " + count);
}
public static void main(String[] args) {
Cat cat = new Cat();
cat.head();
cat.body();
cat.move();
cat.run();
}
}
输出结果如下
此实现类继承了 Animal 抽象类和 Behavior 接口,并重写了它们的方法
根据以上结果可以看到
- 因为抽象类有构造方法,Cat类继承了抽象类,所以在实例化Cat类时,执行了父类的构造方法
- 调用了head方法,因为重写了抽象的head方法,所以输出“Override head”
- 调用了body方法,此方法是Animal抽象类中的非抽象方法,此方法本身具有输出("body " + name)的功能,因为 Cat 类继承了这个抽象类,并重写了这个非抽象方法,所以既输出原抽象方法中的内容("body" + name”),也输出重写之后的内容“Override body”
- 调用了move方法,因为继承了接口,所以拥有接口的属性,因此可以直接使用count变量,输出 “Override move 520”
- 最后调用了run方法,并重写run方法,输出 “Override run 520”(但不会输出原run方法中的内容,因为对接口中的非抽象方法重写时,不能使用super.run()继承父类的方法内容)
总之,一定要记住两者的本质区别
- 接口设计的目的:对类的行为进行约束,一种行为规范
- 抽象类设计的目的:类的模板,为了代码复用
对于抽象类和接口的理解,随着JDK版本的更新,也做出了一些改动
我在网上看到很多资料,内容大部分意思相近,但是仍有少部分不同,原因就是JDK的版本在不断更新,所以建议参考JDK原码来进行学习