6.13、多态
多态的基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的使用前提
- 多态必须发生在子父类关系中。(继承关系)
- 使用多态一般要使用方法的重写,如果不使用方法的重写,那么多态就没有意义了。
好处:优化代码设计,比较强调代码的设计思想。
多态的使用
方法的多态
重写和重载就体现多态
对象的多态 (核心,困难,重点)
(1) 一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时,就确定了,不能改变
(3) 运行类型是可以变化的
(4) 编译类型看定义时 =号的左边,运行类型看 =号 的右边
多态的向上转型
本质:父类的引用指向了子类的对象
语法:父类类型 引用名 = new 子类类型()
特点:
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类中特有成员
- 最终运行效果看子类的具体实现!
多态的向下转型
语法:子类类型 引用名 = (子类类型) 父类引用
前提:发生了向上转型
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
当向下转型后,可以调用子类类型中所有的成员
如何解决类型转换异常
向下转型,可能会发生java.lang.ClassCastException: 类型转换异常(运行期异常)
instanceof
关键字
instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
语法:
//如果父类的引用是子类的类型:返回true
//如果不是:返回false
if(父类的引用 instanceof 子类类型){
//转换
}else{
System.out.println("类型转换异常!");
}
多态案例
父类:宠物类 属性:名字,种类 抽象方法:eat,call
子类:鸟,狗,猫 属性:名字,种类 重写:eat、call 自己独有的方法
manager类:feed(Pet pet)方法,findName(Pet[] pets,String name)方法,Pet findType(int num)方法,doSomething(Pet pet)方法
public abstract class Pet {
private String name;
private String variety;
public Pet(){};
public Pet(String name, String variety) {
this.name = name;
this.variety = variety;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVariety() {
return variety;
}
public void setVariety(String variety) {
this.variety = variety;
}
public abstract void eat();
public abstract void call();
}
public class Bird extends Pet{
public Bird(){};
public Bird(String name, String variety) {
super(name, variety);
}
@Override
public void eat() {
System.out.println("鸟吃五谷");
}
@Override
public void call() {
System.out.println("啾啾~~~");
}
public void fly(){
System.out.println("俺能飞~~~");
}
}
public class Dog extends Pet{
public Dog(){};
public Dog(String name, String variety) {
super(name, variety);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void call() {
System.out.println("汪汪~~~");
}
public void destoryHome(){
System.out.println("拆家~~~");
}
}
public class Cat extends Pet{
public Cat(){};
public Cat(String name, String variety) {
super(name, variety);
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void call() {
System.out.println("喵喵~~~");
}
public void CatchMouse(){
System.out.println("抓老鼠");
}
}
public class Manger {
public String name;
/* public void feed(Dog dog){
dog.eat();
dog.call();
}
public void feed(Cat cat){
cat.eat();
cat.call();
}*/
//此处喂食方法形参是宠物类的对象,但在调用该方法时
// pet 的形态会发生变化,有 dog、cat、bird三种宠物,这就是多态
public void feed(Pet pet){
pet.eat();
pet.call();
}
//新建Pet[]保存3只宠物,写一个方法查找名称为xxx的宠物,然后调用eat()方法。
public void findName(Pet[] pets,String name){
for (Pet pet : pets) {
if (pet.getName().equals(name)){
pet.eat();
return;
}
}
System.out.println("没有找到该宠物");
}
//根据宠物的类型来进行不同的调用 : 1 : 返回狗 2:返回猫 3:返回鸟
public Pet findType(int num) {
Pet pet = null;
if (num == 1) {
pet = new Dog();
}else if (num == 2) {
pet = new Cat();
}else if (num == 3) {
pet = new Bird();
}else {
System.out.println("类型错误");
}
return pet;
}
public void doSomeThing(Pet pet){
pet.eat();
pet.call();
if (pet instanceof Dog){
Dog dog = (Dog) pet;
dog.destoryHome();
} else if (pet instanceof Cat) {
((Cat)pet).CatchMouse();
} else if (pet instanceof Bird) {
((Bird) pet).fly();
}else {
System.out.println("类型转换异常");
}
}
}
public class Test1 {
public static void main(String[] args) {
Bird bird = new Bird("花花", "画眉鸟");
Dog dog = new Dog("大黄", "泰迪");
Cat cat = new Cat("小妖", "加菲猫");
Manger manger = new Manger();
manger.name = "黄某";
//调用方法时,向上转型
// dog -> pet , cat -> pet , bird -> pet
manger.feed(dog);
manger.feed(cat);
manger.feed(bird);
System.out.println("===============");
//应用2:在创建对象的时候,可以是多态的方式
// 父类类型 父类的引用 = new 子类类型();
//父类的引用指向子类对象
Pet pet1 = new Bird("多多", "大鸟");
//看起来pet是父类,本质上是子类
//编译看左边,运行看右边,调用的是父类中的方法,运行的是子类中重写后的方法
// 父类的引用不能调用子类独有的方法!!
Pet pet2 = new Dog("小黄", "泰迪");
Pet pet3 = new Cat("小花", "加菲猫");
manger.feed(pet1);
manger.feed(pet2);
manger.feed(pet3);
Pet[] pets ={new Bird("花花", "画眉鸟"),
new Dog("大黄", "泰迪"),
new Cat("小妖", "加菲猫")};
manger.findName(pets,"大黄");
//根据序号返回具体的动物类型
Pet pet = manger.findType(2);
if (pet != null){
pet.eat();
}
System.out.println("===============");
// 向上转型后,发现不能再调用子类独有的方法了
// 这时需要向下转型 -> 强制转换
Bird bird1 = (Bird) pet1;
bird1.fly();
//但是有时你并不知道,他向上转型之前是什么类型,需要用 instanceof 判断
//instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
if (pet2 instanceof Bird){
Bird bird2 = (Bird) pet2;
bird2.fly();
}else {
System.out.println("类型转换异常");
}
if (pet2 instanceof Dog){
Dog dog2 = (Dog) pet2;
dog2.destoryHome();
}else {
System.out.println("类型转换异常");
}
System.out.println("===============");
manger.doSomeThing(cat);
}
}
6.14、接口
接口的概念
接口是更加抽象的抽象的类
接口:在java中,接口也是一个类,但是这个类不能写 class ,只能写 interface ,
在接口中,一般只能定义抽象方法,只能定义静态常量,接口起到了定义规则和规范的作用,体现了代码优化的思想、程序设计的多态和高内聚低耦合的设计思想,表现出的是一种能力。Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。
接口的特点
-
接口中的方法一般都是 抽象方法 :public abstract(隐式存在/可以不写)
-
接口中的变量都是 静态常量 :public static final(隐式存在/可以不写)
-
接口是一个特殊的类,所以不能写class修饰,接口中的方法默认是public的
-
接口不能实例化!!!
-
接口中不能存在构造方法!
-
一个类可以 实现多个接口 ,所以接口是对java单继承的扩展,提高代码的扩展性。
-
一个类可以继承父类,同时可以实现多个接口。语法: public class 类名 extends 父类 implements 接口1, 接口2,接口3…
-
实现类必须实现接口中的全部方法,如果一个实现类只想实现接口的部分方法,那么必须要把该类定义成抽象类。
public abstract class InterImpl2 implements Inter{ }
-
接口是可以继承接口的,接口可以实现 多继承 ,一个接口可以继承多个接口!!!
-
接口继承其他接口时,如果其他接口的抽象方法名重复,那么会默认使用第一个有此方法的接口,因为抽象方法没有具体实现,所以重写那个抽象方法都是一样的!!!
-
类只能继承类,类只能实现接口。
-
实现类一旦实现了接口中的抽象方法,就不能修改方法的任何定义!!!
如何实现接口
语法:
public interface 接口名{
//静态常量
//抽象方法
//普通方法(jdk新特性)
}
案例:
案例:实现防盗门功能
实线:继承 ; 虚线:接口
TheftproofDoor:防盗门 ; Door:门 ; Lock: 锁
防盗门 is a 门 ; 防盗门 has a 锁 功能 —> 能力
TheftproofDoor extends Door implements Lock
public abstract class Door {
public abstract void doorUp();
public abstract void doorDown();
}
public interface Doorbell {
void takePhoto();
}
public interface Lock {
void lockUp();
void lockDown();
}
public class SafeDoor extends Door implements Lock, Doorbell{
@Override
public void doorUp() {
System.out.println("用力推,门打开了。");
}
@Override
public void doorDown() {
System.out.println("轻轻拉门,门关上了。");
}
@Override
public void takePhoto() {
System.out.println("铃......咔嚓......照片已存储");
}
@Override
public void lockUp() {
System.out.println("插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙。");
}
@Override
public void lockDown() {
System.out.println("插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙。");
}
}
public class Test01 {
public static void main(String[] args) {
SafeDoor safeDoor = new SafeDoor();
safeDoor.doorDown();
safeDoor.lockDown();
safeDoor.takePhoto();
safeDoor.lockUp();
safeDoor.doorUp();
}
}
抽象类和接口有哪些区别
- 定义上来说:
抽象类: public abstract class 类名{}
接口: public interface 接口名{}
接口使用比较多
- 具体使用上来说:
抽象类:可以定义普通方法,可以有构造方法,可以定义成员变量。
接口:一般定义成抽象方法,不能有构造方法,变量都是静态常量
抽象类和接口都不能实例化。
- 实际使用来说:
抽象类:is…a
接口:has…a
实现接口 与 继承 的区别
1)接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性。
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即 更加的灵活
2)接口比继承更加灵活
接口比继承更加灵活,继承是满足 is … a 的关系,而接口是 is … a 的关系
3)接口在一定程度上实现代码解耦[即:接口规范性 + 动态绑定机制]
使用多态的写法实例化对象
Pet pet = new Dog();
1)父类类型是普通类型
语法: 父类类型 父类引用 = new 子类类型();
2)父类类型是抽象类
语法: 抽象类 父类引用 = new 子类类型();
3)父类类型是接口
语法: 接口 接口引用 = new 实现类();
抽象类和接口在实际开发中的选择
- 抽象类侧重的是子类全部 共有的属性和行为 。然后用子类去继承。
- 接口侧重的是 部分子类具有的一些行为和能力 。然后用实现类去实现接口。
接口作为形参使用=多态
案例:学生管理
分析:
学生管理:StudentManager类
Student 父类 – study()
Code 接口
JAVA学生: coding();
WEB学生: coding();
UI学生:
public interface Code {
//接口中的抽象方法
void coding();
}
public abstract class Student {
//抽象类的抽象方法
public abstract void study ();
}
// JavaStudent 继承了 Student ,实现了 Code
public class JavaStudent extends Student implements Code{
//重写接口的 coding 方法
@Override
public void coding() {
System.out.println("Java学生敲代码");
}
// 重写抽象类的 study 方法
@Override
public void study() {
System.out.println("Java学生在学习……");
}
}
// JavaStudent 继承了 Student ,实现了 Code
public class WebStudent extends Student implements Code{
//重写接口的 coding 方法
@Override
public void coding() {
System.out.println("Web学生敲代码");
}
// 重写抽象类的 study 方法
@Override
public void study() {
System.out.println("Web学生在学习……");
}
}
// UIStudent 只继承了 Student
public class UIStudent extends Student{
// 重写 抽象类的 study 方法
@Override
public void study() {
System.out.println("UI学生在学习……");
}
}
public class StudentManger {
//把抽象类对象作为形参
public void study(Student student){
student.study();
}
//把接口对象作为形参
public void code(Code code){
code.coding();
}
//把接口作为方法的返回值类型,返回值必须要实现了接口的对象
public Code reIndex(){
return new JavaStudent();
}
}
public class Test4 {
public static void main(String[] args) {
// UIStudent 向上转型 -> Student(抽象类)
Student student = new UIStudent();
student.study();
// JavaStudent 向上转型 -> Code(接口)
Code code = new JavaStudent();
code.coding();
//创建一个学生管理者对象,调用学生管理者的方法
StudentManger studentManger = new StudentManger();
// study 方法传递的参数必须是 Student抽象类的子类
studentManger.study(new UIStudent());
// code 方法传递的参数必须是 Code接口的实现类
studentManger.code(new JavaStudent());
// reIndex 方法的返回值类型是 Code。
studentManger.reIndex().coding();
}
}
接口作为类的属性使用=多态
案例:
USB接口:USBService 服务/功能
void connect();
USB风扇:USBFan
USB鼠标:USBMouse
U盘:USBDisk
扩展USB接口,实现电脑接入USB设备进行工作。组装一台计算机。
public interface USBService {
void connect();
}
public class USBDisk implements USBService{
@Override
public void connect() {
System.out.println("U盘已连接到电脑");
}
}
public class USBFan implements USBService{
@Override
public void connect() {
System.out.println("USB风扇已连接到电脑");
}
}
public class USBMouse implements USBService{
@Override
public void connect() {
System.out.println("USB鼠标连接到电脑");
}
}
public class Computer {
//接口作为类的属性使用
USBService usb;
//USBService usb = new USBDisk(); //也可以之间给这个属性创建对象
public void work(){
usb.connect();
}
}
public class Test3 {
public static void main(String[] args) {
new USBFan().connect();
new USBMouse().connect();
new USBDisk().connect();
Computer computer = new Computer();
//为类的属性创建对象
computer.usb = new USBDisk();
computer.work();
}
}
原创文章,作者:306829225,如若转载,请注明出处:https://blog.ytso.com/tech/java/275778.html