抽象类
抽象类概念,语法,特性
抽象类概念
在面相对象的概念中,所有的对象都是通过类来描绘的,但反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
- Animal是动物类,每种动物都有不同的叫法,但Animal不是一个具体的动物,故此内部bark()方法无法具体实现
- Dog是狗类,狗是动物,与Animal是继承关系,狗是是一种具体的类,bark()可以实现
- Dog是猫类,猫是动物,与Animal是继承关系,猫是是一种具体的类,bark()可以实现
- Animal可以设计为"抽象类",即不是抽象的类就被称为具体类
通过发现,我们可以发现,父类中Animal中的bark()方法没有什么实际工作,动物的叫声都是由Animal中的子类bark()完成的,像这种没有实际的工作,我们把它设计成一个抽象方法(abstractmethod),包含抽象方法的类我们称为 抽象类(abstract class)
抽象类语法
在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,如果你声明出一个抽象的方法,就必须将类也标记为抽象的,你不能在非抽象类中拥有抽象方法,抽象类的最大悲哀就是没有实体
//抽象类:被abstract修饰的类
abstract class Animal {
private String name;//私有成员变量name
//抽象类的构造方法
public Animal(String name) {
this.name = name;
}
public abstract void eat();
//抽象类的实例方法
public void sleep() {
System.out.println(name+" 正在睡觉");
}
//name是私有的,只能在本类中使用,使用公开的接口
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
//子类Dog具体实现eat方法,实现抽象方法时,方法的访问修饰符必须是public,因为抽象方法默认是public的
public void eat() {
System.out.println(getName()+"吃狗粮");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小黑");
dog.eat();
}
}
//抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
抽象类特性
- 抽象类不能直接实例化,但是普通类可以
Animal animal = new Animal();
//Animal是抽象的;无法实例化
- 抽象和抽象方法是使用abstract的修饰的,这个方法没有具体的实现
- 抽象类中不一定包含抽象方法,但是包含抽象方法的类,一定是抽象类。
- 抽象类中可以定义成员变量和成员方法
- 当一个普通类继承抽象类,那么在普通类中一定需要重写抽象类中的抽象方法
- 抽象类存在的最大意义就是被继承
- 如果抽象类A继承了抽象类B,此时抽象类A就不需要重写抽象类B的抽象方法,但是如果抽象类A被普通类继承,就需要重写
- 抽象方法不能是 private(私有的) 的,final也不行,要满足重写的规则,因为抽象类意义就是被继承
- 抽象类当中可以有构造方法,为了方便子类能够调用,来初始化抽象类当中的成员
抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
那么就会出现一个问题:普通的类也可以被继承和重写,为什么非要使用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验,使用抽象类,实际工作不由父类完成,而是子类完成那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
接口
接口的概念,语法,使用
接口概念:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
接口的语法
接口的语法与类的语法相似,只需要将class关键字换成interface关键字,就定义了一个接口。
interface IShape {
public static final age = 10;//默认是public static final修饰的
public abstract void fun();// public abstract 是固定搭配,可以不写
//接口中不能含有具体的实现
default public void fun1() {
System.out.printf("fhoiwej");
}
}
class A implements IShape {
//必须重写接口的抽象方法
public void fun() {
System.out.println("fqwfqwfwq");
}
@Override
public void fun1() {
System.out.println("这个方法的重写是可以选择的");
}
}
class B implements IShape {
public void fun() {
System.out.println("oooooo");
}
}
public class Test {
public static void fun(IShape iShape) {
iShape.fun();
}
public static void main(String[] args) {
fun(new A());//向上转型
fun(new B());
}
}
- 创建接口时,接口的命名一般以大写字母I开头
- 接口的命名一般是形容词的单次
- 接口中的方法和属性不要加任何修饰符号,保持代码的简洁性。
接口的使用
接口的特性
- 使用interface来修饰接口
- 接口中的方法不能在接口中实现的,只能由实现接口的类来实现,抽象方法默认是public abstract的方法
从JDK1.8开始,允许有可以实现的方法,但是这个方法需要default修饰
可以有一个静态方法。 - 成员变量默认是public static final修饰的
- 接口中每一个方法都是public的抽象方法,接口中的隐藏方法会被隐式为public abstract(只能是public abstract,其他修饰符都会报错)
- 接口不能被实例化
- 类和接口之间采用implements 来实现多个接口
- 当一个类实现了一个接口之后,这个类必须重写这个接口当中的抽象方法,如果没有实现接口中的抽象方法,则类必须设置为抽象类
- 当接口当中存在default方法,可以选择重写,也可以选择不重写
- 不管是接口还是抽象类,都可以发生向上转型
- 子类实现接口方法时,方法一定是public修饰的
- 接口中不能有静态代码块和构造方法
接口的实例
interface IUSB {
void openDevice();
void closeDevice();
}
//鼠标类,实现USB接口
class Mouse implements IUSB{
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
//子类特有的方法
public void click() {
System.out.println("鼠标点击");
}
}
//键盘类,实现USB接口
class KeyBoard implements IUSB {
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void input() {
System.out.println("键盘输入文字");
}
}
class Computer {
public void open() {
System.out.println("打开电脑");
}
public void colse() {
System.out.println("关闭电脑");
}
//使用服务
public void useDevice(IUSB iusb) {
//通过iusb只能调用两个方法,iusb.openDevice()和iusb.closeDevice(),
//调用键盘这个input是不行的,因为是键盘类input和鼠标类click是特有方法,所以这里需要判断,发生向下转型
iusb.openDevice();
// instanceof 用于判断一个对象是否属于某个类或其子类的实例
//如果iusb引用的是键盘
if (iusb instanceof KeyBoard) {
//将父类类型给子类类型,进行强制类型转换
KeyBoard keyBoard = (KeyBoard) iusb;
keyBoard.input();
} else if (iusb instanceof Mouse) {
Mouse mouse = (Mouse) iusb;
mouse.click();
}
iusb.closeDevice();
}
}
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
//打开电脑
computer.open();
//使用鼠标设备
computer.useDevice(new Mouse());//引用键盘这个对象
//使用键盘设备
computer.useDevice(new KeyBoard());
//关闭电脑
}
}
实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口(解决了多继承的问题)
//分别提供会飞的,跑的,游的接口
interface IFlying {
void fly();
}
interface ISwimming {
void swim();
}
interface IRunning {
void run();
}
abstract class Animal {
private String name;//对成员进行封装,其他类要想使用提供set和get方法即可
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Animal(String name) {
this.name = name;
}
public abstract void eat();
public void sleep(){
System.out.println(name+"正在睡觉");
}
}
class Dog extends Animal implements IRunning,ISwimming {
public Dog(String name) {
super(name);//子类构造时要先帮助父类去构造,通过super去完成
}
@Override
public void swim() {
System.out.println(this.getName()+"正在游泳");
}
@Override
public void run() {
System.out.println(this.getName()+"正在跑");
}
@Override
public void eat() {
System.out.println(this.getName()+"正在吃骨头");
}
}
class Bird extends Animal implements IFlying {
public Bird(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(this.getName()+"正在飞");
}
@Override
public void eat() {
System.out.println(this.getName()+"正在吃鸟粮");
}
}
class Robot implements IRunning {
@Override
public void run() {
System.out.println("机器人正在跑");
}
}
public class Test {
public static void test(Animal animal) {
animal.eat();
}
public static void test1(IFlying iFlying) {
iFlying.fly();
}
public static void test2(IRunning iRunning) {
iRunning.run();
}
public static void main(String[] args) {
test(new Bird("布吉岛"));
test(new Dog("旺财"));
test1(new Bird("布吉鸟"));
test2(new Dog("阿狗"));
test2(new Robot());
}
}
一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
多态的好处:忘记类型,有了接口之后,类的使用者不必关注具体的类型,只需要关注某个类具备什么样的能力。在Java中,类和类之间是单继承,一个类可以实现多个接口,接口与接口之间可以多继承。即:使用接口可以达到多继承的目的。
接口可以继承一个接口,达到复用的效果,使用extends关键字。
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法,接口间的继承相当于多个接口拼接在一起(拓展接口功能)。
抽象类和接口的区别
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和普通字段可以不需要被重写就直接使用,而接口中不能包含普通方法(要加普通方法需要前面加default关键字修饰),子类必须重写父类所有的抽象方法。
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
//抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类.万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.
No | 区别 | 抽象类(abstract) | 接口(interface) |
---|---|---|---|
1 | 结构组成 | 普通类+抽象方法 | 抽象方法+全局常量 |
2 | 权限 | 各种权限,可以有非抽象的方法 | 方法默认public,所有方法在接口中不能有实现 |
3 | 子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现接口 |
4 | 关系 | 一个抽象类可以实现若干个接口 | 接口不能继承抽象类但是接口可以使用extends关键字继承多个父接口 |
5 | 子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
6 | 静态代码块+静态方法 | 可以有静态代码块和静态方法 | 不能含有静态代码块以及静态方法 |
7 | 成员变量 | 各种类型 | 只能是成员变量只能是public static final类型的 |