thinking in java test chapter9接口(1)~(10)

抽象方法是没有方法体的方法,java提供的一种机制,仅有声明而没有方法体。语法:abstract void f();
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。
抽象类无法创建对象,创建抽象类的对象是不安全的。
从抽象类继承产生一个新类,必须为基类中所有抽象方法提供方法定义。如果不这样做,那么意味着新类中还有至少一个抽象方法,根据抽象类的定义,这个新类也是抽象类,该类也必须用abstract声明为抽象类。

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

abstract class Rodent{
    public Rodent(){
        shanghai = 100;
        System.out.println("Rodent");
    }
    private int shanghai;
    public abstract void bite();
    @Override
    public abstract String toString();
}
class Mouse extends Rodent{
    private int sh;
    public Mouse(){
        sh = 1000;
        System.out.println("Mouse");
    }
    @Override
    public void bite() {
        System.out.println("造成伤害" +sh + "点" );
    }
    @Override
    public String toString() {
        return "Mouse";
    }
}

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

public class Test1 {
    public static void main(String[] args) {
        absTest abs = new absTest();//编译器报错:xxx is abstract ,cannot be instantiated;
    }
}
abstract class absTest{}

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

public class Test3 {
    public static void main(String[] args){
        new PrinDrive().print();
    }
}
abstract class PrinTest{
    public PrinTest(){
        print();
    }
    abstract void print();
}
class PrinDrive extends PrinTest{
    private int id = 10;
    @Override
    void print() {
        System.out.println("PrintDrive : " + id);
    }
}

输出:
PrintDrive : 0
PrintDrive : 10
前面第8章已经说过初始化。
在类的初始化时,jvm会先给对象分配一块空间,所有的成员都被暂时赋值为0,对象引用赋值为null,这时int值还是0,然后从基类开始调用构造方法,在基类的构造方法中,调用了print()方法,注意此时调用的print()是导出类的方法,看内存示例图。
这里写图片描述
看似是调用了基类的print()的方法,其实是调用了自身的方法,调用某类的方法是需要该类的实例,但导出类中根本没有基类的实例成员。
在完成基类构造方法的调用之前,导出类的其他成员都不会初始化,此时int值是0,所以最终先输出:PrintDrive : 0
完成基类构造方法调用后,然后进行自身的成员变量的初始化,这时int值被赋为10。然后调用自身的构造方法,在构造方法里调用了重写的抽象方法print(),所以此时再输出:PrintDrive : 10

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

public class Test4 {
    public static void main(String[] args){
        Test4Abs t = new Child();
        Child.g(t);//6,然后调用该静态方法
        //7,运行后输出child created
    }
}
abstract class Test4Abs{//1,创建一个不包含任何方法的抽象类
    abstract void f();//8,为基类方法添加abstract
}
class Child extends Test4Abs{//2,从它那儿到处一个类
    void f(){};//3,添加一个方法
    public Child(){
        System.out.println("child created");
    }
    public static void g(Test4Abs t4){//4,再添加一个静态方法,接受指向基类的引用
        t4 = new Child();//5,向下转型到导出类
    }
}

笔者实在不知道这个练习在将声明,想让读者理解什么,如果你能读懂题目的目的的话,请指导笔者。

abstract的类是至少包含一个抽象方法,而interface是完全的抽象类,它里面的所有方法必须是抽象的,不提供具体实现的。(可以规定参数列表返回值类型等,但不能有方法体)接口提供了形式,没有任何具体实现。
接口也可以包含域,但是这些域隐式的是static和final。

练习(5):在某个包内创建一个接口,内含桑方法,然后在另一个包中实现此接口。
创建接口同创建类相似,点new然后出来这个对话框,选interface即可
这里写图片描述

public interface Excample {
    void fi();//方法是默认public的,如果是包访问权限,那么接口就有很大限制,失去了意义。
    void g(int i);
    boolean h(float f);
}

在另一个包内

public class InterfaceLearn implements Excample {
    @Override
    public void fi() {

    }
    @Override
    public void g(int i) {

    }
    @Override
    public boolean h(float f) {
        return false;
    }
}

练习(6):证明接口内所有的方法都是自动public的。
前面曾说到重写(覆盖)的几个规则,重写就是对父类方法的重新编写,接口的规则其实符合重写,与类的继承的重写相比,只不过接口是必须重写所有方法。
在重写的几个规则里,有一个规定:派生类重写的方法的权限不能低于基类方法的权限。我们可以运用这一规则完成题目。

public class InterfaceLearn implements Excample {
    @Override
    void fi() {//此时该方法的权限为包权限
    }

如代码,当我们把重写自接口的方法fi()的权限改成包权限时,此时,编译器报错:
这里写图片描述
这是重写权限小于父类方法的错误,在最后,明确的指出了,原方法的权限:was “public”.
可见,接口的方法确实是默认public的。

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

interface Rodent{
    //没有构造方法
    int shanghai = 10;//域必须是初始化有值的
    public abstract void bite();//所有方法都是没有方法体的 public abstract 编译器是暗色显示,因为没有写的必要,是默认的
    @Override
    public abstract String toString();
}

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

public class Sandwich extends PortableLunch implements FastFood{
    public Sandwich(){
        System.out.println("Sandwich");
    }
    public static void main(String[] args){
        new Sandwich().cook();
    }
    @Override
    public void cook() {
        Bread b = new Bread();
        Cheese c = new Cheese();
        Lettuce l = new Lettuce();
    }
}
interface FastFood{
    void cook();
}

其他简单代码略,请见书本P158

练习(9):重构Music5.java,将在Wind,Percussion和Stringed中的公共方法移入一抽象类中。
其实就是在接口中添加toString()方法。

interface Instrument{
    int VALUE = 5;//static &final
    void play(Note n);
    void adjust();
    String toString();
}

其它代码见书本P173

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

public class Music5 {
    static void tune(Playable p){
        p.play(Note.FIRST);
    }
    static void tuneAll(Playable[] e){
        for (Playable p :
                e) {
            tune(p);
        }
    }
    public static void main(String[] args){
        Playable[] orchestra = {new Wind(),
                            new Percussion(),new Stringed(),
                            new Brass(),new Woodwing()};
        tuneAll(orchestra);
    }
}
enum Note{
    FIRST,SECCOND,THIRD;
}
interface Instrument{
    int VALUE = 5;//static &final
    void adjust();
    String toString();
}
interface Playable{
    void play(Note n);
}
class Wind implements Instrument,Playable{
    @Override
    public void play(Note n) {
        System.out.println(this + "play " + n);
    }
    @Override
    public void adjust() {
        System.out.println(this + "adjust");
    }
    @Override
    public String toString() {
        return "Wind";
    }
}

可以看到,这个练习也许涉及到了设计模式的知识,暂时无法理解,记住这种做法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值