第9章-接口


Think in java 读书笔记

pikzas

2019.07.31

第九章 接口

知识点

1.抽象类和抽象方法

1.1.抽象类的设计目的

抽象类的主要功能是为了在实现多态过程中,避免错误的创建了父类实例对象而提供的语法支持。
在标准的多态实践中,父类仅仅是为子类提供了一个模板,子类提供具体实现。父类也是一个普通类的情形下,创建出一个父类的对象是没有意义的。他没有具体的方法实现。

1.2.什么是抽象方法

abstract void f(); 仅有声明而没有方法体的方法。

1.3.什么是抽象类

class 前有abstract修饰的类为抽象类

1.4.抽象类的特点

  • 抽象类不能直接实例化,必须通过子类向上转型得到
  • 如果一个类中含有一个或多个抽象方法,那么该类必须为抽象类
  • 如果一个类是抽象类,其中也可以一个抽象方法也没有(可以有任意多个普通的方法)
  • 抽象方法的修饰符不能是private的,那样禁止override,和抽象类的设计初衷相违背
  • 抽象类中也可定义属性,属性的用法和普通类一致。
  • 继承抽象类的子类中,如果实现了父类所有的抽象方法,那么他可以申明为一个普通类(也可以依旧申明为一个抽象类,甚至还可添加新的抽象方法),也可以声明为抽象类,但是如果尚有抽象方法未完全实现,那么该类必须也是抽象类。

2.接口

接口是抽象方法的一种极端形式,要求所有的方法都是抽象方法

2.1.接口的设计目的

除了为了更方便的实现多态,更为了突破JAVA的单继承限制,实现一种隐式的多继承(内部类),因为接口可以多实现

2.2.什么是接口

用interface关键字而不是class关键字标明的类,其中可以有属性,只能有抽象方法(提供方法名,确定参数列表和返回值),却不给任何实现。

2.3.接口的特点

  • 接口类上的修饰符可以是public和default,和普通类的修饰符一样效果。
  • 接口中的属性默认是public static final的,所以可以直接通过接口点属性拿来用,也不能对属性作出修改。而且不能添加访问修饰符private protected或者是default,会提示编译错误。
  • 接口中的方法默认也是public abstract的,不写修饰符也默认是public abstract的,不能添加private protected default这些修饰符。

3.JAVA中的多继承

JAVA允许一个类单继承自一个实体类或者是抽象类,但是可以多实现多个接口。

public class Actor {
    public void fight(){
        System.out.println("do nothing");
    };
}

public interface CanFight {
    void fight();
}

public interface CanFly {
    void fly();
}

public interface CanSwim {
    void swim();
}

public class ActActor extends Actor implements CanFight,CanFly,CanSwim {
    @Override
    public void fly() {

    }
    @Override
    public void swim() {

    }
}

public class Test {
    public static void a(Actor actor) {
        actor.fight();
    }

    public static void b(CanFight canFight) {
        canFight.fight();
    }

    public static void c(CanFly canFly) {
        canFly.fly();
    }

    public static void d(CanSwim canSwim ) {
        canSwim.swim();
    }

    public static void main(String[] args) {
        ActActor actActor = new ActActor();
        a(actActor);
        b(actActor);
        c(actActor);
        d(actActor);
    }
}

上面的例子中注意fight()方法并没有在ActActor中做实现,这是因为Actor类中已有实现
如果将Actor类中的fight()方法改为非public的,那么ActActor就相当于未对CanFight中的fight()方法做实现,此时编译会出错。
如果Actor类中没有fight()方法,但是Actor继承自Human类,Human类中有fight()方法,那么此时也是可以的。

此处示例也说明了接口的两个用处

  • 可以将子类向上转型为不同的基类,代码更灵活
  • 接口也不能实例化,从编译器就可以防止误用。

4.接口与接口,接口与类之间的关系

类可以实现接口,接口可以继承接口并且可以多继承接口


public interface Monster {
    void menace();
}

/**
 * 接口之间用继承
 */
public interface DangerousMonster extends Monster {
    void destory();
}

public interface Lethal {
    void kill();
}

/**
 * 接口可以多继承接口,只要用逗号隔开就行。
 */
public interface Vampire extends DangerousMonster,Lethal {
    void drinkBlood();
}


public class VeryBadVampire implements Vampire {
    @Override
    public void drinkBlood() {

    }

    @Override
    public void destory() {

    }

    @Override
    public void kill() {

    }

    @Override
    public void menace() {

    }
}

5.组合接口时候可能发生的名字冲突

前面讲过如果某个类的父类和其实现的接口都有同一个方法的时候,这个类可以不用再对该方法做实现。
但是如果多个接口,其中都有同一个方法,导致重载与覆盖同时发生,这时候可能就会发生错误。


public interface InterfaceA {
    void f();
}

public class Father {
    public int f(){}
}

/**
* 如下写法的子类会提示编译错误,因为方法必须override,但是又会与父类的方法冲突(同名,同参数列表,就返回值不同,不是overload,所以会提示编译错误)
*/
public class Son extends Father implements InterfaceA {

}

