2.7Java多态

1.对象的多态性:

class 动物
{}

classextends 动物
{}

classextends 动物
{}

猫 x = new 猫();
动物 x = new 猫();    //一个对象,两种形态

猫这类事物即具备了猫的形态,又具备着动物的形态。
这就是对象的多态性。

简单说:就是一个对象对应着不同类型。

2.多态在代码中的体现:
父类或者接口的引用指向其子类的对象。

3.多态的好处:
提高了代码的扩展性,前期定义的代码可以使用后期的内容。(接口也符合这种意义)

4.多态的弊端:用向下转型解决
前期定义的内容不能使用(调用)后期子类的特有功能。

5.多态的前提:

    1.必须有关系,继承,实现。
    2.要有覆盖。

6.向上转型:
作用:
1.提高扩展性
2.限制对子类特有功能的访问。

    Animal a = new Cat();  //实际上进行了自动类型提升,猫对象提升为动物类型,特有功能无法访问,只能按照动物类型进行操作。

7.向下转型:转回来。
作用:为了使用子类的特有功能。

Cat c = (Cat)a; //向下转型是为了使用子类的特有功能。
c.catMouse();

8.关于转型的注意:

对于转型,自始自终都是子类对象在做着类型的变化。

例子:

//误区:假设Animal类可以创建对象。
//  Animal a = new Animal();   
//  Animal a = new Dog();
//  Cat c = (Cat)a;        //类型转换异常ClasscastException,动物对象不能转换成子类对象。狗对象也不能转换成猫对象(不存在继承关系)  

9.多态例子:

abstract class Animal
{
    abstract void eat();
}

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

        void lookHome()
    {
            System.out.println("看家");
        }
}

class Cat extends Animal
{
    void eat()
        {
            System.out.println("吃鱼");
        }

        void catchMouse()
    {
            System.out.println("抓老鼠");
        }
}

class  DuoTaiDemo
{
    public static void main(String[] args) 
    {
//      Cat c = new Cat();     
//      Dog d = new Dog();
//      c.eat();       //扩展性不强

            Animal a = new Cat();  //实际上进行了自动类型提升,猫对象提升为动物类型,特有功能无法访问,只能按照动物类型进行操作。
                                                            //作用就是限制对特有功能的访问。
                                                            //专业称为:向上转型。
            a.eat();            
                                                                //如果还想用具体动物猫的特有功能,可以对该对象进制向下转换。
            Cat c =  (Cat)a;         //向下转型是为了使用子类的特有功能。
            c.catMouse();


//注意:对于转型,自始自终都是子类对象在做着类型的变化。
//误区:假设Animal类可以创建对象。
//          Animal a = new Animal();   
        //  Animal a = new Dog();
//          Cat c = (Cat)a;          //类型转换异常ClasscastException,动物对象不能转换成子类对象。狗对象也不能转换成猫对象(不存在继承关系)  

//      method(c);
//      method(d);
    }

    public static void method(Animal a)    //多态:Animal a = new Cat(); 。避免有不同的对象需要不同的函数
    {
        a.eat();
    //  a.catchMouse();   //多态的弊端:前期定义的内容不能使用(调用)后期子类的特有功能。
    }
}

理解转型举例:

class 毕姥爷
{
    void 讲课()
    {
            System.out.println("管理");
    }

    void 钓鱼()
    {
            System.out.println("钓鱼");
    }
}

class 毕老师 extends 毕姥爷
{
        void 讲课()
        {
                System.out.println("Java");
        }

        void 看电影()
        {
                System.out.println("看电影");
        }
}

class DuoTaiDemo
{
        public static void main(String[] args)
        {
//              毕老师 x = new 毕老师();
//              x.讲课();         //Java
//              x.看电影();

                毕姥爷  x = new 毕老师();   
                x.讲课();       //Java,向上转型,变成父类型
                x.看电影();   //错误,无法访问子类的特有功能
                x.钓鱼();    //钓鱼,继承了毕姥爷的功能,毕老师没有覆盖而已,可以执行。

                毕老师 y = 毕老师(x);
                y.看电影();     //看电影,向下转型,变成本类型
                y.钓鱼();    //钓鱼,继承了毕姥爷的功能,毕老师没有覆盖而已,可以执行。
        }
}

10.向下转型中的类型判断:instanceof, 用于判断对象的具体类型,只能用于引用数据类型判断。
作用:通常在向下转型前用于健壮性的判断。

    public static void method(Animal a)    //多态:Animal a = new Cat(); 。避免有不同的对象需要不同的函数
    {
        a.eat();
    //  a.catchMouse();   //多态的弊端:前期定义的内容不能使用(调用)后期子类的特有功能。

//      Cat c = (Cat)a;
//      c.catchMouse();     //如果传了一只狗,就错了。要进行类型判断。(子类的子孙类也可以接收的)

//类型判断
        if(a instanceof Cat)   //instanceof:用于判断对象的具体类型(类或者接口)。只能用于引用数据类型判断。
                                                            //作用:通常在向下转型前用于健壮性的判断。
                                                                //a instanceof Animal  ,始终真
    {
        Cat c = (Cat)a;
        c.catchMouse(); 
    }
    //挨个判断一般不会出现,太多。

    }

11.多态在 继承当中成员的一些变化:向上转型,子类型被隐藏,子类的特有方法不能使用。
Animal a = new Cat();

1.成员变量:

编译时:参考引用型变量所属的类中是否有调用的成员变量,有,编译通过。没有,编译失败。
运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。

简单说:编译和运行都参考左边。
面试才会出现 父子类成员变量同名。

