Java基础(八)——面向对象基础(四)
一、抽象类
猫狗举例子,猫狗都是动物类,猫狗可以有具体的行为,但动物不行,因为动物是一个大类,不够具体。比如猫吃猫粮,狗就吃狗粮,但这两个吃的东西不一样,Animal类就不能定义具体吃什么;但是可以定义一个吃的方法,不写方法体,但不写方法体会报错;且Animal类不够具体,理应不能实例化,所以,这里引入一个抽象类的概念。
1、abstract——抽象
public abstract class Animal{ // 抽象类
}
被 abstract 修饰的类称为抽象类。
抽象类中的方法有方法体会报错,因此,抽象类中的方法没有方法体:
接着子类继承抽象的父类:
会报错,因为继承抽象父类的子类,必须要重写抽象父类的抽象方法,不然就会报错,这是一种约束行为,你继承了我这个类,就必须重写抽象方法,这时的抽象父类有一种模板的意味:
抽象类可以有构造方法:
但是不能实例化:
抽象类可以有属性,访问修饰符也有,跟普通类一样,看需求:
2、抽象类举例子
假设定义一个门的抽象类,定义开门这个方法,各种门开的方式不同。再定义木门和感应门,都继承门这个抽象类,所以必须重写开门这个方法。木门就是普通的开门即可,感应门人靠近就自动开门。这就是一个简单的抽象类例子。
3、总结
- abstract——修饰的类为抽象类,修饰的方法为抽象方法,抽象的方法没有方法体,这只能存在于抽象类中。
- 普通子类继承抽象类,必须重写抽象方法(约束效果)。
- 抽象类可以有普通方法。
- 抽象类有构造方法,但是不能实例化对象。
二、接口
1、接口介绍
继续引用上面的例子。除了普通开门的方式,还能有密码开门的方式。但是密码开门的方式不适合写在模板里,因为木门和感应门没有这种开门方式,是密码门才有的方式。所以单独给密码门即可,但是想要约束效果的话,就要给到抽象类里面,但是另外两个门没有这种方式,不适合。所以这里引入一个接口的概念:额外的提升类的功能,意思是功能的扩展。
2、interface——接口
interface 修饰的类为接口,是特殊的抽象类:
这里为这个类额外提供一个密码开门的功能。可以发现前面的 public 和 abstract 是灰色的,其实是这里不写也行,因为接口类中,默认方法时公开抽象的。接口方法只需要写返回值和参数列表就够了。
3、implements——接入接口
普通类怎么接入接口呢?通过关键字 implements 接入:
接入之后可以发现报错,这是因为跟抽象类一样,也要重写抽象方法:
可以看到,普通子类既可以继承抽象类,也可以实现接口类:
4、接口的特点
1、接口中同样不允许有方法体,更不允许有普通方法。
2、默认为公开的静态常量:
其他修饰符会报错:
3、接口没有构造方法,因为方法默认都是抽象的,抽象方法没有方法体。更不能实例化。
4、类可以实现多个接口。就是一个类可以接入多个接口,实现多种功能。
5、接口可以多继承接口(多继承概念),可以实现三合一功能,比如一个接口继承三个接口类,那么这个接口就有三个功能,再来个普通子类接入这一个接口,那么这个子类也能有三个功能:
可以看到这个接口什么都不用写都行。
多继承这个概念只存在接口与接口!
6、JDK8以后接口允许存在 static 或者 default 修饰的方法(新特性):
5、总结
再稍微总结一下:
- 通过 implements 实现接口,类只能实现接口,不能继承接口。
- 接口中同样不允许有方法体,更不允许有普通方法。
- 接口没有构造方法,因为方法默认都是抽象的,抽象方法没有方法体。更不能实例化。
- 类可以实现多个接口(多实现概念)。就是一个类可以接入多个接口,实现多种功能。
- 接口可以多继承接口(多继承概念),比如三合一功能。
- JDK8以后接口允许存在 static 或者 default 修饰的方法(新特性)
三、多态
1、多态的概念引入
继续拿猫狗举例子。创建猫狗类,以及一个医生类。:
猫类一样,但输出的不是狗,是猫,然后是医生类,并创建一个打针的方法:
看上述代码,打针方法接收的是猫类以及猫对象,如果传的是狗,则不能接收,不能给狗打针。不同类型不能随意接收。接着实例化:
运行结果:
接着看运行流程:
这时如果把医生打针这个当做一个技能,来尝试能否给狗打针:
很明显报错了,因为接收的是Cat类型,但是这里传的是Dog类型。当然我们可以给医生类再增加一个接收Dog类型的方法,但这样一来,如果有上百种不同的动物,就要有上百种方法。
所以这里开始引入多态的概念。Java中可以引入它自己,以及本身的父类。意思就是,猫是猫,是动物的子类;狗也是动物的子类。是什么动物不要紧,只要它是动物这个类的子类就行。医生给动物打针,给什么动物不重要,重要的是,它是动物就行,或者说是动物这个类的子类就行,这样,就只需要一个方法就能给不同动物打针。
2、多态
接着,医生打针这个方法,接收动物类就行:
然后无论是接收猫类或者狗类,都不报错了:
然后运行效果,跟之前的一模一样,还能给不同动物打针:
3、父类对象指向(引用)子类对象 (接口同理)
为什么传入Cat或者Dog都可以呢?
// 父类对象引用子类对象。这里的 = 是引用的意思。
Animal animal = new Cat();
Animal animal = new Dog();
接口同理,接口也能实现一样的效果,接口对象引用实现类对象。
4、约束对象只能调用父类继承或重写的方法
现在取消抽象:
这时候使用多态方式创建对象:
问:调用的是什么方法?打印的是谁的名字?
答案:
使用多态创建对象,调用方法为子类对象的方法,但是调用的属性是父类的属性。
这时候猫类创建自己独有的方法:
这时会发现父类不能调用子类独有的方法:
这里是因为:约束了对象只能调用从父类继承或者重写的方法。
5、强制转换
在数据类型中,强制转换,存在大转小和小转大:
在这里的对象(引用数据类型)中,也存在大转小和小转大:
看上方代码,当animal是猫时,猫变猫没有问题,但如果是狗时,猫变成狗就要出大问题,但是这里的IDEA并不会给你标红,但是编译运行的时候就会报错,显示类型转换异常。
6、instanceof——判断对象是否为某类型
那么这里可以写一个方法,通过这个方法来判断是否在可转范围,如果不在此范围内就不允许强转。但是需要判断原本是什么类型,这里引入一个关键词 instanseof ,左边写对象,右边写类型,意为判断左边是否是右边这种类型,如果是的话返回 true 。
(对象 instanceof 类型)
这样一来,转换类型就不会出错了,是猫就转猫,是狗就转狗。
四、题外话——面向对象设计原则
简写:开口合里最单依
开:开闭原则
口:接口原则
合:聚合原则
里:替换原则
最:最少原则
单:单一原则
依:依赖原则
开闭原则:
(1)open :对外部代码持放开状态。(上面举的猫狗医生例子中,只要是Animal的子类统统都能接收,不像原本只能接收一种,持开放状态)
(2)close:对内部代码持关闭状态(上面举的猫狗医生例子中,内部只需要一个方法即可,不再需要添加其他方法。内部就可关闭了)