一、抽象类
(一)概念
在面向对象的概念中,所有的对象都是通过类来描绘的。但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
简单来说,就是只进行抽象的“喊口号”,而暂且先不管具体如何实现,就叫抽象类。
比如你的朋友对你说:“我想找一个对象”,你问他想找什么样的,他说不出具体细节,但只是说他想找一个对象。那么他这样的描述就是“没有包含足够信息”的,就属于抽象类。——具体细节的定义,可由抽象类的子类来完成。
由于抽象类并不包含足够的信息,因此抽象类不能实例化对象。而抽象类虽然抽象,但它仍然还是个”类“,因此类的三要素依然存在,即成员变量、成员方法、构造函数,且调用方法与普通类一致。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见方法,但由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类。而一个类却可以实现多个接口。
(二)和普通类的区别
- 普通类可以实例化对象,而抽象类不能实例化对象。
- 有成员变量和方法的可以是普通类也可以是抽象类,但是有抽象方法的一定是抽象类。
- 普通类可以被继承、也可以不被继承,抽象类只能被继承、必须被继承。且由于抽象类中包含抽象方法,因此必须被子类继承并实现抽象方法。
- 子类必须重写抽象类中所有的抽象方法,若不能全部重写,则子类也必须声明为抽象类。而普通类被继承时,是否要重写其中的方法是可选的。
(三)语法
//只需要在 class 前面加上 abstract 即可将普通类变为抽象类
abstract class User {
}
抽象类也是类,也有成员变量和方法。需要注意的是,它们的访问修饰符一定不能是private,因为如果是private,子类就继承不到了,那么抽象类就没有任何意义了。
抽象类存在的最大的意义就是被继承,当被继承后就可以利用抽象类实现多态。
举例:
//定义了一个动物的抽象类
abstract class Animal {
//提供了一个“吃”的抽象方法,但并没有方法体
public abstract void eat();
}
//定义了一个“狗”类,继承“动物”类
class Dog extends Animal {
//重写抽象类的抽象方法,并给出具体实现
@Override
public void eat() {
System.out.println("吃骨头");
}
}
//定义了一个“猫”类,继承“动物”类
class Cat extends Animal {
//重写抽象类中的抽象方法,并给出具体实现
@Override
public void eat() {
System.out.println("吃鱼");
}
}
//执行
public class Main {
public static void main(String[] args) {
//使用多态的方式创建对象
Animal ani = new Dog();
Animal ani2 = new Cat();
//调用eat()方法实现不同行为,即多态
ani.eat(); //输出:吃骨头
ani2.eat(); //输出:吃鱼
}
}
此处涉及到多态的相关知识,见上文相关内容即可。
(四)抽象类的构造方法
抽象类的构造方法和普通类的构造方法以及子类继承后的调用用法是类似的。直接看下面例子:
abstract class Animal{
public String name;
public Animal(String name){
System.out.println("动物");
}
}
class Dog extends Animal{
public Dog(String name) {
super(name);
System.out.println(name);
}
}
class Cat extends Animal{
public Cat(String name) {
super(name);
System.out.println(name);
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog("哈士奇");
Animal animal1= new Cat("美短");
}
}
(五)抽象类和接口
抽象类的例子:
abstract class Animal {
abstract void eat();
void run() {
System.out.println("Animal run.");
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("狗吃骨头");
}
}
class Cat extends Animal {
@Override
void eat() {
System.out.println("猫吃鱼");
}
}
public class Main {
public static void main(String[] args) {
Animal ani = new Dog();
Animal ani2 = new Cat();
}
}
接口的例子:
interface Animal {
void eat();
void run();
}
class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void run() {
System.out.println("狗跑");
}
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void run() {
System.out.println("猫爬");
}
}
总结:
抽象类中可以有抽象方法,也可以有普通方法。接口中只能是抽象方法。
所有继承抽象类的子类,可以重写抽象方法,也可以直接调用父类的普通方法。而继承接口的子类必须实现接口中的抽象方法。
定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口,可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
二、接口
(一)概念
接口(interface),在Java语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但它们属于不同的概念。类描述对象的属性和方法,接口则包含类要实现的方法。
除非实现接口的是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则必须声明为抽象类。另外,在Java中,接口类型可用来声明一个引用变量,它们可以绑定在一个以此接口实现的对象上(多态)。
(二)接口与类的区别
- 接口不能用于实例化对象。
- 接口不能有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后接口中可以使用以
default
关键字修饰的非抽象方法。 - 接口中定义的成员变量默认被
public static final
所修饰。另外,一般情况下我们不会在接口中定义变量。 - 接口不是被类继承(extends)了,而是被类实现(implements)了。
- 接口支持多继承。
(三)接口的特点
- 接口中的每一个方法是隐式抽象的,接口中的方法会被隐式的指定为
public abstract
(注:如果你手写,则不能写除了public abstract之外的修饰符,否则会报错)。 - 接口中可以含有变量,但接口中的变量会被隐式的指定为
public static final
变量(注:如果手写,则不能写除public之外的修饰符,否则会报错)。 - 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
- 接口可以通过
extends
继承其他接口,且允许多继承。
(四)接口与抽象类的区别
- 抽象类中的方法可以是有方法体的普通方法,但是接口中的方法必须全部都是抽象方法,不能有方法体。(注:JDK1.8以后,接口里允许有以
default
修饰的普通方法) - 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是
public static final
类型的。 - 一个类只能继承一个抽象类(单继承),而一个类可以实现多个接口。另外,接口可以在继承一个父类的同时再去实现多个接口(注:这种情况下,若父类与接口之间存在冲突了,父类具有决定权、优先级最高)。
(五)接口的语法
接口的声明语法格式如下:
[访问修饰符] interface 接口名称 [extends 其他的接口名] {
//声明变量
//抽象方法
}
示例:
/* 文件名:Animal.java */
public interface Animal {
void eat();
void travel();
}
类对接口实现的语法格式如下:
[访问修饰符] class 类名 implements 接口名 [, 其他接口名, 其他接口名, ...] {
//...
}
示例:
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog eats.");
}
@Override
public void travel() {
System.out.println("Dog travels.")
}
public int countLegs() {
return 4;
}
}