[面向对象]多态

多态

同一个引用类型,使用不同的实例而执行不同操作,即父类引用指向子类对象

1.多态的必要条件

  1. 一定要有继承关系
  2. 一定要有重写
  3. 父类引用指向子类对象

2.访问多态成员的特点

  • 成员变量:编译时期看左边类型,运行时期看左边类型。(多态运用于行为,不是属性,所以和变量无关)

  • 成员方法:编译时期看左边类型,运行时期看右边类型。(虽然运行时期看右边类型,但因为子类方法通过父类方法重写的,所以父类方法不能删除【一定要有重写】)

  • 静态成员:编译时期看左边类型,运行时期看左边类型

            //static是不能继承的
            //所以Fu和Zi的method()是两个同名的方法,并不是重写
    
            public class DuoTaiDemo02 {
                public static void main(String[] args) {
                    Fu fu = new Zi();
                    fu.methed();
                    //The static method methed() from the type Fu should be accessed in a static way
                    //系统也不建议用对象调用静态方法,应该用类直接调用
                }
            }
    
            class Fu {
                public Fu() {}
                public static void methed() {
                    System.out.println("Fu staticMethod");
                }
            }
    
            class Zi extends Fu {
                public Zi() {}	
                public static void methed() {
                    System.out.println("Zi staticMethod");
                }
            }
    
  • 构造方法:先执行父类构造方法,加载父类静态成员,在执行子类构造方法

    • 可以加载父类的静态成员方法区
    • 帮助子类初始化从父类继承下来的成员

注意

私有方法 静态方法 final方法不能被继承,所以不能重写,不能构成多态,所以都是编译时期看左,运行时期看左,只有成员方法运行时期是看右的

3.多态的好处

  1. 简化代码
  2. 提高了代码维护性
  3. 提高了代码的扩展性

4.多态的缺点

多态的缺点:利用多态[父类引用]无法访问子类所有的方法

        public class DuoTaiDemo04 {
            public static void main(String[] args) {
                Car c = new Bmw();

                c.run();
                c.showBrand();
                //The method showBrand() is undefined for the type Car
                //父类Car没办法识别方法showBrand()
                //因为父类没有这方法,遵循  2.访问多态成员的特点
                //成员方法:编译时期看左边类型,运行时期看右边类型
                //左边在编译时期并没有知道showBrand()方法,所报错
            }
        }

        class Car {
            public void run() {
                System.out.println("我也不知道怎么跑,想怎么跑就怎么跑!!");
            }
        }

        class Bmw extends Car {
            public void run() {
                System.out.println("嗖嗖搜地跑");
            }
            public void showBrand() {
                System.out.println("别摸我!");
            }
        }

解决方法: 向下转型 + i**nstanceof **关键字

4.1向上转型

        //格式
       		[父类类型] 引用变量名 = new [子类类型]( ) ;
