Java cheatsheet (IV)

4.1. 关于多态

  1. 派生类不可重写或者覆盖 final 的方法;
  2. private 方法不可以 Override,但是可以重载;
  3. 类的成员字段不会被多态,事实上基类与派生类同名的字段会各自保存副本;
  4. 静态方法不具有多态性;
  5. Override 的方法的返回值可以返回一个基类方法返回类型的派生类;

4.2. 向下转型与运行时类型识别(RTTI Run Time Type Identify)

class Base {
}

class Derived extends Base {
}

public class Program {
    public static void main(String[] args) {
        Base base = new Derived();
        // Base base = new Base();  // 这种情况向下转换会触发运行时错误
        try {
            Derived derived = (Derived)base;
        } catch(ClassCastException e) {
            System.out.println(e);
        }
    }
}

4.3. 抽象方法与抽象类

  1. 通过 abstract 修饰的类方法是抽象方法,只有声明没有函数体;
  2. 包含有一个或以上抽象方法的类,必须想通过 abstract 来限定其为抽象类;
  3. 抽象类不能实例化;
  4. 在派生类中必须 Override 基类的抽象方法,除非派生类也是抽象的;
abstract class AbstractClass {
    public abstract void method();
}

4.4. 接口

  1. 接口使用 interface 关键字代替 class 关键字,使用 implements 代替 extends 关键字;
  2. 接口的所有方法都必须且自动为 abstract 的,不能定义函数体;
  3. 接口中所有的方法都默认是 public 的;
  4. 接口中的所有字段(成员变量)都是 static 和 final 的。
import java.util.*;

interface ICanExport {
    String export();
}

class Invoice implements ICanExport {

    String content;

    public Invoice(String content) {
        this.content = content;
    }

    @Override
    public String export() {
        return content;
    }

}

public class Program {
    public static void main(String[] args) {
        ICanExport invoice = new Invoice("This is an invoice");
        System.out.println(invoice.export());
    }
}

4.5. 适配器模式

例如上面这个例子,Invoice 类是现成由别人开发好的库,具有 export 方法,但是本身并没有实现 ICanExport 的接口。

这个时候我们需要写一个 ICanExport 的接口,并且让 Invoice 去实现它,这时候会用到一个“适配器模式”,ICanExport 的定义以及 Invoice 的定义都不需修改,而我们另行定义一个 InvoiceAdapter 类来实现 ICanExport 接口,并用组合(而不是继承)存放一个 Invoice 的实例,并且手动编写所有 ICanExport 的接口,如下:

import java.util.*;

interface ICanExport {
    String export();
}

class Invoice {

    String content;

    public Invoice(String content) {
        this.content = content;
    }

    public String export() {
        return content;
    }

}

class InvoiceAdapter implements ICanExport {

    Invoice invoice;

    public InvoiceAdapter(Invoice invoice) {
        this.invoice = invoice;
    }

    @Override
    public String export() {
        return invoice.export();
    }

}

public class Program {
    public static void main(String[] args) {
        Invoice invoice = new Invoice("This is an invoice");
        ICanExport adapter = new InvoiceAdapter(invoice);
        System.out.println(adapter.export());
    }
}

4.6. 接口的继承

可以通过 extends 语法将接口进行派生。

4.7. 接口与继承的规范

一般而言,一个类通过 extends 只继承于一个单一的类,但是可以通过 implements 实现多个不同的接口,用逗号分隔开。

4.8. 接口名称冲突

如果一个类实现了不同的接口,而不巧这些接口上面有着名称相同,签名或者返回值不同的时候,会引起混乱,尽量不要这么搞。

4.9. 适配 Scanner 输入

对于前面提到过的 Scanner 类,其构造函数接受一个实现了 Readable 接口的类。

这个 Readable 接口需要实现一个 int read(CharBuffer cb) 方法,将字符串附加到 cb 字符缓冲区中,然后返回添加的字符长度。

然后 Scanner 就会根据这个接口实现的方法,读入字符流并进行处理(可以简单认为反复调用 read 直到 返回 -1,得到一个完整的字符串流,然后 Scanner 利用这个字符串流进行读取扫描)。

import java.util.*;

class RandomDoubles {
    private static Random rand = new Random(47);
    public double next() { return rand.nextDouble(); }
}

public class AdaptedDoubles extends RandomDoubles implements Readable {
    private int count;
    public AdaptedDoubles(int count) {
        this.count = count;
    }
    public int read(CharBuffer cb) {
        if(count-- == 0) return -1;
        String result = Double.toString(next()) + " ";
        cb.append(result);
        return result.length();
    }
}

public class Program {
    public static void main(String[] args) {
        Scanner s = new Scanner(new AdaptedRandomDoubles(7));
        while(s.hasNextDouble()) {
            System.out.print(s.nextDouble() + " ");
        }
    }
}

4.10. 利用接口创建常量组(不推荐)

注意,Java 中具有常量初始化值的 static final 成员会用大写字母加下划线分隔的方式来命名。

因此,可以用这种方式来定义常量组,模拟 C/C++ 的枚举类型。

注意接口的字段不能为空,因此必须初始化,但是可以不为常量。

由于是 static,初始化会在具备这个接口第一次加载的时候被初始化。

package interfaces;

public interface Months {
    int JUANUARY = 1, FEBURARY = 2, MARCH = 3,
        APRIL = 4, MAY = 5, JUNE = 6,
        JULY = 7, AUGUST = 8, SEPTEMBER = 9,
        OCTOBER = 10, NOVEMBER = 11, DECEMBER = 12;
}

4.11. 嵌套接口

可以在类或者接口的内部定义接口,并且可以定义为默认(包权限)、public 或者 private。

其访问以及可见性都与类的成员一样(访问通过成员运算符级联,可见性参考包权限、public 和 private)。

4.12. 接口和工厂

有一种模式是这样的,设定一个接口,然后定义一个工厂,这个工厂总是返回实现了这个接口的对象。

这种模式耦合比较少,但是,切勿滥用。


【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2015/02/java-cheatsheet-4.html【Java cheatsheet (IV)】

发表评论

电子邮件地址不会被公开。 必填项已用*标注