[读书笔记]Java编程思想第9章之接口

Posted Spring-_-Bear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[读书笔记]Java编程思想第9章之接口相关的知识,希望对你有一定的参考价值。

  1. Java提供了一个叫做抽象方法的机制,这种方法是不完整的;仅有声明而没有方法体。包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。如果从一个抽象类继承,并想创建该导出类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做,那么导出类便也是抽象类,且编译器会强制要求我们用abstract这个关键字来限定这个类。抽象的类不可以创建对象。
  2. abstract关键字允许人们在类中创建一个或多个没有任何定义的方法-提供了接口部分,但是没有提供任何相应的具体实现,这些实现是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本就没有提供任何实现。接口也可以包含域,但是这些域隐式地势static和final的。当要实现一个接口时,在接口中被定义的方法必须被定义是public的。
  3. 向如下示例一般,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。此例中,Processor对象就是一个策略,在main()中可以看到有三种不同类型的策略应用到了String类型的s对象上。
package thinkinginjava.charpenter9;

import java.util.Arrays;

/**
 * @author Spring-_-Bear
 * @version 2021/10/2 10:11
 */
public class Apply {
    public static void process(Processor p, Object o) {
        System.out.println("Using Processor " + p.name());
        System.out.println(p.process(o));
    }

    public static String s = "Disagreement with beliefs is by definition incorrect";

    public static void main(String[] args) {
        process(new Upcase(), s);
        process(new Downcase(), s);
        process(new Splitter(), s);
    }
}

class Processor {
    public String name() {
        return getClass().getSimpleName();
    }

    Object process(Object input) {
        return input;
    }
}

class Upcase extends Processor {
    @Override
    String process(Object input) {
        return ((String) input).toUpperCase();
    }
}

class Downcase extends Processor {
    @Override
    String process(Object input) {
        return ((String) input).toLowerCase();
    }
}

class Splitter extends Processor {
    @Override
    String process(Object input) {
        /**
         * The split() argument divides a String into pieces:
         */
        return Arrays.toString(((String) input).split(" "));
    }
}
  1. 适配器设计模式:适配器的代码将接受你所拥有的接口,并产生你所需要的接口。将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具可复用性。
  2. Java中的多重继承:如果要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口。需要将所有的接口名都置于implements关键字之后,用逗号隔开。
  3. 使用接口的核心原因:为了能够向上转型为多个基类型(以及由此而带来的灵活性)。然而,使用接口的第二个原因确实却是与使用抽象类相同:防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。
  4. 通过继承类扩展接口:通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合多个接口。
package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 10:12
 */
public class Adventure {
    public static void t(CanFight canFight){
        canFight.fight();
    }

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

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

    public static void w(ActionCharacter actionCharacter){
        actionCharacter.fight();
    }

    public static void main(String[] args) {
        Hero hero = new Hero();
        t(hero);
        u(hero);
        v(hero);
        w(hero);
    }
}

interface CanFight {
    void fight();
}

interface CanSwim {
    void swim();
}

interface CanFly {
    void fly();
}

class ActionCharacter {
    public void fight() {
    }
}

class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
    @Override
    public void swim() {

    }

    @Override
    public void fight() {
        super.fight();
    }

    @Override
    public void fly() {

    }
}
  1. 接口中的任何域都自动是static和final的,所以接口就成为了一种很便捷的用来创建常量组的工具。在接口中定义的域不能是"空final",但是可以被非常量表达式初始化。既然域是static的,它们就可以在类第一次被加载时初始化。当然,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
  2. 接口可以嵌套在类或其他接口中。当实现某个接口时,并不需要实现嵌套在其内部的任何接口。
    10.接口与工厂:接口是实现多重继承的途径,而生成遵循某个对象的典型方式就是工厂方法设计模式。这与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,通过这种方式,我们的代码将完全与接口的实现分离。
package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 11:26
 */
public class Factories {
    public static void serviceConsumer(ServiceFactory fact){
        Service service = fact.getService();
        service.method1();
        service.method2();
    }

    public static void main(String[] args) {
        serviceConsumer(new Implementation1Factory());
        serviceConsumer(new Implementation2Factory());
    }
}

interface Service{
    void method1();
    void method2();
}

interface ServiceFactory{
    Service getService();
}

class Implementation1 implements Service{
    Implementation1() {
    }

    @Override
    public void method1() {
        System.out.println("Implementation1.method1()");
    }

    @Override
    public void method2() {
        System.out.println("Implementation2.method2()");
    }
}

