JAVA-多态

目录

1、什么是多态

2、多态存在的三个必要条件

3、多态的类型

4、多态的实现

5、多态执行原理分析

6、如何通过父类引用调用子类独有方法?

7、instanceof关键字

8、多态的作用


1、什么是多态

        字面意思,“多种形态”,同一个行为具有不同的表现形式,同一个接口使用不同的实例而执行不同的操作,同一个操作在不同对象上会产生不同的结果。

2、多态存在的三个必要条件

        继承、重写、父类引用指向子类的对象

3、多态的类型

        ①向上转型(upcasting)的多态:是一种自动类型转换,是将子类型转换为父类型

        ②向下转型(downcasting)的多态:是一种强制类型转换,需要手动的通过强制类型转换符进行实现,将父类型转换成为子类型。

⚠注意,无论是哪种类型的多态,两个类型之间一定要存在继承关系,否则编译阶段无法通过。无继承就无多态。

4、多态的实现

        语法结构:父类名 引用=new 子类名();

5、多态执行原理分析

        因为多态的存在,整个程序会有两种状态 ①编译期的形态 ②运行期的形态,可以称之为多态语法机制。

        当在编译的时候,编译器会把它看作自动类型转换,只认它为父类型,是一种向上转型,能够自动类型转换完好的完成赋值操作,不会报错,这是编译期的形态。

        但是运行的时候,在其底层引用指向的始终是一个子类型的内存单元,所以主体还是子类型的对象,这是一个运行期的形态。

        注意因为以上的原因,这个引用只能访问即父类和子类都有的方法(子类重写过的或者从父类那边继承过来的),不能访问到子类中独有的自己的方法。 如果访问父类中没有的方法,编译器太笨了,它只认父类型,在父类中找不到那个方法会进行报错,即使其底层真正的对象是子类型的。

        例如:people父类:

public class people {
    private String id="130634";
    private String name;

    public String getId() {return id;}
    public void setId(String id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    public void sys(){
        System.out.println("父类独有的方法");
    }
}

        China子类

public class China extends people{
    public China(){
        super();
    }
    public void syso(){
        System.out.println("China子类中独有的方法");
    }
}

        测试类:

public class test {
    int i = 0;
    public static void main(String[] args) {
        people people=new China();
        people.syso();   //此行报错,因为sys()是子类独有的方法,编译器只认为引用是父 
                        //类的引用,在父类中找不到这个方法,就会报错。
    }
}

        看输出结果:

java: 找不到符号
  符号:   方法 syso()
  位置: 类型为people的变量 people

        但是访问方法 sys( )是可以顺利访问的,因为编译器在父类中可以找到这个方法,在运行的时候,底层是子类对象,子类因为继承了父类,所以也继承了父类中的该方法,编译和运行都能找到这个方法,所以可以顺利执行。

        如果找到了那个方法,编译就会通过,这个过程我们称之为静态绑定、编译阶段绑定,只有静态绑定成功后才会有运行阶段。

        上文中提到的,父类引用调用子类中独有的方法就会导致编译器在父类的class文件中找不到那个方法,编译报错,导致静态绑定失败,所以也无法进入运行阶段。

        在运行阶段,运行方法的时候,实际上调用的还是子类型内存单元中的方法,这称为动态的绑定。

public class test {
    int i = 0;
    public static void main(String[] args) {
        people people=new China();
        people.sys();
    }
}

        看一下输出结果:

父类独有的方法

Process finished with exit code 0

6、如何通过父类引用调用子类独有方法?

        通过向下转型实现,将父类的引用强制转型为子类型,就可以正确的实现静态绑定和动态绑定,完整的实现运行。 

        例如依旧是上边的people-China的例子,当people想要调用子类中独有的方法sys()的时候,静态绑定失败,编译器只认识父类型,这时候如果通过强制类型转换,对其进行转型,如下:

public class test {
    int i = 0;
    public static void main(String[] args) {
        people people=new China();
        China china=(China)people;  //将引用进行类型的转换
        china.syso();               //然后再进行方法的调用
    }
}

        这时,再进行代码的执行,可以得到正确的输出结果:很明显成功的调用了China子类中的独有的方法。

China子类中独有的方法

Process finished with exit code 0

        但是在使用的时候要注意强转的对象以及接收对象,例如:

父类:Animal类

子类:Cat类、Dog类 

        有Animal a=new Dog();   //①

        Cat  cat=(Cat)a;                //②

        这种在编译阶段是不会出现问题的,因为①是向上转型,编译正确,而②的强转,赋值符号左右两侧的类型相匹配,不存在什么问题,是向下转型。

        但是在运行阶段会出现问题,因为运行时以上的代码实际上可以理解为:

        Cat cat=(Cat)new Dog();

        将两个不相关的子类之间建立一个关系,这种是不成立的,因为多态的前提是两个类之间存在继承关系。

