Thinking in java 第9章 接口 笔记+习题

本文详细探讨了Java中的接口,包括接口与抽象类的区别、完全解耦的概念、多继承的实现、接口的扩展以及接口在实际编程中的应用,如Scanner的适配、工厂方法等。通过对《Thinking in java》第9章的学习,理解接口在软件设计中的重要作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Thinking in java 第9章 接口

学习目录


9.1 抽象类和抽象方法

1. 抽象类是普通类与接口之间的一种中庸之道。

2. 有抽象方法的类一定要是抽象类,抽象类可以有有非抽象方法。抽象方法只需声明方法体。

3. 抽象类和抽象方法的意义:

  • 可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。
  • 重要的重构工具,使我们可以很容易地将公共方法沿着继承层次结构向上移动。

 

9.2 接口

1. 接口允许人们通过创建一个能够被向上转型为多种基类的类型,来实现类似多重继承的特性。

2. 接口隐式是static和final的。

 

9.3 完全解耦

1. 如果某个继承结构以外的类想要用里面的方法,就会很麻烦,而接口可以在很大程度上放宽这种限制,使得可复用性更好。

 

9.4 Java中的多继承

1,.可以继承任意多个接口,并可以向上转型为每个接口,因为每一个接口都是一个独立的类型。

2. 使用接口的核心原因:为了能够向上转型为多个基类型(以及由此而带来的灵活性)。另一方面可以防止客户端程序员创建该类的对象。

3. 如果要创建不带任何方法定义和成员变量的基类,那么久应该选择接口而不是抽象类。

 

9.5 通过继承来扩展接口

1. 使用extends来扩展接口。

2. 基类和接口的方法名相同时,当只有返回类型不同时会报错。

 

9.6 适配接口

1. Scanner类的构造器接受的就是一个Readable接口,若要让Scanner可以作用于它,就让它成为Readable,并重写public int read(CharBuffer cb)方法。在read内部,将输入内容添加到CharBuffer参数中,或者在没有任何输入时返回-1。(例P182-183)。

 

9.7 接口中的域

1. 放入接口中的任何域都是staticfinal的(方法是public的),在Java SE5之前会由此来创建常量组工具,之后被enum替代。

 

9.8 嵌套接口

1. 接口可以嵌套在类或其他接口之中(应用格式为例:implements A.B) 

2. 类中可以嵌套private类的接口,可以强制该接口中的方法定义不要添加任何类型信息(不允许向上转型),且其不能在定义它的类之外被实现。

3. 在接口中嵌套时,所有的接口元素必须是public的,不能声明为private。

 

9.9 接口与工厂

1. 理论上通过工厂方法设计模式,代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。(例P187)。工厂类用工厂接口返回一个实现接口,实现类用实现接口实现具体操作。


练习:

练习1:修改第8章练习9中的Rodent,使其成为一个抽象类。只要有可能们就将Rodent的方法声明为抽象方法。

略。(因为没去写过Rodent... 总之统一加个abstract即可)

练习2:创建一个不包含任何抽象方法的抽象类,并验证我们不能为该类创建任何实例。

略。

练习3:创建一个基类,让它包含抽象方法print(),并在导出类中覆盖该方法。覆盖后的方法版本可以打印导出类中定义的某个整型变量的值。在定义该变量之处,赋予它非零值。在基类的构造器中调用这个方法。现在,在main()方法中,创建一个导出类对象,然后调用它的print()方法。请解释发生的情形。

package Chapter9;

public class E3 {
    public static void main(String[] args) {
        E3B e = new E3B();
        e.print();
    }
}

abstract class E3A{
    abstract void print();

    E3A() {
        System.out.print("E3A ");
        print();
    }
}

class E3B extends E3A {

    private int a = 1;

    @Override
    void print() {
        System.out.println(a);
    }

    E3B() {
        System.out.print("E3B ");
        print();
    }
}


/*
E3A 0
E3B 1
1
*/

抽象类中可以调用抽象方法,因为调用时一定是有导出类实现,故直接用导出类重写的方法。且最开始未初始化完全时为默认值。

练习4:创建一个不包含任何方法的抽象类,从它那里导出一个类,并添加一个方法。创建一个静态方法,它可以接受指向基类的引用,将其向下转型到导出类,然后再调用该静态方法。在main()中,展现它的运行情况。然后,为基类中的方法加上abstract声明,这样就不再需要进行向下转型。

package Chapter9;

public class E4 {
    public static void main(String[] args) {
        func(new E4B());
    }

    public static void func(E4A e) {
        ((E4B)e).write();
    }
}

abstract class E4A {

}

class E4B extends E4A {
    void write() {
        System.out.println("this is E4A");
    }
}

/*
this is E4A
*/

练习5:在某个包内创建一个接口,内含三个方法,然后在另一个包中实现此接口。

略。

练习6:证明接口内所有方法都自动是public的。

略。

 练习7:修改第8章中的练习9,使Rodent成为一个接口。

略。同练习1。 

练习8:在polymorphism.Sandwich.java中,创建接口FastFood并添加加合适的方法,然后修改Sandwich以实现FastFood接口。

略。相当于接口实现练习。

练习9:重构Music5.java,将在Wind,Precussion和Stringed中的公共方法移入一个抽象类中。

略。 同上。