class Implementation1Factory implements ServiceFactory{
    @Override
    public Service getService() {
        return new Implementation1();
    }
}

class Implementation2 implements Service{
    Implementation2(){}

    @Override
    public void method1() {
        System.out.println("Implementation2.method1()");
    }

    @Override
    public void method2() {
        System.out.println("Implementation2.method2()");
    }
}

class Implementation2Factory implements ServiceFactory{
    @Override
    public Service getService() {
        return new Implementation2();
    }
}

为什么我们想要添加这种额外级别的间接性呢?一个常见的原因是想要创建框架。

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

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

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

package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 11:47
 */
public class HpPrinter extends Printer {
    private int temp = 88;

    @Override
     void print(){
        System.out.println("The values of temp is " + temp);
    }

    public static void main(String[] args) {
        HpPrinter printer = new HpPrinter();
        printer.print();
    }
}

abstract class Printer {
    Printer() {
        print();
    }
    abstract void print();
}

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

package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 12:38
 */
public class Dog extends Animal {
    void eat() {
        System.out.println("Dog.eat()");
    }

    static void method(Animal animal) {
        ((Dog) animal).eat();
    }

    public static void main(String[] args) {
        Animal dog = new Dog();
        Dog.method(dog);
    }

}

abstract class Animal {
}

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

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

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

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

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

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

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

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

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

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

package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 15:25
 */
public class Person extends Animal implements Behavior {
    @Override
    public void forward() {
        System.out.println("Person.forward()");
    }

    @Override
    public void back() {
        System.out.println("Person.back()");
    }

    @Override
    public void chew() {
        System.out.println("Person.chew()");
    }

    @Override
    public void gulping() {
        System.out.println("Person.gulping()");
    }

    @Override
    public void loud() {
        System.out.println("Person.loud()");
    }

    @Override
    public void low() {
        System.out.println("Person.low()");
    }

    @Override
    public void drink() {
        System.out.println("Person.drink()");
    }

    @Override
    void smell() {
        System.out.println("Person.smell()");
    }

    public static void move(Move move) {
        move.back();
        move.forward();
    }

    public static void eat(Eat eat) {
        eat.chew();
        eat.gulping();
    }

    public static void speak(Speak speak) {
        speak.loud();
        speak.low();
    }

    public static void behavior(Behavior behavior) {
        behavior.drink();
    }

    public static void main(String[] args) {
        Person person = new Person();
        move(person);
        eat(person);
        speak(person);
        behavior(person);
    }
}

interface Move {
    void forward();

    void back();
}

interface Eat {
    void chew();

    void gulping();
}

interface Speak {
    void loud();

    void low();
}

interface Behavior extends Move, Eat, Speak {
    void drink();
}

class Animal {
    Animal() {
        System.out.println("Animal constructor");
    }

    void smell() {
        System.out.println("Animal.smell()");
    }
}

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

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

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

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

package thinkinginjava.charpenter9;

/**
 * @author Spring-_-Bear
 * @version 2021/10/3 15:45
 */
public class FactoryMode {
    public static void cycleConsumer(CycleFactory cycleFactory) {
        Cycle cycle = cycleFactory.getCycle();
        cycle.back();
        cycle.forward();
    }

    public static void main(String[] args) {
        cycleConsumer(new BicycleFactory());
        cycleConsumer(new UnicycleFactory());
        cycleConsumer(new TricycleFactory());
    }
}

interface Cycle {
    void back();

    void forward();
}

interface CycleFactory {
    Cycle getCycle();
}

class Unicycle implements Cycle {
    @Override
    public void back() {
        System.out.println("Unicycle.back()");
    }

    @Override
    public void forward() {
        System.out.println("Unicycle.forward()");
    }
}

class Bicycle implements Cycle {
    @Override
    public void back() {
        System.out.println("Bicycle.back()");
    }

    @Override
    public void forward() {
        System.out.println("Bicycle.back()");
    }
}

class Tricycle implements Cycle {
    @Override
    public void back() {
        System.out.println("Tricycle.back()");
    }

    @Override
    public void forward() {
        以上是关于[读书笔记]Java编程思想第9章之接口的主要内容,如果未能解决你的问题,请参考以下文章

[读书笔记]Java编程思想第4章之控制执行流程

[读书笔记]Java编程思想第6章之访问权限控制

[读书笔记]Java编程思想第3章之操作符

[读书笔记]Java编程思想第7章之复用类

[读书笔记]Java编程思想第5章之初始化与清理

[读书笔记]Java编程思想第2章之一切都是对象