        这种代码写法的运行结果会报如下错误:类型转换异常,这种异常总是在向下转型的时候会发生这种异常。

Exception in thread "main" java.lang.ClassCastException: China cannot be cast to American
	at test.main(test.java:5)

Process finished with exit code 1

        所以其实不合理的向下转型的使用是存在一定的风险的,并且这种异常只有在强制类型转换的时候会发生。 即使编译的时候不会出现问题,在运行的时候也是会出现问题的,向上的转型不会出现相关的问题。

7、instanceof关键字

        如何解决上述的问题?使用instanceof关键字可以避免以上问题的发生。

        语法结构:引用  instnceof  数据类型名

        运算结果:boolean型

                A引用  instanceof  B类型    

                        true:A引用指向的对象是B类型的对象

                        false:A引用指向的对象不是B类型的对象

        作用:用于在转型的时候,去进行类型的判断,避免出现不合理的向下转型的情况。

        以上面的例子(Animal-Dog-Cat)为例:

        有Animal a=new Dog();   //①

        Cat  cat=(Cat)a;               //②

        当进行②操作的时候,先对引用指向的对象类型进行判断,如果是Dog类型的就定义Dog类型的引用接收强转为Dog类型的引用a,同理Cat,这样就不会导致向下转型时,使类型不匹配。

        如下所示:

if(a instanceof Cat){
    Cat cat=(Cat)a;
}else if(a instanceof Dog){
    Dog dog=(Dog)a;
}

8、多态的作用

        那么多态到底有什么作用?以(people-China-American)例子为例

        人 和美国和中国交朋友

        主体:人、美国、中国

        事件:①人请求交朋友 ②美国和中国答应交朋友

        people类:

public class people {
    private String id="130634";
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //和美国交朋友
    public void makeFriend(American american){
        american.OK();
    }
    //和中国交朋友
    public void makeFriend(China china){
        china.OK();
    }
}

        America:

public class America{
    public void OK(){
        System.out.println("美国人:好的,我们交朋友");
    }
}

        China:

public class China{
    public void OK(){
        System.out.println("中国人:好的,我们交朋友");
    }
}

        测试类:

public class test {
    int i = 0;
    public static void main(String[] args) {
        people people=new people();
        American american=new American();
        China china=new China();

        people.makeFriend(china);
        people.makeFriend(american);
    }
}

         运行结果:

中国人:好的,我们交朋友
美国人:好的,我们交朋友

Process finished with exit code 0

        整体代码的运行并不存在什么问题, 但是如果“人”交朋友的实体数目多了以后,要修改很多代码

        例如,人和巴西交朋友

        首先添加巴西类:

public class Brazil{
    public void OK(){
        System.out.println("巴西人:好的,我们交朋友");
    }
}

        然后在people类中添加方法:

public void makeFriend(Brazil brazil){
    brazil.OK();
}

        测试类中也要添加如下代码:

Brazil brazil=new Brazil();
people.makeFriend(brazil);

        运行结果:

中国人:好的,我们交朋友
美国人:好的,我们交朋友
巴西人:好的,我们交朋友

Process finished with exit code 0

        运行结果不存在问题

        可能觉得代码修改也不是很复杂,但是要知道在实际的项目中,代码量是很大的,而且代码之间功能环环相扣,很有可能更改了代码之后,需要修改的地方有很多,这样代码的升级和修改其实是不方便的,也会更容易导致代码错误,这样便出现了“多态”这个概念。

        如果将America、China、Brazil这些实体类统一的去继承一个父类,在这些实体类对象的地方统一用该父类型的引用去替代,这样在进行代码的添加的时候,代码会更加的简洁方便。

        如下:创建一个父类Country

public class Country {
    public void OK(){
    }
}

         America、China、Brazil三个类均都继承Country类,重写了OK方法

        修改people类中的makeFriend方法

/*
    //和美国交朋友
    public void makeFriend(American american){
        american.OK();
    }
    //和中国交朋友
    public void makeFriend(China china){
        china.OK();
    }
    */

    public void makeFriend(Country country){
        country.OK();
    }

        然后修改测试类:

public class test {
    int i = 0;
    public static void main(String[] args) {
        people people=new people();

        /*American american=new American();
        China china=new China();
        Brazil brazil=new Brazil();

        people.makeFriend(china);
        people.makeFriend(american);
        people.makeFriend(brazil);*/

        Country country=new American();
        people.makeFriend(country);
        country=new China();
        people.makeFriend(country);
        country=new Brazil();
        people.makeFriend(country);
    }
}

        很明显这样代码的修改和升级变得相对简单, people类几乎无需进行改动,测试类中对于参数的传递更加的简便。

        这就是多态作用的体现。

降低程序的耦合度,提高程序的扩展能力,能使用多态尽可能的使用多态,使父类引用指向子类对象,便于代码的处理和修改,便于软件的升级

程序员要面向抽象层级进行编程,而不是面向具体的对象,对于猫和狗来说,抽象的一层就是pet类,用pet类统一接收,利用多态实现不同的效果和输出结果。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值