练习10:修改Music5.java,添加Playable接口。将play()的声明从Instrument中移动Playable中。通过将Playable包括在implements列表中,把Playable添加到导出类中。修改tune()使它接受Playable而不是Instrument作为参数。

略。同上。

练习11:创建一个类,它有一个方法用于接受一个String类型的参数,生成的结果是将该参数中每一对字符进行互换。对该类进行适配,使得它可以用于interfaceprocessor.Apply.process()。 

package Chapter9;

public class E11 {
    public static void main(String[] args) {
        Apply.process(new StringProcessor(), new String("abc"));
    }
}

interface Processor {
    String name();
    Object process(Object input);
}

class Apply {
    public static void process(Processor p, Object s) {
        System.out.println("Using Processor" + p.name());
        System.out.println(p.process(s));
    }
}

class StringProcessor implements Processor {

    @Override
    public String name() {
        return getClass().getSimpleName();
    }

    @Override
    public String process(Object input) {
        return new StringBuffer((String)input).reverse().toString();
    }
}

/*
cba
*/

 练习12:在Adventure.java中,按照其他接口的样式,增加一个CanClimb接口。

略。照猫画虎。

练习13:创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。

package Chapter9;

public class E13 {
    public static void main(String[] args) {
        E13D e = new E13D();
        e.func();
    }
}

interface E13A {
    void func();
}

interface E13B1 extends E13A {
    void func();
}

interface E13B2 extends E13A {
    void func();
}

interface E13C extends E13B1, E13B2 {
    void func();
}

class E13D implements E13C {

    @Override
    public void func() {
        System.out.println("E13D.func()");
    }
}

/*
E13D.func()
*/

练习14:创建三个接口,每个接口都包含两个方法。继承出一个接口,它组合了这三个接口并添加了一个新方法。创建一个实现了该新接口并继承了某个具体类的类。现在编写四个方法,每一个方法接受这四个接口之一作为参数。在main()方法中,创建这个类的对象,并将其传递给这四个方法。

package Chapter9;

public class E14 {
    public static void main(String[] args) {
        func1(new E14F());
        func2(new E14F());
        func3(new E14F());
        func4(new E14F());
        func5(new E14F());

    }

    public static void func1(E14A e) { e.funcA(); }
    public static void func2(E14B e) { e.funcB(); }
    public static void func3(E14C e) { e.funcC(); }
    public static void func4(E14D e) { e.funcD(); }
    public static void func5(E14E e) { e.funcE(); }
}

interface E14A {void funcA();}

interface E14B {void funcB();}

interface E14C {void funcC();}

interface E14D extends E14A, E14B, E14C {void funcD();}

class E14E {
    void funcE() {System.out.println("E14E.funcE()");}
}

class E14F extends E14E implements E14D {

    @Override
    public void funcA() {
        System.out.println("E14F.funcA()");
    }

    @Override
    public void funcB() {
        System.out.println("E14F.funcB()");
    }

    @Override
    public void funcC() {
        System.out.println("E14F.funcC()");
    }

    @Override
    public void funcD() {
        System.out.println("E14F.funcD()");
    }
}

/*
E14F.funcA()
E14F.funcB()
E14F.funcC()
E14F.funcD()
E14E.funcE()
*/

练习15:将前一个练习修改为:创建一个抽象类,并将其继承到一个导出类中。

略。

练习16:创建一个类,它将生成一个char序列,适配这个类,使其可以成为Scanner对象的一种输入。

略。即实现Readable接口中的read()方法,和P182相同。

练习17:证明在接口中的域隐式地是static和final的。

略。

练习18:创建一个Cycle接口及其Unicycle、Bicycle和Tricycle实现。对每种类型的Cycle都创建相应的工厂,然后编写代码使用这些工厂。

 略。工厂参考练习19。

练习19:使用工厂方法来创建一个框架,它可以执行抛硬币和掷骰子功能

package Chapter9;

import java.util.Random;

public class E19 {
    public static void main(String[] args) {
        playGame(new CoinFactory(), 3);
        playGame(new DiceFactory(), 6);
    }

    public static void playGame(GameFactory factory, int num) {
        Game game = factory.getGame(num);
        game.play();
    }
}

interface Game { void play(); }
interface GameFactory { Game getGame(int num); }

class CoinFactory implements GameFactory {

    @Override
    public Game getGame(int num) {
        return new Coin(num);
    }
}

class DiceFactory implements GameFactory {

    @Override
    public Game getGame(int num) {
        return new Dice(num);
    }
}

class Coin implements Game {

    private int num;

    Coin(int num) {
        this.num = num;
    }

    @Override
    public void play() {
        Random r = new Random();
        int cnt = 1;
        while(this.num > 0) {
            System.out.println("第"+ cnt++ +"次投硬币结果为"+(r.nextInt(2)==1?"正":"反"));
            this.num--;
        }
    }
}

class Dice implements Game {

    private int num;

    Dice(int num) {
        this.num = num;
    }

    @Override
    public void play() {
        Random r = new Random();
        int cnt = 1;
        while(this.num > 0) {
            System.out.println("第"+ cnt++ +"次投色子结果为"+(r.nextInt(6)+1));
            this.num--;
        }
    }
}

/*
第1次投硬币结果为反
第2次投硬币结果为反
第3次投硬币结果为反
第1次投色子结果为2
第2次投色子结果为2
第3次投色子结果为2
第4次投色子结果为4
第5次投色子结果为6
第6次投色子结果为4
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值