4.1.1向上转型的特点
  1. 子类转化为父类,父类引用指向子类对象,类似于自动进行数据类型转换
  2. 通过父类引用变量调用的方法是子类覆盖或者继承父类的方法(多态的体现
  3. 通过父类引用变量无法调用子类特有的属性和方法(多态的缺点

4.2向下转型

        //格式
        	[子类类型] 引用变量名 = ( [子类类型] ) 父类引用变量名;
4.2.1向下转型的特点
  1. 父类转化为子类,父类引用转为子类对象,类似于强制类型转换

  2. 向下转换的过程中,子类类型与父类创建对象指向的子类类型不同时,会类型转换异常

            Car c = new Bmw();
            Benz benz = (Benz) c;//java.lang.ClassCastException
            //原因是向下转型的子类类型并不是创建对象时,父类引用指向的子类对象
            //即:c 指的是Bmw,而向下转型的子类类型是Benz
    

4.3instanceof 关键字

用于解决向下转型发生的类型转换异常

判断左边对象是否属于右边实例

        //格式
        	[对象] instanceof [];
			//返回值类型:boolean

4.4解决方式的缺陷

instanceof 解决类型转异常是有缺陷的

		if (c instanceof Bmw) {
			Bmw bmw = (Bmw) c;
			// 利用向下转型访问子类所特有的方法和属性
			bmw.showBrand();
		} else if (c instanceof Benz) {
			Benz benz = (Benz) c;
			benz.showSpeed();
		} else if (c instanceof Maslati) {
			Maslati maslati = (Maslati) c;
			maslati.showMoney();
		}
		//每当新建一个类,就需要增加一个else if
        //问题一:每次修改原码都必须进行测试
        //问题而:就算这可修改没什么问题不测试,对于有很多类的情况,就得大量加入else if,这工作量是不合理的

5.多态的表现形式(重载)

        //一个方法参数列表不同,在Test类中使用的时候,就可以根据多态调用同一个方法,不同的参数列表
        interface IPerson {
            void use(Student s);

            void use(Student s, Teacher t);

            void use(Teacher t, Student s);

            void use(Student s, Teacher t, Worker w);
        }

6.多态的应用

多态应用大致有三类:直接使用多态;将父类引用作为形参;将父类引用作为返回值

参数类型可以分为:基本数据类型;引用数据类型:包括(普通类 抽象类 接口)

6.1直接使用多态

        //普通类:
        //当一个形参希望我们传入的是一个普通类时,
        //我们实际上传入的是该类的对象/匿名对象/子类匿名对象[匿名内部类]
        public class Test {
            public static void main(String[] args) {
                //访问学生类中的方法study()
                Student s = new Student;
                s.study();

                //用StudentTest访问method方法
                StudentTest st = new StudentTest();
                Student s = new Student;
                st.method(s);
                //改进1
                StudentTest st = new StudentTest();
                st.method(new Student);
                //改进2
                new StudentTest().method(new Student);

                //子类匿名对象
                Student s = new Student() {
                    @Override
                    public void study() {
                        System.out.println("very good!!!");
                    }
                };
                new StudentTest().method(s);
            }
        }

        class StudentTest {
             //假如method方法参数列表使用null会出现空指针异常
             //所以当一个方法的形参是引用数据类型的时候,建议做非空判断!
            public void method(Student s) {
                if (s != null) {
                    s.study();
                }
            }
           // public void method(Student s) {
           //     s.study
           // }
        }

        class Student{
            public void study() {
                System.out.println("好好学习")
            }
        }

6.2将父类引用作为形参

        //抽象类:
        //当一个形参希望我们传入的是一个抽象类时,
		//我们实际上传入的是该类的子类对象/子类匿名对象 [匿名内部类]
        public class Test {
            public static void main(String[] args) {
               //父类引用作为形参
                new StudentTest().method(new Student);
            }
        }

        class StudentTest {
            //AbsPerson sp = new Student
            public void method(AbsPerson ap) {
              s.study
            }
        }

        //抽象类(父类)
        class AbsPerson {
            public void study();
        }

        //子类
        class Student extends AbsPerson{
            public void study() {
                System.out.println("好好学习")
            }
        }
        //接口
        //当一个形参希望我们传入的是一个接口时,
        //我们实际上传入的是该类的实现类对象/实现类匿名对象
        public class Test {
            public static void main(String[] args) {
               //父类引用作为形参
                new StudentTest().method(new Student);
            }
        }

        class StudentTest {
            //AbsPerson sp = new Student
            public void method(IPerson ip) {
              s.study
            }
        }

        //抽象类(父类)
        class Iperson {
            public void study();
        }

        //子类
        class Student implements{
            public void study() {
                System.out.println("好好学习")
            }
        }

6.3父类引用作为返回值

        //普通类
        //当一个方法的返回值是一个普通的类时,
        //实际上返回的是该类的对象/子类对象,我们可以使用该类的对象接收,或者该类的父类接受
        public class Test {
            public static void main(String[] args) {
                //TeacherDemo().method的返回值类型是Teacher
                Teacher t = new TeacherDemo().method();
                t.teach();

                // 多态
                //Object是所有类的父类
                Object obj = new TeacherDemo().method();
                //多态防止数据类型转换异常,所以需要向下转型 + instanceof 关键字
                if (obj instanceof Teacher) {
                    Teacher t = (Teacher) obj;
                    t.teach();
                 }

                //匿名内部类
                 Teacher t = new Teacher() {
                        @Override
                        public void teach() {
                            System.out.println("my teach");
                        }
                 };

                //当方法的返回值类型是引用类型的时候,可以使用链式调用
                new TeacherDemo().method().teach();
            }	
        }

        class TeacherDemo {
            public Teacher method() {
                // Teacher t = new Teacher();	
                return t;
            }
        }

        class Teacher {
            public void teach() {
                System.out.println("教书");
            }
        }

6.4对多态应用的理解题

        //输出结果为:返回 swimming
        //因为最终return时创建了新的对象,所以之前的都被覆盖了
        public class Test {
            public static void main(String[] args) {
                new SwimmingTest().method(new ISwimming() {

                    @Override
                    public void swimming() {
                        System.out.println("外部 swimming");
                    }
                }).swimming();
            }
        }

        class SwimmingTest {
            public ISwimming method(ISwimming swimming) {
                swimming = new ISwimming() {

                    @Override
                    public void swimming() {
                        System.out.println("内部 swimming");
                    }
                };

                return new ISwimming() {

                    @Override
                    public void swimming() {
                        System.out.println("返回 swimming");
                    }
                };
            }
        }
        interface ISwimming {
            void swimming();
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值