6.接口中的属性都默认是public static final的(不需要特别写明,默认就是)

接口中的属性如果是基本数据类型,他们都要大写,都是编译器常量,可以拿来替代枚举使用
接口中定义的域是不能为空final的,但是可以被非常量表达式初始化
这些属性不是接口的一部分,他们的值会存储在接口的静态存储区域内

import java.util.Random;

public interface RandVals {
    Random RAND = new Random(47);
    int RANDOM_INT = RAND.nextInt(10);
    long RANDOM_LONG = RAND.nextLong() * 10;
}

7.接口的嵌套

接口可以嵌套在类或者其他接口中

  • 接口嵌套在类内部,除了public,包访问权限之外,还多了private访问权限
package com.pikzas.thinkinjava.chapter09.demo6;
public class OuterClass {
    //1.类内部接口可以是包访问权限
    interface B{
        void f();
    }

    //1.1. 对应的实现类可以是public的
    public class BImpl implements B{
        public void f(){}
    }
    //1.2. 对应的实现类可以是private的
    private class BImpl2 implements B{
        public void f(){}
    }
    //1.3. 对应的实现类可以是默认访问权限的
    class BImpl3 implements B{
        public void f() {

        }
    }

    //2. 类内部接口可以是包访问权限
    public interface C{
        void f();
    }

    //2.1. 对应的实现类可以是public的
    public class CImpl implements C{
        public void f(){}
    }
    //2.2. 对应的实现类可以是private的
    private class CImpl2 implements C{
        public void f(){}
    }
    //2.3. 对应的实现类可以是默认访问权限的
    class CImpl3 implements C{
        public void f() {}
    }

    //2. 类内部接口可以是private的
    private interface D{
        void f();
    }

    //2.1. 对应的实现类可以是public的
    public class DImpl implements D{
        public void f(){}
    }
    //2.2. 对应的实现类可以是private的
    private class DImpl2 implements D{
        public void f(){}
    }
    //2.3. 对应的实现类可以是默认访问权限的
    class DImpl3 implements D{
        public void f() {}
    }

}

package com.pikzas.thinkinjava.chapter09.demo6;

public class TestClass {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.B b1 =  outerClass.new BImpl();
//        OuterClass.B b2 =  outerClass.new BImpl2(); BImpl2是private的,访问不到,编译报错
        OuterClass.B b3 =  outerClass.new BImpl3(); //BImpl3是包访问权限,可以访问到

        OuterClass.C c1 = outerClass.new CImpl();
//        OuterClass.C c2 = outerClass.new CImpl2();  CImpl2是private的,访问不到,编译报错
        OuterClass.C c3 = outerClass.new CImpl3();

//        OuterClass.D D接口是private的 访问不到
    }
}

package com.pikzas.thinkinjava.chapter09;

import com.pikzas.thinkinjava.chapter09.demo6.OuterClass;

public class TestOuter {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
//        OuterClass.B b3 =  outerClass.new BImpl3();  访问不到B接口,因为不和他在同一个包路径下
        OuterClass.C c1 =  outerClass.new CImpl();
//        OuterClass.C c2 =  outerClass.new CImpl2();  // 访问不到CImpl2实现,因为是private的
//        OuterClass.C c3 =  outerClass.new CImpl3();  // 访问不到CImpl3实现,因为不和他在同一个包路径下
//        OuterClass.C c3 =  outerClass.new CImpl3();  // 访问不到CImpl3实现,因为不和他在同一个包路径下
//        OuterClass.D d =  outerClass.new DImpl();    // 访问不到D接口,因为D接口是private的
    }
}


  • 接口嵌套在接口内部,内部的接口默认就是public的,如要标明访问权限,也只能写public
public class TestInterface {

}
class EImp implements E{
    public void g(){}
}
class EGImp implements E.G{
    public void f(){}
}

class EImp2 implements E{
    @Override
    public void g() {}
    class EG implements E.G{
        @Override
        public void f() {}
    }
}

8.接口在工厂方法的应用

public interface Game {
    int play();
}

public class Dice implements Game {
    Random random = new Random(47);
    @Override
    public int play() {
        return (random.nextInt(6)+1);
    }
}

public class CoinFlip implements Game {
    Random random = new Random(47);
    @Override
    public int play() {
        int rand = random.nextInt(10);
        return rand > 5? 1:-1;
    }
}

public interface GameFactory {
    Game getGame();
}

public class DiceFactory implements GameFactory {
    @Override
    public Game getGame() {
        return new Dice();
    }
}

public class CoinFlipFactory implements GameFactory {
    @Override
    public Game getGame() {
        return new CoinFlip();
    }
}

public class StartGame {
    public static void play(GameFactory gameFactory) {
       Game game = gameFactory.getGame();
        System.out.println(game.getClass().getSimpleName() + "---"+game.play());
    }
    public static void main(String[] args) {
        for(int i = 0 ; i < 5 ; i++){
            play(new DiceFactory());
            play(new CoinFlipFactory());
            System.out.println("-----");
        }
    }
}

相关