一、多态简介
多态是面向对象的三大特性之一,建立在封装和继承之上
1.1相关概念
多态的定义:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作
多态性是对象多种表现形式的体现
1.2实现多态的相关技术
动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法
引出静态绑定:
也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用哪个方法。 典型代表函数重载。
1.3 多态的作用
消除类型之间的耦合关系。
1.4多态存在的三个必要条件
继承
重写
父类引用指向子类对象:Parent p = new Child();
1.5多态的好处
1.可替换性(substitutability)。多态对已存在代码具有可替换性。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
6.消除类型之间的耦合关系
二、方法的多态
方法的重写和重载就是方法的多态的体现形式
class Person {
public void eat () {
System.out.println("吃大餐");
}
//方法重载 Overload
public void eat (String food) {
System.out.println("吃" + food);
}
}
class Man extends Person {
//方法重写
@Override
public void eat() {
System.out.println("吃小餐");
}
@Override
public void eat(String food) {
System.out.println("吃"+ food);
}
}
public class Demo1 {
public static void main(String[] args) {
//......
}
}
三、对象的多态【*】
一个对象的 编译类型 和运行类型是不一样的
编译类型是在定义对象时,就已经确定
而运行类型是可变的
编译时多态性:对于多个同名方法,如果在编译时能够确定执行同名方法中的哪一个,则称为编译时多态性.
运行时多态性:如果在编译时不能确定,只能在运行时才能确定执行多个同名方法中的哪一个,则称为运行时多态性.方法覆盖表现出两种多态性,当对象获得本类实例时,为编译时多态性,否则为运行时多态性
Person person = new Person();
编译类型(父类引用) 运行类型(子类对象)
多态就是 父类的引用 指向 子类的对象
//披萨类
class PizzaFactory{
public void produce(Pizza pizza) { //定义生产方法,参数为Pizza类对象
pizza.showMsg();
}
}
abstract class Pizza{
String name;
double price;
int size; // 尺寸大小
public Pizza(String name, double price, int size) {
super();
this.name = name;
this.price = price;
this.size = size;
}
public abstract void showMsg();
}
class BaconPizza extends Pizza{
public BaconPizza(String name, double price, int size) {
super(name, price, size);
}
@Override
public void showMsg() {
System.out.println("商品名;"+name + " 价格:"+price+ "尺寸大小"+size);
}
}
class SeaPizza extends Pizza{
public SeaPizza(String name, double price, int size) {
super(name, price, size);
}
@Override
public void showMsg() {
System.out.println("商品名;"+name + " 价格:"+price+ "尺寸大小"+size);
}
}
public class Demo02 {
public static void main(String[] args) {
PizzaFactory pizzaFactory = new PizzaFactory();//实例化工厂对象
Pizza pizza1 = new BaconPizza("培根披萨", 28.6, 14);//多态 (向上转型)
pizzaFactory.produce(pizza1);
Pizza pizza2 = new SeaPizza("海鲜披萨", 38.7, 9);//多态 (向上转型)
pizzaFactory.produce(pizza2);
}
}
四、多态的转型问题
4.1多态的向上转型
实际上就是创建一个子类对象,将其当成父类对象来使用
本质就是:父类的引用指向子类对象
语法格式 :
父类 父类引用 = new 子类();
将子类的对象赋值给了父类的引用。
小范围转为大范围
父类的引用可以调用父类的所用成员方法,可以调用子类重写父类的方法但是不能调用子类自己独有的方法
//多态的向上转型
abstract class People{
public abstract void eat();
}
class Student extends People{
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("大口吃");
}
public void sleep() {
System.out.println("要睡就好好睡");
}
}
public class Demo03 {
public static void main(String[] args) {
People people = new Student(); // 向上转型 父类的引用指向子类的对象
people.eat();
//people.sleep(); // error
//父类的引用可以调用父类的所有成员方法,
//可以调用子类的重写父类的方法,但是不能调用子类独有的方法。
}
}
4.2多态的向下转型
将一个子类对象经过向上转型之后当成父类对象使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型。
语法格式:
父类类型 父类引用 = new 子类(); // 向上转型
子类类型 子类引用 = (子类类型)父类的引用; // 向下转型
粗浅认知:
lass TestA {
//Object 是所有类的父类
public void test (Object obj ) {
int i1 = (int)obj;
System.out.println(i1);
}
}
public class Demo4 {
public static void main(String[] args) {
TestA testA = new TestA();
testA.test(98);//在传参时 就已经完成了 向上转型 Object obj = new Integer(98);
//obj <--98 -->Object obj = (new Objdect()) ==> int(98) ==> obj(98)
//int i1 = (int)obj; 向下转型 obj(98) ==> int(98) //可以调用子类独有的方法
}
}
4.3 转型注意
向上转型:
将子类对象当作父类使用时不需要任何显式地声明,需要注意的是,此时不能通过父类变量去调用子类中的某些方法。
这种不需要显式声明的类型转换叫自动类型转换。
向下转型:
将父类对象当作子类使用时需要显示声明,可以调用子类独有的方法,
这种需要显式声明的类型转换叫强制类型转换。
!!!
但是需要注意的是,需要先将子类对象向上转型为父类对象,才能确保该父类对象强制转型为此子类对象时不出问题!!
五、 instanceof关键字
比较操作符,返回值是布尔类型的数据
instanceof 是 Java 的保留关键字。它的左边是对象的引用,右边是类,它的作用是测试它左边的对象的引用映射的对象是否是它右边的类的实例,返回 boolean 的数据类型,当对象是右边类或子类所创建对象时,返回true;否则,返回false。
类的实例包含本身的实例,以及所有直接或间接子类的实例
instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误
左边的对象引用实例不能是基础数据类型
null用instanceof跟任何类型比较时都是false
语法格式:
对象引用 instanceof 运行类型
目的:
为了在强制转换的时候不出现问题
示例1:
class AA {}
class BB extends AA {}
public class Demo1 {
public static void main(String[] args) {
BB bb = new BB();
BB bb1 = new BB();
AA aa = new AA();
//instanceof 的左边放的对象的引用,右边是类
System.out.println(bb instanceof BB);//true
System.out.println(bb1 instanceof BB);//true
//判断对象bb是否是AA的对象或者子类的对象
System.out.println(bb instanceof AA);//true
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//false
//总结: 左边 的辈分小(对象的引用) 右边辈分大(类) 屡试不爽
AA aa2 = new BB();
System.out.println(aa2 instanceof AA);//true
System.out.println(aa2 instanceof BB);//true
//
Object object = new Object();
System.out.println(object instanceof AA);//false
System.out.println(object instanceof Object);//true
String string = "qwer";
System.out.println(string instanceof Object);//true
}
}
示例2:
class Employee {}
class Monkey {
//这个方法的返回值是一个字符串
public String retStr(Object obj) {
//obj instanceof String
if (obj instanceof String) {
String string = (String)obj;
return "传给我的 参数是字符串类型的数据";
} else if (obj instanceof Integer) {
Integer i1 = (Integer)obj;
return "传给我的 参数是int类型的数据";
} else if (obj instanceof Employee) {
Employee employee = (Employee)obj;
return "传给我的参数是一个Employee对象";
}
else {
return "参数不合法";
}
}
}
public class Demo3 {
public static void main(String[] args) {
Monkey monkey = new Monkey();
System.out.println(monkey.retStr("台湾是中国的"));
//传给我的 参数是字符串类型的数据
System.out.println(monkey.retStr(89));
//传给我的 参数是int类型的数据
Employee employee = new Employee();
System.out.println(monkey.retStr(employee));
//传给我的参数是一个Employee对象
}
}
总结:
关于多态的学习大概就是上述的这些,一开始也有些迷惑,不过在不断的实践中发现多态的用途是真的广,更能理解多态的优点了。
一句话:同一个父类可以有多种子类对象的 表现形式