覆盖只发生在函数中。

2.成员函数:(非静态)

编译时:参考引用型变量所属的类中是否有调用的函数,有,编译通过。没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数,

简单说:编译看左边,运行看右边。
必须通过对象调用,依赖的是对象,看对象是谁。

运行时   有个动态绑定的过程:

多态在继承中成员函数的动态绑定

3.静态函数:

编译时:参考引用型变量所属的类中是否有调用的静态方法。
运行时:参考引用型变量所属的类中是否有调用的静态方法。

简单说:编译和运行都看左边。

其实对于静态方法,是不需要对象的,直接用类名调用。
//Fu f = new Zi();   //对象多余
//f.staticMethod(); 

Fu. staticMethod(); 
Zi. staticMethod(); 

静态函数在方法区中的静态区,不依赖对象。new的对象用不上。
直接依赖引用型变量所在的类。可以说静态函数不存在多态性。

12.内部类:将一个类定义在另一个类里,对里面的那个类称为内部类(内置类,嵌套类)

1)特点:
①内部类可以直接访问外部类中的成员,包括私有成员。
②而外部类要访问内部类中的成员必须要建立内部类的对象。

③可以被成员修饰符修饰:

直接访问外部类中的内部类中的成员:不多见,内部类一般私有化。
Outer.Inner in = new Outer().new Inner();

如果内部类是静态的,相当于一个外部类。
Outer.Inner in = new Outer.Inner(); 

如果内部类是静态的,成员是静态的。(如果内部类中有静态成员,该内部类也必须是静态的)
Outer.Inner.function();

2)作用:访问方便

一般用于类的设计。

分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容。
这时就把还有的事物定义成内部类来描述。

心脏是内部类,身体是外部类。

3)细节:

为什么内部类能直接访问外部内的成员?
因为内部类持有了外部类的引用。外部名.this

①外部类和内部类的成员不同名的时候:
外部名.thisthis省略,默认添加。

②同名的时候,需要手动指明。
1.默认先找方法中的局部成员。
2.如果要找内部类的成员,通过this. 或者内部类.this. 来访问。
3.如果要访问外部类的成员,通过外部类.this.来访问。

4)局部内部类:
内部类可以放在局部位置上。

注意:从内部类中访问局部变量 ,需要被声明成final类型。
原因:内部类在局部位置上只能访问局部中被final修饰的局部变量。因为局部变量的生命周期结束后,就消失了,这时内部类就不能访问了。所以必须加final。

5)匿名内部类:内部类的简写格式。就是一个匿名子类对象。
1.格式:new 父类or接口(){ 子类内容 } (一个有点胖的对象)
(匿名对象:是对象的简写格式。)

2.前提:内部类必须继承或者实现一个外部类或者接口。

abstract class Demo
{
    abstract void show();
}
class Outer
{
    int num = 4;

    /*
    class Inner extends Demo
    {
        void show()
        {
            System.out.println("有继承的内部类");
        }
    }
    */

    public void method()
    {
        //new Inner().show();

    //匿名内部类
        new Demo()    //Demo类子类的对象,在对象中把抽象方法实现。就是一个匿名子类对象。简化封装。有点胖的对象。
        {
            void show()
            {
                System.out.println("匿名内部类");
            }
            void haha()
            {
            }
        }.show();
    }
}


//如果需要调用多个方法,可以给匿名内部类取个名字:
/*
    Demo d = new Demo()
        {
                void show()
            {
                    System.out.println("匿名内部类 + 外部类的num:" + num);
                }
        };
        d.show1();
        d.show2();
*/


class InnerClassDemo 
{
    public static void main(String[] args) 
    {
        new Outer().method();
    }
}

3.匿名内部类使用场景:
当函数参数是接口类型时,而且接口中的方法不超过三个。否者对象太胖了。

可以用匿名内部类作为实际参数进行传递。

class InnerClassDemo 
{
    public static void main(String[] args) 
    {
        method(new Inter()      //直接用匿名内部类作为实际参数进行传递。就是一个子类的匿名对象。
        {
        public void show1(){};
        public void show2(){};
        public void show3(){};
        })
    }
    public static void method(Inter in)   //发生向上转型,子类对象特有的show3()无法调用。
    {
        in.show1();
        in.show2();
    }
}

4.内部类的细节:
①当内部类在主类中时:
主函数不能访问非静态的内部类成员,因为不能存在this,所以不能通过外部类.来调用。
在主类的其他方法中可以访问非静态的内部类,可以用this.new Inner();,其实this.表示外部类,默认添加,可以省略。
内部类在主类中的情况

②匿名内部类是子类的对象,可以直接访问子类的成员。

如果给它取了名字,就相当于进行了向上转型,只能调用接口或者父类中存在的成员,子类中的特有成员被隐藏。

13.对象的初始化过程:
构造函数先进栈,最后出栈。
中间的初始化顺序:父类构造函数,显示初始化,构造代码块。

Person p = new Person();

1.JVM读取指定路径下的person.class文件,并加载进内存,并会先加载Person的父类(如果有直接父类的情况下)。
2.在堆内存中开辟空间,分配内存地址
3.并在对象空间中,对对象中的属性进行默认初始化。
4.调用对应的构造函数进行初始化。

5.在构造函数中,第一行先调用父类中构造函数进行初始化。
6.父类初始化完毕后,再对子类的属性进行显示初始化。

7.在执行 构造代码块 进行初始化。

8.然后继续完成子类构造函数的特定初始化。

9.初始化完毕后,将地址值赋值给引用变量。

图解:
对象的初始化过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值