前面在知识点的穿插中,分别介绍了封装,继承,再加上多态,JAVA的三大特性就全部介绍完了。但是多态作为最后一个介绍的,越往后面的一定是越难的,在博主曾经学习的时候,也是最开始在多态这一块的学习中,开始有点迷糊了。时过境迁,希望这次,能把多态通过自己的描述,来介绍清楚。
多态
在讲多态之前,我们先引入一个泛化的概念,什么是泛化呢?简单来说,泛化就是一个再次提取一个类似的特征。我们之前再讲面向对象的时候,说道,面向对象是对具体事物的抽象化,就像是所有猴子抽取出了猴子类,所有老虎抽取出了老虎类。从生物学上来分,这些类就又有一个共同的特征。就是这些都属于动物,都是动物类。这样就像是把类又抽取出来了一个类,它的对象就是类。而泛化就差不多是这样的含义。我们可以叫老虎叫做老虎,也可以把它叫做动物,都没有错,但是真实的它还是老虎。从老虎到动物,这就是一个向上转型的过程,而这个过程,就是通往多态的入口。
多态描述的就是这样一个形式:事物有多种形态和描述:我们可以说老虎是一只老虎,也可以说老虎是一只动物。同一个对象,有着不同的特征:我们可以看到老虎在吃肉然后说看一只动物在吃肉,然后旁边的人问你,是什么动物再吃肉,然后你回答,是一只老虎。从本质上来说,就是同一个物体有着不同的名称和描述。
我们再来举一个例子,在超市的后勤仓库,一个工人大喊:进货啦!然后一堆销售阿姨过去排着队领“货”,其中这个货这个类,在工人那里就是同样的东西,但是分到超市的阿姨手里,就又是不一样的东西了,可能有的领的牙刷,有的领的毛巾等等。但是总体上他们又都属于货物。所以,同样的货物,在工人那里就是货物,到阿姨这里就是具体的商品。总结来说就是:同一个名字和描述,可以在不同的场景下有不同的真实体现。
继承上面的例子,货物是货物类,毛巾类又是毛巾类,牙刷类又是牙刷类,很明显,这里面有着继承的一层关系。只有继承了之后,我们的“货物”才会有各自在超市阿姨展现出他们真实的状态。所以,继承是多态的前提。而且要有能用父类的名称就可以描述一个子类的对象这种性质。我们平常创建对象是:
Huo h = new Huo();
Maojin mj = new Maojin();
前面的h是一个父类的引用,后面一new,就在内存中开辟了一块空间,标识着,这个对象真实的存在了。所以这种形式创建对象的过程中,前面的h只是一个引用,存放后面真实的对象的地址。后面的才是创建出来的真实的对象。那我们想了,我们可以用货物来描述货物,也可以说毛巾,牙刷啊都是货物。那么我们可不可以,在创建一个父类的引用,后面new的时候,创建一个子类的真实对象,这样的话,就可以说它是货物,但其实他是毛巾或者牙刷。形式就类似:
Huo h = new YaShua();
这样看起来貌似是不规范,说了那么多,我们去代码里验证一下:
所以说,JAVA是支持这种格式的。所以这就是事物的多态性:同一个事物在不同情况下有不同的描述或者真实体现。事物有着多种状态。
JAVA中的多态,分为静态多态和动态多态两种,其实,静态多态我们已经讲过了,它其实就是方法的重载,同样的概念,同一个事物在不同情况下有着不同的描述或者真实体现:同样都是一个功能的方法,我们都可以说它是A方法,但是其实它的作用和真实的功能都各有差异,本质上不同,但是大体又相同。又体现了事物有着多种状态的性质。。。
动态多态就像是上面描述的那样,在类与类之间,有着多态的这种关系和思想。
一个类中最不可缺少的无非就是,成员变量,成员方法,构造方法三部分,我们来看一下这些在多态中有着怎样的关系。
代码示例:
class 简单的多态介绍 {
public static void main(String[] args) {
//向上转型
Man m = new SuperMan();
System.out.println(m.age);
}
}
class Man{
int age = 20;
public void run() {
System.out.println("走路");
}
}
class SuperMan extends Man{
int age = 200; //超人嘛,活到200很正常
public void run() {
System.out.println("走路走得飞快");
}
public void fly() {
System.out.println("超人会飞");
}
}
我们在主方法中查看了一个人类其实是超人的对象的属性。结果会是多少呢:
20
我们看到答案,怎么会是20呢,超人不是活200岁么?这里的特点如下:
这里的特点就是编译和运行的机制,可以先记住。等到下面会在内存中进行解释。我们把父类的应用指向子类的对象称之为绑定,这里的这种绑定称之为静态绑定,是指在编译期间就确定调用的是父类中的成员。对于成员变量,静态变量,静态方法,private修饰的方法,采用的是静态绑定。可以总结为:成员变量,静态变量,静态方法,private修饰的方法都是跟随前面的引用。
代码示例:
class 简单的多态介绍 {
public static void main(String[] args) {
//向上转型
Man m = new SuperMan();
m.run();
}
}
class Man{
int age = 20;
public void run() {
System.out.println("走路");
}
}
class SuperMan extends Man{
int age = 200; //超人嘛,活到200很正常
public void run() {
System.out.println("走路走得飞快");
}
public void fly() {
System.out.println("超人会飞");
}
}
打印结果:
走路走得飞快
这里调用的就又是子类也就是等号后面的创建的真实的对象的方法。
特点如下:
这种绑定就称为动态绑定,是在运行的时候,才知道要调用哪个方法。而成员方法就是这种机制。
可以理解为:成员方法是跟随着=后面的真实的对象的。
介绍了成员变量和成员方法, 而构造方法是不用介绍的,因为构造方法是不参与继承的。没有了继承,也就没有了多态。
上面说了类中的成员在多态中的特点,我们会想,为什么呢。我们下面在内存中进行解释。
在上面的图中,我们可以知道,属性是随着类的创建一同过去了。但是方法还在原来的方法区里。所以在调用成员变量的时候,因为前面是m,而m是什么,m是一个父类的对象,它直接可以去堆内存中找到属于自己的属性,所以可以直接调用出来。而在调用成员方法的时候,我们需要用对象的实体去发出请求,去向他的方法区中的文件申请调用,而这个时候,是堆内存中的对象实体在发生调用,而这个时候,方法区才不会管你最开始的引用是什么,它只认堆内存里面的实体对象是谁,所以这个时候调用的就是对面的对象实体中的方法。
我们把又父类的引用指向子类的对象称之为向上转型,那么怎么把父类的引用重新转化为子类的引用呢。这里就引入了向下转型的概念。
我们在这里再介绍一个关键字:instanceof 它用来判断前面的对象是否为后面所属的类。
示例代码:
class 简单的多态介绍 {
public static void main(String[] args) {
//向上转型
Man m = new SuperMan();
m.run();
//向下转型
if(m instanceof SuperMan) {
SuperMan sm = (SuperMan)m;
System.out.println(sm.age);
sm.fly();
}
}
}
class Man{
int age = 20;
public void run() {
System.out.println("走路");
}
}
class SuperMan extends Man{
int age = 200;
public void run() {
System.out.println("走路走得飞快");
}
public void fly() {
System.out.println("超人会飞");
}
}
输出结果:
走路走得飞快
超人会飞
200
说了这么多的概念,那么多态到底有什么应用呢。我们来介绍两种:
①多态作为形参
public class 多态作为形参 {
public static void main(String[] args) {
Zoo zoo = new Zoo();
Tiger tiger = new Tiger();
zoo.feed(tiger);
Monkey monkey = new Monkey();
zoo.feed(monkey);
Cat cat = new Cat();
zoo.feed(cat);
Dog dog = new Dog();
zoo.feed(dog);
}
}
class Zoo {
public void feed(Animal a){
a.eat();
}
}
class Animal {
public void eat(){
System.out.println("吃");
}
}
class Tiger extends Animal{
public void eat(){
System.out.println("吃肉###");
}
}
class Monkey extends Animal{
public void eat(){
System.out.println("吃桃...");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("啃骨头");
}
}
输出结果:
吃肉###
吃桃...
吃鱼
啃骨头
这段代码中多态的运用在主方法中,我们定义了一个像Dog,Cat这样的类,但是Zoo里的feed方法的参数是Animal类的对象。但是一样能够调用,这里,就是用了多态。
②多态做为返回值类型(简单工厂模式)
代码示例:
import java.util.Scanner;
public class 多态作为返回值 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("你想要什么车:");
String s = sc.next();
CarFactory cf = new CarFactory();
Car c = cf.makeCar(s);//多态在这里体现
c.run();
}
}
class CarFactory {
public Car makeCar(String name){
if("bmw".equals(name)){
return new BMW();
}
if("benz".equals(name)){
return new Benz();
}
if("ferrari".equals(name)){
return new Ferrari();
}
return null;
}
}
class Car {
public void run(){
System.out.println("run");
}
}
class BMW extends Car{
public void run(){
System.out.println("宝马在路上飞驰...");
}
}
class Benz extends Car{
public void run(){
System.out.println("奔驰在路上奔驰###");
}
}
class Ferrari extends Car{
public void run(){
System.out.println("法拉利跑的很快");
}
}
输出结果:
你想要什么车:
bmw
宝马在路上飞驰...
这里的多态在主方法中有注释的那一行可以体现出来。这里是一个简单的工厂模式。在这里我们可以简单的理解为,用汽车工厂来创建了汽车然后让汽车运行了起来。
希望这篇文章能让你对多态有一个简单的了解。