zoukankan      html  css  js  c++  java
  • java-多态-object

    概要图

    一 多态

    1.1 多态的产生

    下面的 红色部分降低了代码的可扩展性

        Dog d = new Dog();
            method(d);
    
            Cat c = new Cat();
            method(c);
        }
        
        //接收Dog,让dog做事。
        public static void method(Dog d)
        {
            d.eat();
        }
        //接收Cat,让cat做事。
        public static void method(Cat c)
        {
            c.eat();
        }

    对其改进 见下面红色部分

    //多态技术的引出。解决什么问题?程序扩展性的问题。
    
    //描述Dog
    class Dog extends Animal 
    {
        public void eat()
        {
            System.out.println("骨头");
        }
        public void lookHome()
        {
            System.out.println("看家");
        }
    }
    
    //描述猫
    class Cat extends Animal 
    {
        public void eat()
        {
            System.out.println("");
        }
        public void catchMouse()
        {
            System.out.println("抓老鼠");
        }
    }
    //进行抽取。将共性的功能抽取到父类Animal中。
    abstract class Animal
    {
        public abstract void eat();
    }
    
    class DuoTaiDemo
    {
        public static void main(String[] args) 
        {
            Dog d = new Dog();
    //        d.eat();
            method(d);
    
            Cat c = new Cat();
            method(c);
        }
        /*
        发现,每多一个动物,都需要为这个动物单独定义一个功能,
        让这个动物的对象去做事。
        这个程序扩展性就很差。
        如何提高这个扩展性呢?
        发现既然是让动作去eat,无论是dog,还是cat,
        eat是它们共性,干脆,将eat进行抽取。抽取到父类Animal中。
    
        Dog是Animal中的一种。
        Dog d = new Dog();
        Animal a = new Dog();
        Cat c = new Cat();
        Animal aa = new Cat();
        */
        //只要建立animal的引用就可以接收所有的dog cat对象进来。让它们去eat。
        //提高了程序的扩展性。
        public static void method(Animal a)
        {
            a.eat();
        }
    
        /*
        //接收Dog,让dog做事。
        public static void method(Dog d)
        {
            d.eat();
        }
        //接收Cat,让cat做事。
        public static void method(Cat c)
        {
            c.eat();
        }
        */
    
    }

    1.2 多态的一些问题

    【体现】
    父类的引用或者接口的引用指向了自己的子类对象。
    Dog d = new Dog();//Dog对象的类型是Dog类型。
    Animal a = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。
    【好处】
    提高了程序的扩展性。
    【弊端】
    通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

    子类是父类的对象,父类不是子类的对象
    【前提】
    1,必须有关系:继承,实现。
    2,通常都有重写操作。
    【子类的特有方法如何调用呢?】
    Animal a = new Dog();//Animal是父类型,new Dog()是子对象。
    但是父类型引用指向子类对象时,这就是让子类对象进行了类型的提升(向上转型)。
    向上转型好处:提高了扩展性,隐藏了子类型。弊端:不能使用子类型的特有方法。
    如果要想使用子类的特有方法,只有子类型可以用。
    可以向下转型,强制转换。
    Animal a = new Dog();
    a.eat();
    Dog d = (Dog)a;//将a转型为Dog类型。向下转型。
    d.lookHome();
    向下转型什么时候用?当需要使用子类型的特有内容时。

    注意:无论向上还是向下转型,最终都是子类对象做着类型的变化

    【向下转型的注意事项】
    Animal a = new Dog();
    //Cat c = (Cat)a;向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。(代码下面红色)

    public class Test {
     
        public static void main(String[] args) 
        {
            Dog d = new Dog();  
            method(d);
            Cat c = new Cat();
            method(c);
        }
        public static void method(Animal a)
        {
            
            a.eat();
            Dog d = (Dog)a;
            d.lookHome();
        }
    
    }
    class Dog extends Animal 
    {
        public void eat()
        {
            System.out.println("骨头");
        }
        public void lookHome()
        {
            System.out.println("看家");
        }
    }
    
    //描述猫
    class Cat extends Animal 
    {
        public void eat()
        {
            System.out.println("");
        }
        public void catchMouse()
        {
            System.out.println("抓老鼠");
        }
    }
    //进行抽取。将共性的功能抽取到父类Animal中。
    abstract class Animal
    {
        public abstract void eat();
    }

    Exception in thread "main" 骨头
    看家

    java.lang.ClassCastException: test.Cat cannot be cast to test.Dog

     


    所以为了避免这个问题,需要在向下转型前,做类型的判断。
    判断类型用的是关键字 instanceof

    if(a instanceof Cat)//a指向的对象的类型是Cat类型。
    {
    //将a转型Cat类型。
    Cat c = (Cat)a;
    c.catchMouse();
    }
    else if(a instanceof Dog)
    {
    Dog d = (Dog)a;
    d.lookHome();
    }

    【转型总结】
    1,什么时候使用向上转型呢?
    提高程序的扩展性,不关系子类型(子类型被隐藏)。
    需要用子类的特有方法吗?不需要,哦了。向上转型。
    2,什么时候使用向下转型呢?
    需要使用子类型的特有方法时。
    但是一定要使用 instanceof 进行类型的判断。避免发生 ClassCastException

    1.3 多态的例子

    第一个例子

    class 毕姥爷
    {
        public void 讲课()
        {
            System.out.println("讲管理");
        }
        public void 钓鱼()
        {
            System.out.println("钓鱼");
        }
    }
    
    class 毕老师 extends 毕姥爷
    {
        public void 讲课()
        {
            System.out.println("Java");
        }
        public void 看电影()
        {
            System.out.println("看电影");
        }
    }
    
    
    class DuoTaiTest 
    {
        public static void main(String[] args) 
        {
            毕姥爷 x = new 毕老师();//多态,向上转型。
            x.讲课();
            x.钓鱼();
    //        x.看电影();//不行。
            //想要使用毕老师的特有方法时,需要向下转型。
            if(x instanceof 毕老师)
            {
                毕老师 y = (毕老师)x;
                y.看电影();
            }
            //自始至终都是子类对象做着类型的变化。
    
    
        }
    }

     注意:无论向上还是向下转型,最终都是子类对象做着类型的变化

    第二个例子


    /*
    阶段一需求:笔记本电脑运行。
    按照面向对象的思想,用代码体现。
    名称提炼法
    笔记本电脑。
    行为:运行。

    class NoteBook
    {
    //运行功能。
    public void run()
    {
    System.out.println("notebook run");
    }
    }

     阶段二需求:想要在笔记本电脑上加上一个手握式鼠标。
    多了个对象:鼠标。
    行为:开启,关闭。

    class Mouse
    {
    public void open()
    {
    System.out.println("mouse open");
    }
    public void close()
    {
    System.out.println("mouse close");
    }
    }

    笔记本怎么用鼠标呢?
    在笔记本中多一个使用鼠标的功能。
    需要修改原来的笔记本类中的内容,添加一个功能。

    这样的做法相当于把笔记本打开,把鼠标焊到里面

    因为他直接在改原代码

    class Mouse
    {
    public void open()
    {
    System.out.println("mouse open");
    }
    public void close()
    {
    System.out.println("mouse close");
    }
    }
    public class Test {
        
        
        
    
        public static void main(String[] args) 
        {
            NoteBook n =new NoteBook();
            n.run();
            n.userMouse(null);
        
        }
        
    
    }
    class NoteBook {
        public void run (){
            System.out.println("notebook run");
        }
        public void userMouse(mouse m){
            if(m!=null){
                
                m.open();
                m.close();
            }
            
        }
    }
    class mouse {
        public void open (){
            System.out.println("mouse open");
        }
        public void close (){
            System.out.println("mouse close");
        }
        
    }

    结果 notebook run


    //问题:如果想要加入一个键盘呢?
    只要描述一个键盘类,并在电脑类中加入一个使用键盘的功能就哦了。
    但是发现从鼠标开始这个问题就已经产生了,一旦需要添加新设备的时候,
    都需要改变电脑的源码。这个扩展性是非常差的。

    设计上该如何改进呢?
    之前的问题在于外围设备的增加和笔记本电脑之间的耦合性过高。
    如何降低外围设备和笔记本电脑的耦合性呢?
    外围设备还不确定,我们不要面对外围具体设备。
    为了让笔记本可以使用这些设备,可以事先定义好一些规则,
    笔记本只要使用这些规则就可以了。
    有了这些规则就可以进行笔记本的功能扩展。
    后期这些外围设备只要符合这些规则就可以被笔记本使用了。

    那么规则在java中该如何体现呢?接口

    //1,描述接口。USB。

    //2,描述笔记本电脑:运行功能,使用USB接口的功能。

    */
    //USB接口定义。
    interface USB
    {
    void open();
    void close();
    }
    
    //描述笔记本电脑。 
    class NoteBook
    {
    public void run()
    {
    System.out.println("notebook run");
    }
    
    //使用usb接口的功能。
    public void useUSB(USB usb)//接口类型的变量。接口类型的变量指向自己的子类对象。
    //USB usb = new Mouse();
    {
    if(usb!=null)
    {
    usb.open();
    usb.close();
    }
    }
    }
    //需要鼠标 。想要被笔记本电脑使用,该鼠标必须符合规则。
    //描述鼠标。
    class Mouse implements USB
    {
    public void open()
    {
    System.out.println("mouse open");
    }
    public void close()
    {
    System.out.println("mouse close");
    }
    }
    
    class KeyBoard implements USB
    {
    public void open()
    {
    System.out.println("KeyBoard open");
    }
    public void close()
    {
    System.out.println("KeyBoard close");
    }
    }
    
    /*
    发现,接口的出现,
    1,扩展了笔记本电脑功能。
    2,定义了规则。
    3,降低了笔记本电脑和外围设备之间的耦合性。
    */
    
    class DuoTaiTest2 
    {
    public static void main(String[] args) 
    {
    NoteBook book = new NoteBook();
    book.run();
    book.useUSB(null);
    book.useUSB(new Mouse());
    book.useUSB(new KeyBoard());
    }
    }

    1.4 多态中成员调用的特点。

    1,成员变量
    当子父类中出现同名的成员变量时。
    多态调用该变量时:
    编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
    运行时期:也是调用引用型变量所属的类中的成员变量。
    简单记:编译和运行都参考等号的左边。
    编译运行看左边。

    2,成员函数。
    编译,参考左边,如果没有,编译失败。
    运行,参考右边的对象所属的类。
    编译看左边,运行看右边。

    可以看作是方法的重写.当然要运行右边了
    对于成员函数是动态绑定到对象上。(动态是指对象不确定)

    3,静态函数。
    编译和运行都参考左边。

    也可以看作是重写.按理说应该是和上面的成员函数是一个结果的,但是静态函数是与对象无关的,

    所以运行的是左边Fu 的method,只和调用者有关系

    静态函数是静态的绑定到类上。(静态 是指在那个类中)

    下面红色部分

    package test;
    
    public class Test {
        
        
        
    
        public static void main(String[] args) 
        {
            Fu f = new Zi();
            System.out.println(f.num);// 3
            f.show();  // zi show run..
            f.method(); // fu static method run
        
        }
        
    
    }
    class Fu
    {
        int num = 3;
        
        void show()
        {
            System.out.println("fu show run");
        }
        static void method()
        {
            System.out.println("fu static method run");
        }
    }
    class Zi extends Fu
    {
        int num = 5;
    
        void show()
        {
            System.out.println("zi show run..");
        }
        static void method()
        {
            System.out.println("zi static method run");
        }
    }

    【结论】
    对于成员变量和静态函数,编译和运行都看左边。 
    对于成员函数,编译看左边,运行看右边。

    二 object

    2.1 object 常用方法

    API(应用程序接口(接口:指的是一些公共的东西))文 档

    /*
    Object类中的常用方法

    Object类是所有类的根类,定义了所有对象都具备的功能。
    API(应用程序接口)文档

    */

    class Person extends Object
    {
    private int age;
    Person(int age)
    {
    this.age = age;
    }

    //判断是否是同龄人。这个方法也是在比较两个person对象是否相等。
    //注意:Person类中是否有比较两个Person对象相等的方法?有的!因为继承Object,它本身就具备着equals方法。
    //既然有,还需要定义compare方法吗?不需要。
    //但是,equals方法判断的是地址,不是我们所需要的内容。
    //咋办?继续使用Object的equals方法,但是建立子类的自己的内容。传说中的重写。
    【记住:以后判断对象是否相同,就需要覆盖equals方法。】

    重写equals 

    注意: 

    1 参数是object obj

    2 要向下转型

    public boolean equals(Object obj)
    {
    
    //建立Person自己的判断相同的依据。判断年龄是否相同。
    //    return this.age == obj.age;//obj所属类型Object,Object中没有定义age,所以编译失败。
    
    //如果调用该方法的对象和传递进来的对象是同一个。就不要转型和判断,直接返回true。效率高一点。
    if(this == obj)
    return true;
    
    //age是Person类型的属性。既然要用到子类型的内容,需要向下转型。
    if(!(obj instanceof Person))
    //    return false;
    throw new ClassCastException("类型是不对的!请改正。");
    Person p = (Person)obj;
    
    return this.age == p.age;
    
    }

     重写toString 

    //覆盖toString方法,建立Person对象自己的字符串表现形式。
    public String toString()
    {
    return "Person[age = "+age+"]";
    }

     三 习题

    1,描述图书:book  
        作者,书名,价格。
        行为:6个set get
        建立book比较相同的依据。只要书名相同就视为同一本书。  
        字符串判断相同:请查api文档。equals方法,String类重写Object类中的equals
        建立book对象的字符串表现形式。 Book[作者:+  书名 +  价格]
    
    
    2,写出结果。如果编译失败,请注明原因。
    
    class Super
    {
        int i=0;
        public Super(String a)
        {
            System.out.println("A");
            i=1;    
        }
        public Super()
        {
            System.out.println("B");
            i+=2;
        }
    }
    class Demo extends Super
    {
        public Demo(String a)
        {//super();
            System.out.println("C");
            i+=5;                
        }
        public static void main(String[] args)
        {
            int i=4;
            Super d=new Demo("A");
            System.out.println(d.i);
        }
    }
    //B C 7
    
    3.
    选择题,写出错误答案错误的原因,用单行注释的方式。
    class Demo
    {
         int show(int a,int b){return 0;}
    }
    下面那些函数可以存在于Demo的子类中。    
    A.public int show(int a,int b){return 0;}//    可以的,重写了。权限大于父类方法权限。
    B.private int show(int a,int b){return 0;}// 不可以的,要重写,但是权限不够。
    C.private int show(int a,long b){return 0;}// 可以。相当于重载。
    D.public short show(int a,int b){return 0;}// 不可以的。调用的不确定性。
    E.static int show(int a,int b){return 0;}//    不可以的,static只能重写静态。
    
    
    4,
    写出程序结果,如果编译失败,请注明原因。
    
    interface A{}
    class B implements A
    {
        public String test()
        {
            return "yes";
        }
    }
    class Demo
    {
        public static A get()
        {
            return new B();
        }
        public static void main(String[] args)
        {
    
    
            A a=get();
            System.out.println(a.test());//编译失败,因为a所属的A接口中没有Test()方法。
    //        A a = new B();相当于这句。
    //        a.test(); 是多态调用不?是!test()是非静态成员函数不?是! 规律:编译看左边,有吗?没有,所以编译失败。


    作者:8亩田
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.

    本文如对您有帮助,还请多帮 【推荐】 下此文。
    如果喜欢我的文章,请关注我的公众号
    如果有疑问,请下面留言

    学而不思则罔 思而不学则殆
  • 相关阅读:
    简单的JAVAWeb选课系统
    公文流转系统001
    第九周动手动脑
    JAVA文件操作
    动手动脑-异常处理
    个人NABCD
    水王(课堂作业)
    软件学习进度表07
    软件工程学习进度表06
    软件工程个人作业05(二维数组求最大子数组的和)
  • 原文地址:https://www.cnblogs.com/liu-wang/p/8214287.html
Copyright © 2011-2022 走看看