美文网首页
Java编程思想学习笔记(9)

Java编程思想学习笔记(9)

作者: Cool_Pomelo | 来源:发表于2019-12-29 16:42 被阅读0次

    Java编程思想学习笔记(9)

    抽象类与抽象方法

    首先先看例子,之前的一系列乐器的例子,把基类Instrument声明为抽象类。

    UML图:

    1.PNG
    abstract class Instrument {
    
        private int i; // Storage allocated for each
    
        //抽象方法,不能有“{}”
        public abstract void play(Note n);
        public String what() { return "Instrument"; }
    
        public abstract void adjust();
    
    }
    
    
    public class Brass extends Instrument{
    
        public void play(Note n) {
            print("Brass.play() " + n);
        }
        public void adjust() { print("Brass.adjust()"); }
    
    
    }
    
    public class Percussion extends Instrument{
    
        public void play(Note n) {
            print("Percussion.play() " + n);
        }
        public String what() { return "Percussion"; }
        public void adjust() {}
    
    
    }
    
    public class Stringed extends Instrument{
    
        public void play(Note n) {
            print("Stringed.play() " + n);
        }
        public String what() { return "Stringed"; }
        public void adjust() {}
    
    
    }
    
    
    public class Woodwind extends Wind{
    
        public void play(Note n) {
            print("Woodwind.play() " + n);
        }
        public String what() { return "Woodwind"; }
    
    
    }
    
    public class Music4 {
    
        // Doesn’t care about type, so new types
    // added to the system still work right:
        static void tune(Instrument i) {
    // ...
            i.play(Note.MIDDLE_C);
        }
        static void tuneAll(Instrument[] e) {
            for(Instrument i : e)
                tune(i);
        }
        public static void main(String[] args) {
    // Upcasting during addition to the array:
            Instrument[] orchestra = {
                    new Wind(),
                    new Percussion(),
                    new Stringed(),
                    new Brass(),
                    new Woodwind()
            };
            tuneAll(orchestra);
        }
    
    }
    
    
    

    抽象:从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。

    这句话概括了抽象的概念,而在Java中,你可以只给出方法的定义不去实现方法的具体事物,由子类去根据具体需求来具体实现。

    这种只给出方法定义而不具体实现的方法被称为抽象方法,抽象方法是没有方法体的,在代码的表达上就是没有“{}”。

    包含一个或多个抽象方法的类也必须被声明为抽象类。

    抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。

    抽象类不能被实例化,也就是不能使用new关键字来得到一个抽象类的实例,抽象方法必须在子类中被实现。

    抽象类总结规定:

    1. 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

    2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

    4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。

    5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

    接口

    interface关键字使得抽象的概念更加向前迈进了一步,abstract关键字允许人们在类中创建一个或多个没有任何定义的方法---提供了接口部分。但是没有提供任何相应的具体实现,这些实现是由此类的继承者实现的。

    interface关键字产生一个完全抽象的类,根本没有提供任何具体实现,它允许创建者确定方法名,参数列表和返回类型。但是没有任何方法体,接口只是提供了形式,没有提供任何具体实现。

    继续把之前乐器的例子来进行改造:

    UML图:

    2.PNG
    public interface Instrument {
    
        // Compile-time constant:
        int VALUE = 5; // static & final
        // Cannot have method definitions:
        void play(Note n); // Automatically public
        void adjust();
    
    
    }
    
    public class Percussion implements Instrument{
    
        public void play(Note n) {
            print(this + ".play() " + n);
        }
        public String toString() { return "Percussion"; }
        public void adjust() { print(this + ".adjust()"); }
    }
    
    
    
    public class Stringed implements Instrument{
    
        public void play(Note n) {
            print(this + ".play() " + n);
        }
        public String toString() { return "Stringed"; }
        public void adjust() { print(this + ".adjust()"); }
    
    
    }
    
    public class Brass extends Wind{
    
        public String toString() { return "Brass"; }
    
    
    }
    
    
    
    public class Woodwind extends Wind{
    
        public String toString() { return "Woodwind"; }
    
    
    }
    
    public class Music5 {
    
        // Doesn’t care about type, so new types
    // added to the system still work right:
        static void tune(Instrument i) {
    // ...
            i.play(Note.MIDDLE_C);
        }
        static void tuneAll(Instrument[] e) {
            for(Instrument i : e)
                tune(i);
        }
        public static void main(String[] args) {
    // Upcasting during addition to the array:
            Instrument[] orchestra = {
                    new Wind(),
                    new Percussion(),
                    new Stringed(),
                    new Brass(),
                    new Woodwind()
            };
            tuneAll(orchestra);
        }
    }
    

    在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。

    接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。

    与抽象类相比,接口有其自身的一些特性:

    • 接口中只能定义抽象方法,这些方法默认为 public abstract 的,因而在声明方法时可以省略这些修饰符。试图在接口中定义实例变量、非抽象的实例方法及静态方法,都是非法的

    • 接口中没有构造方法,不能被实例化

    • 一个接口不实现另一个接口,但可以继承多个其他接口。接口的多继承特点弥补了类的单继承

    Java中的多重继承

    接口不仅仅是一种更加纯粹的抽象类,它的目标比这更高。因为接口中根本没有任何具体实现,所以没有任何与接口相关的存储,因此也就无法阻止多个接口的组合。在C++中,组合多个类的接口的行为叫做多重继承,但这可能会带来很多副作用,因为每个类都有一个具体实现。在Java中,可以执行一样的行为,但是只有一个类可以有具体实现,所以通过组合多个接口,C++的问题不会在Java中发生。

    例子:

    public interface CanFly {
    
        void fly();
    
    
    }
    
    public interface CanFight {
    
        void fight();
    
    
    }
    
    public interface CanSwim {
    
        void swim();
    
    }
    
    public class ActionCharacter {
    
        public void fight() {}
    
    
    }
    
    public class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{
        public void swim() {}
        public void fly() {}
    
    
    }
    
    
    public class Adventure {
    
        public static void t(CanFight x) { x.fight(); }
        public static void u(CanSwim x) { x.swim(); }
    
        public static void v(CanFly x) { x.fly(); }
        public static void w(ActionCharacter x) { x.fight(); }
        public static void main(String[] args) {
            Hero h = new Hero();
            t(h); // Treat it as a CanFight
            u(h); // Treat it as a CanSwim
            v(h); // Treat it as a CanFly
            w(h); // Treat it as an ActionCharacter
        }
    
    
    
    }
    
    
    
    

    上面例子,Hero组合了具体类ActionCharacter与多个接口,当通过这种方式将一个具体类和多个接口组合到一起时,这个具体类必须放在前面,后面跟着的才是接口。

    细节:

    CanFight接口与ActionCharacter类中的fight()方法的特征签名一样,而且Hero中没有提供fight()的定义。可以扩展接口,但是得到的却是另一个接口。即时Hero中没有显式的提供fight()的定义,但是其定义也随之ActionCharacter而来。

    通过继承来扩展接口

    例子:

    public interface Monster {
    
        void menace();
    
    }
    
    
    public interface DangerousMonster extends Monster{
        void destroy();
    
    
    }
    
    public interface Lethal {
    
        void kill();
    
    }
    
    
    public interface Vampire extends DangerousMonster,Lethal{
        void drinkBlood();
    
    
    }
    
    public class DragonZilla implements DangerousMonster{
    
        public void menace() {}
        public void destroy() {}
    
    
    }
    
    
    public class HorrorShow {
    
        static void u(Monster b) { b.menace(); }
        static void v(DangerousMonster d) {
            d.menace();
            d.destroy();
        }
        static void w(Lethal l) { l.kill(); }
        public static void main(String[] args) {
            DangerousMonster barney = new DragonZilla();
            u(barney);
            v(barney);
            Vampire vlad = new VeryBadVampire();
            u(vlad);
            v(vlad);
            w(vlad);
        }
    
    
    }
    
    
    
    
    

    在Vampire中使用的语法仅仅适用于接口继承,一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口,只需要用逗号将接口名字一一分隔开就行了。

    组合接口时的名字冲突问题

    例子:

    public interface I1 {
        void f();
    }
    public interface I2 {
    
        int f(int i);
    
    }
    
    public interface I3 {
        int f();
    
    
    }
    
    public class C {
    
        public int f() { return 1; }
    
    }
    
    public class C2 implements I1,I2{
    
        public void f() {}
        public int f(int i) { return 1; } // overloaded
    
    }
    
    public class C3 extends C implements I2{
    
        public int f(int i) { return 1; } // overloaded
    
    }
    
    public class C4 extends C implements I3{
    
        // Identical, no problem:
        public int f() { return 1; }
    
    
        // Methods differ only by return type:
    //! class C5 extends C implements I1 {}
    //! interface I4 extends I1, I3 {} ///:~
    }
    
    
    

    注释掉的语句会引起编译器报错,因为覆盖,实现和重载全部搅乱在了一起,而且重载方法仅仅根据返回类型是区分不了的。

    接口中的域

    初始化接口中的域

    在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。

    public interface RandVals {
    
        Random RAND = new Random(47);
        int RANDOM_INT = RAND.nextInt(10);
        long RANDOM_LONG = RAND.nextLong() * 10;
        float RANDOM_FLOAT = RAND.nextLong() * 10;
        double RANDOM_DOUBLE = RAND.nextDouble() * 10;
    
    
    }
    
    
    public class TestRandVals {
    
        public static void main(String[] args) {
            print(RandVals.RANDOM_INT);
            print(RandVals.RANDOM_LONG);
            print(RandVals.RANDOM_FLOAT);
            print(RandVals.RANDOM_DOUBLE);
        }
    
    
    }
    
    
    

    既然域是static的,那么它们就可以在类第一次被加载时初始化。

    嵌套接口

    class A {
        interface B {
            void f();
        }
        public class BImp implements B {
            public void f() {}
        }
        private class BImp2 implements B {
            public void f() {}
        }
        public interface C {
            void f();
        }
        class CImp implements C {
            public void f() {}
        }
        private class CImp2 implements C {
            public void f() {}
        }
    
    //    修饰为private
        private interface D {
            void f();
        }
        private class DImp implements D {
            public void f() {}
        }
        public class DImp2 implements D {
            public void f() {}
        }
    
    
        public D getD() { return new DImp2(); }
    
    
        private D dRef;
    
    
        public void receiveD(D d) {
            dRef = d;
            dRef.f();
        }
    }
    
    
    
    public interface E {
        interface G {
            void f();
        }
        // Redundant "public":
        public interface H {
            void f();
        }
        void g();
    // Cannot be private within an interface:
    //! private interface I {}
    
    }
    
    public class NestingInterfaces {
    
        public class BImp implements A.B {
            public void f() {}
        }
        class CImp implements A.C {
            public void f() {}
        }
        // Cannot implement a private interface except
    // within that interface’s defining class:
    //! class DImp implements A.D {
    //! public void f() {}
    //! }
        class EImp implements E {
            public void g() {}
        }
        class EGImp implements E.G {
            public void f() {}
        }
        class EImp2 implements E {
            public void g() {}
            class EG implements E.G {
                public void f() {}
            }
        }
        public static void main(String[] args) {
            A a = new A();
    // Can’t access A.D:
    //! A.D ad = a.getD();
    // Doesn’t return anything but A.D:
    //! A.DImp2 di2 = a.getD();
    // Cannot access a member of the interface:
    //! a.getD().f();
    // Only another A can do anything with getD():
            A a2 = new A();
            a2.receiveD(a.getD());
        }
    
    }
    
    

    在类中嵌套接口的语法是相当显而易见的。

    作为一种新添加的方式,接口也是被实现为private。

    接口E说明接口彼此之间可以嵌套。但是作用域接口的各种规则,特别是所有的接口元素都必须是为public,在此处都会严格执行。因此嵌套在另一个接口中的接口自动就是public的,而不能声明为private。

    相关文章

      网友评论

          本文标题:Java编程思想学习笔记(9)

          本文链接:https://www.haomeiwen.com/subject/vdoyoctx.html