Java多态

目录

前言:

多态的形式

多态使用场景

看一个代码

 多态的定义以及前提

实现多态的前提

多态运行的特点

​编辑 多态的弊端

引用类型转换

向上转型 

向下转型 

instanceof关键字


前言:

众所周知,面向对象有三大特性:封装,继承,多态,仔细观察一下其实是有顺序的出现,我们就会发现,我们最开始讲封装如何做出来一个标准的Javabeen,在进行封装之后,我们发现在现实中有很多的场景是可以通过继承去实现的,而今天我们所要讲的就是多态,并且我们可以在这里补充一点就是多态是出现在继承或者是实现中,而接下来我们先举一个例子,通过下面的这个例子,来理解为什么多态是三大特性之一

多态的形式

父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
//举一个例子
Animal cat = new Cat();
cat.eat();

**多态的前提**:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。 

多态使用场景

如果没有多态,在下图中register方法只能传递学生对象,其他的Teacher和administrator对象是无法传递给register方法方法的,在这种情况下,只能定义三个不同的register方法分别接收学生,老师,管理者。

 

看一个代码

父类:
public class Person {
    private String name;
    private int age;

    空参构造
    带全部参数的构造
    get和set方法

    public void show(){
        System.out.println(name + ", " + age);
    }
}

子类1:
public class Administrator extends Person {
    @Override
    public void show() {
        System.out.println("管理员的信息为:" + getName() + ", " + getAge());
    }
}

子类2:
public class Student extends Person{

    @Override
    public void show() {
        System.out.println("学生的信息为:" + getName() + ", " + getAge());
    }
}

子类3:
public class Teacher extends Person{

    @Override
    public void show() {
        System.out.println("老师的信息为:" + getName() + ", " + getAge());
    }
}

测试类:
public class Test {
    public static void main(String[] args) {
        //创建三个对象,并调用register方法

        Student s = new Student();
        s.setName("张三");
        s.setAge(18);


        Teacher t = new Teacher();
        t.setName("王建国");
        t.setAge(30);

        Administrator admin = new Administrator();
        admin.setName("管理员");
        admin.setAge(35);



        register(s);
        register(t);
        register(admin);


    }



    //这个方法既能接收老师,又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父类
    public static void register(Person p){
        p.show();
    }
}

 多态的定义以及前提

**多态**: 是指同一行为,具有多个不同表现形式。

从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。

换一句话说就是,Cat和Dog都需要去吃,但是Cat吃的是鱼,而Dog吃的是骨头,不一样,而这时候就需要多态的出现,多态顾名思义,多种状态。

但是在这里会有人讲接口与多态的概念搞反,这里就需要我们提示一下,在接口实现的时候,是有一些物种有这个属性,而另一些物种没有那种属性,所以将他们做差之后,将没有这些属性的单独划分成为一个借口,比如猫不会游泳,但是狗会游泳,那么我们就可以定义一个接口去实现猫和狗在游泳不同,换一句话说,多态的情况下是我们都有的方法,但实现犯法的状态不同,而接口是你有我没有,或者我有你没有。

实现多态的前提

1. 有继承或者实现关系

2. 方法的重写【意义体现:不重写,无意义】

3. 父类引用指向子类对象【格式体现】

   > 父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

多态运行的特点

调用成员变量时:编译看左边,运行看左边

调用成员方法时:编译看左边,运行看右边

Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();

在多态调用成员变量的时候我们需要看左侧的父亲有没有这个属性,运行时我们所打印的也是左边父亲的值。在多态进行调用成员方法的时候,先看左侧的父亲有没有这个方法,如果没有这个方法将直接报错,然后在打印时候,我们打印的是右侧子类的的方法,换一句话理解,在左侧的是我们的父亲,右边是我们的孩子,无论是调用成员变量或者是方法我们都是先看左侧的父亲是否拥有,如果是变量我们将直接打印父亲的值,如果是方法,我们将打印右侧子类的值。

public class Father {
    private String name;
    private int age;
    public Father(){}
    public Father(String name,int age){
        this.name=name;
        this.age = age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
    public void show()
    {
        System.out.println("i am father");
    }

}
public class Son extends Father{
    public void show()
    {
        System.out.println("i am son");
    }

}
public class Main {
    public static void main(String[] args) {
        Father son = new Son();
        Father father = new Father();
        father.setAge(66);
        father.setName("chen66");

        son.setAge(11);
        son.setName("chen11");

        System.out.println(son.getAge());
        System.out.println(son.getName());
        son.show();
        father.show();
    }
}

打印结果

 多态的弊端

我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时**多态的写法就无法访问子类独有功能了**。 

class Animal{
    public  void eat(){
        System.out.println("动物吃东西!")
    }
}
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
   
    public void catchMouse() {  
        System.out.println("抓老鼠");  
    }  
}  

class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
}

class Test{
    public static void main(String[] args){
        Animal a = new Cat();
        a.eat();
        a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
    }
}

引用类型转换

**多态的写法就无法访问子类独有功能了。**

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,**不能调用**子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

回顾基本数据类型转换

- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5; 
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14 

​     多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。

向上转型 

*向上转型**:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
  当父类引用指向一个子类对象时,便是向上转型。
  使用格式:

父类类型  变量名 = new 子类类型();
如:Animal a = new Cat(); 

**原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。**所以子类范围小可以直接自动转型给父类类型的变量。 

向下转型 

**向下转型**:父类类型向子类类型向下转换的过程,这个过程是强制的。
  一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

 子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
   Cat c =(Cat) a;  

 为什么要这么转呢,我们的a其实是一个来自于父类的Animal类型,而我们的Cat c是低于父类的类别,所以我们需要通过通过强制转换,将Cat c转换为Cat a

abstract class Animal {  
    abstract void eat();  
}  

class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void catchMouse() {  
        System.out.println("抓老鼠");  
    }  
}  

class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void watchHouse() {  
        System.out.println("看家");  
    }  
}
public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat(); 				// 调用的是 Cat 的 eat

        // 向下转型  
        Cat c = (Cat)a;       
        c.catchMouse(); 		// 调用的是 Cat 的 catchMouse
    }  
}

 打印结果

instanceof关键字

变量名 instanceof 数据类型 
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。 

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        if (a instanceof Cat){
            Cat c = (Cat)a;       
            c.catchMouse();        // 调用的是 Cat 的 catchMouse
        } else if (a instanceof Dog){
            Dog d = (Dog)a;       
            d.watchHouse();       // 调用的是 Dog 的 watchHouse
        }
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值