• 多态、抽象类和接口


    一、多态

    动态绑定(多态):动态绑定是指在“执行期间”(而非编译期间)判断所引用的实际对象类型,根据其实际的类型调用其相应的方法。所以实际当中找要调用的方法时是动态的去找的,new的是谁就找谁的方法,这就叫动态绑定。动态绑定帮助我们的程序的可扩展性达到了极致。

    多态的存在有三个必要的条件:

    1. 要有继承(两个类之间存在继承关系,子类继承父类)
    2. 要有重写(在子类里面重写从父类继承下来的方法)
    3. 父类引用指向子类对象

      这三个条件一旦满足,当你调用父类里面被重写的方法的时候,实际当中new的是哪个子类对象,就调用子类对象的方法(这个方法是从父类继承下来后重写后的方法)。

    实例:

    package javastudy.summary;
    
    class Animal {
        /**
         * 声明一个私有的成员变量name。
         */
        private String name;
    
        /**
         * 在Animal类自定义的构造方法
         * @param name
         */
        Animal(String name) {
            this.name = name;
        }
    
        /**
         * 在Animal类里面自定义一个方法enjoy
         */
        public void enjoy() {
            System.out.println("动物的叫声……");
        }
    }
    
    /**
     * 子类Cat从父类Animal继承下来,Cat类拥有了Animal类所有的属性和方法。
     * @author gacl
     *
     */
    class Cat extends Animal {
        /**
         * 在子类Cat里面定义自己的私有成员变量
         */
        private String eyesColor;
    
        /**
         * 在子类Cat里面定义Cat类的构造方法
         * @param n
         * @param c
         */
        Cat(String n, String c) {
            /**
             * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。
             * 把子类对象里面的父类对象先造出来。
             */
            super(n);
            eyesColor = c;
        }
    
        /**
         * 子类Cat对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。
         */
        public void enjoy() {
            System.out.println("我养的猫高兴地叫了一声……");
        }
    }
    
    /**
     * 子类Dog从父类Animal继承下来,Dog类拥有了Animal类所有的属性和方法。
     * @author gacl
     *
     */
    class Dog extends Animal {
        /**
         * 在子类Dog里面定义自己的私有成员变量
         */
        private String furColor;
    
        /**
         * 在子类Dog里面定义Dog类的构造方法
         * @param n
         * @param c
         */
        Dog(String n, String c) {
            /**
             * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。
             * 把子类对象里面的父类对象先造出来。
             */
            super(n);
            furColor = c;
        }
    
        /**
         * 子类Dog对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。
         */
        public void enjoy() {
            System.out.println("我养的狗高兴地叫了一声……");
        }
    }
    
    /**
     * 子类Bird从父类Animal继承下来,Bird类拥有Animal类所有的属性和方法
     * @author gacl
     *
     */
    class Bird extends Animal {
        /**
         * 在子类Bird里面定义Bird类的构造方法
         */
        Bird() {
            /**
             * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。
             * 把子类对象里面的父类对象先造出来。
             */
            super("bird");
        }
    
        /**
         * 子类Bird对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。
         */
        public void enjoy() {
            System.out.println("我养的鸟高兴地叫了一声……");
        }
    }
    
    /**
     * 定义一个类Lady(女士)
     * @author gacl
     *
     */
    class Lady {
        /**
         * 定义Lady类的私有成员变量name和pet
         */
        private String name;
        private Animal pet;
    
        /**
         * 在Lady类里面定义自己的构造方法Lady(),
         * 这个构造方法有两个参数,分别为String类型的name和Animal类型的pet,
         * 这里的第二个参数设置成Animal类型可以给我们的程序带来最大的灵活性,
         * 因为作为养宠物来说,可以养猫,养狗,养鸟,只要是你喜欢的都可以养,
         * 因此把它设置为父类对象的引用最为灵活。
         * 因为这个Animal类型的参数是父类对象的引用类型,因此当我们传参数的时候,
         * 可以把这个父类的子类对象传过去,即传Dog、Cat和Bird等都可以。
         * @param name
         * @param pet
         */
        Lady(String name, Animal pet) {
            this.name = name;
            this.pet = pet;
        }
    
        /**
         * 在Lady类里面自定义一个方法myPetEnjoy()
         * 方法体内是让Lady对象养的宠物自己调用自己的enjoy()方法发出自己的叫声。
         */
        public void myPetEnjoy() {
            pet.enjoy();
        }
    }
    
    public class TestPolymoph {
        public static void main(String args[]) {
            /**
             * 在堆内存里面new了一只蓝猫对象出来,这个蓝猫对象里面包含有一个父类对象Animal。
             */
            Cat c = new Cat("Catname", "blue");
            /**
             * 在堆内存里面new了一只黑狗对象出来,这个黑狗对象里面包含有一个父类对象Animal。
             */
            Dog d = new Dog("Dogname", "black");
            /**
             * 在堆内存里面new了一只小鸟对象出来,这个小鸟对象里面包含有一个父类对象Animal。
             */
            Bird b = new Bird();
    
            /**
             * 在堆内存里面new出来3个小姑娘,名字分别是l1,l2,l3。
             * l1养了一只宠物是c(Cat),l2养了一只宠物是d(Dog),l3养了一只宠物是b(Bird)。
             * 注意:调用Lady类的构造方法时,传递过来的c,d,b是当成Animal来传递的,
             * 因此使用c,d,b这三个引用对象只能访问父类Animal里面的enjoy()方法。
             */
            Lady l1 = new Lady("l1", c);
            Lady l2 = new Lady("l2", d);
            Lady l3 = new Lady("l3", b);
            /**
             * 这三个小姑娘都调用myPetEnjoy()方法使自己养的宠物高兴地叫起来。
             */
            l1.myPetEnjoy();
            l2.myPetEnjoy();
            l3.myPetEnjoy();
        }
    }
    View Code

    运行结果:

      

    二、抽象类介绍

     

    完整的测试代码

    package javastudy.summary;
    
    /**
     * 父类Animal
     * 在class的前面加上abstract,即声明成这样:abstract class Animal
     * 这样Animal类就成了一个抽象类了
     */
    abstract class Animal {
    
        public String name;
    
        public Animal(String name) {
            this.name = name;
        }
        
        /**
         * 抽象方法
         * 这里只有方法的定义,没有方法的实现。
         */
        public abstract void enjoy(); 
        
    }
    
    /**
     * 这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),
     * 但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,
     * 那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。
     * @author gacl
     *
     */
    abstract class Cat extends Animal {
    
        /**
         * Cat添加自己独有的属性
         */
        public String eyeColor;
    
        public Cat(String n, String c) {
            super(n);//调用父类Animal的构造方法
            this.eyeColor = c;
        }
    }
    
    /**
     * 子类BlueCat继承抽象类Cat,并且实现了从父类Cat继承下来的抽象方法enjoy
     * @author gacl
     *
     */
    class BlueCat extends Cat {
    
        public BlueCat(String n, String c) {
            super(n, c);
        }
    
        /**
         * 实现了抽象方法enjoy
         */
        @Override
        public void enjoy() {
            System.out.println("蓝猫叫...");
        }
        
    }
    
    /**
     * 子类Dog继承抽象类Animal,并且实现了抽象方法enjoy
     * @author gacl
     *
     */
    class Dog extends Animal {
        /**
         * Dog类添加自己特有的属性
         */
        public String furColor;
    
        public Dog(String n, String c) {
            super(n);//调用父类Animal的构造方法
            this.furColor = c;
        }
    
        @Override
        public void enjoy() {
            System.out.println("狗叫....");
        }
    
    }
    
    public class TestAbstract {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
    
            /**
             * 把Cat类声明成一个抽象类以后,就不能再对Cat类进行实例化了,
             * 因为抽象类是残缺不全的,缺胳膊少腿的,因此抽象类不能被实例化。
             */
            //Cat c = new Cat("Catname","blue");
            Dog d = new Dog("dogname","black");
            d.enjoy();//调用自己实现了的enjoy方法
            
            BlueCat c = new BlueCat("BlueCatname","blue");
            c.enjoy();//调用自己实现了的enjoy方法
        }
    }
    View Code

    三、接口

    JAVA是只支持单继承的,为了实现多继承,可以把其中的类封装成接口,使用接口实现多重继承。

    接口和接口之间可以相互继承,类和类之间可以相互继承,类和接口之间,只能是类来实现接口

     接口的本质——接口是一种特殊的抽象类,这种抽象类里面只包含常量和方法的定义,而没有变量和方法的实现。在这种抽象类里面,所有的方法都是抽象方法,并且这个抽象类的属性(即成员变量)都是声明成“public static final 类型 属性名”这样的,默认也是声明成“public static final”即里面的成员变量都是公共的、静态的,不能改变的。因此在接口里面声明常量的时候,可以写成“public static final 类型 常量名=value()”这样的形式,也可以直接写成“类型 常量名=value()”。在接口里面声明的抽象方法可以不写abstract关键字来标识,因为接口里面所有的方法都是抽象的,因此这个“abstract”关键字默认都是省略掉的。这一点与在抽象类里面声明抽象方法时有所区别,在抽象类里面声明抽象方法是一定要使用“abstract”关键字的,而在接口里面声明抽象方法可以省略掉“abstract.

    实例:

    package javastudy.summary;
    
    /**
     * 把“值钱的东西”这个类定义成一个接口Valuable。在接口里面定义了一个抽象方法getMoney()
     * @author gacl
     *
     */
    interface Valuable {
        public double getMoney();
    }
    
    /**
     * 把“应该受到保护的东西”这个类定义成一个接口Protectable。
     * 在接口里面定义了一个抽象方法beProtected();
     * @author gacl
     *
     */
    interface Protectable {
        public void beProteced();
    }
    
    /**
     * 这里是接口与接口之间的继承,接口A继承了接口Protectable,
     * 因此自然而然地继承了接口Protectable里面的抽象方法beProtected()。
     * 因此某一类去实现接口A时,除了要实现接口A里面定义的抽象方法m()以外,
     * 还要实现接口A从它的父接口继承下来的抽象方法beProtected()。
     * 只有把这两个抽象方法都实现了才算是实现了接口A。
     * @author gacl
     *
     */
    interface A extends Protectable {
        void m();
    }
    
    /**
     * 这里定义了一个抽象类Animal。
     * @author gacl
     *
     */
    abstract class Animal {
        private String name;
        /**
         * 在Animal类里面声明了一个抽象方法enjoy()
         */
        abstract void enjoy();
    }
    
    /**
     * 这里是为了实现了我们原来的语义:
     * “金丝猴是一种动物”同时“他也是一种值钱的东西”同时“他也是应该受到保护的东西”。而定义的一个类GoldenMonKey。
     * 为了实现上面的语义,这里把“值钱的东西”这个类定义成了一个接口Valuable,
     * 把“应该受到保护的东西”这个类也定义成了一个接口Protectable。这样就可以实现多继承了。
     * GoldenMonKey类首先从Animal类继承,然后GoldenMonKey类再去实现Valuable接口和Protectable接口,
     * 这样就可以实现GoldenMonKey类同时从Animal类,Valuable类,Protectable类继承了,即实现了多重继承,
     * 实现了原来的语义。
     * @author gacl
     *
     */
    class GoldenMonKey extends Animal implements Valuable,Protectable {
    
        /**
         * 在GoldenMoKey类里面重写了接口Protectable里面的beProtected()这个抽象方法,
         * 实现了接口Protectable。
         */
        @Override
        public void beProteced() {
            System.out.println("live in the Room");
        }
    
        /**
         * 在GoldenMoKey类里面重写了接口Valuable里面的getMoney()这个抽象方法,实现了接口Valuable。
         */
        @Override
        public double getMoney() {
            return 10000;
        }
    
        /**
         * 这里重写了从抽象类Animal继承下来的抽象方法enjoy()。
         * 实现了这抽象方法,不过这里是空实现,空实现也是一种实现。
         */
        @Override
        void enjoy() {
            
        }
        
        public static void test() {
            /**
             * 实际当中在内存里面我们new的是金丝猴,在金丝猴里面有很多的方法,
             * 但是接口的引用对象v能看到的就只有在接口Valuable里面声明的getMoney()方法,
             * 因此可以使用v.getMoney()来调用方法。而别的方法v都看不到,自然也调用不到了。
             */
            Valuable v = new GoldenMonKey();
            System.out.println(v.getMoney());
            /**
             * 把v强制转换成p,相当于换了一个窗口,通过这个窗口只能看得到接口Protectable里面的beProtected()方法
             */
            Protectable p = (Protectable)v;
            p.beProteced();
        } 
    }
    
    /**
     * 这里让Hen类去实现接口A,接口A又是从接口Protectable继承而来,接口A自己又定义了一个抽象方法m(),
     * 所以此时相当于接口A里面有两个抽象方法:m()和beProtected()。
     * 因此Hen类要去实现接口A,就要重写A里面的两个抽象方法,实现了这两个抽象方法后才算是实现了接口A。
     * @author gacl
     *
     */
    class Hen implements A {
    
        @Override
        public void beProteced() {
            
        }
    
        @Override
        public void m() {
            
        }
        
    }
    
    /**
     * java中定义接口
     */
    public class JavaInterfacesTest {
    
        public static void main(String[] args) {
            GoldenMonKey.test();
        }
    }
    View Code
  • 相关阅读:
    仲兆鹏 160809329 第5次
    仲兆鹏160809329-第四次作业
    160809329 仲兆鹏 3
    张浩飞 160809307 (12)
    160809307 张浩飞
    160809307 张浩飞
    160809307 张浩飞(8)
    160809307 张浩飞(7)
    160809307 张浩飞
    160809307 张浩飞 5
  • 原文地址:https://www.cnblogs.com/huangdabing/p/9231940.html
Copyright © 2020-2023  润新知