接口
接口和类的异同
相同点:
- 编译后,每个接口跟类都对应一个独立的 .class 字节码文件
- 字节码文件必须包含在与包名匹配的目录结构中
不同点:
- 接口没有构造方法,不可实例化
- 接口中的所有方法都必须是抽象方法
- 接口不能包含成员变量,除了 static 和 final 变量
- 接口不是被类继承,而是被类实现
- 接口支持多继承
接口中的所有方法会被隐式的指定为 public abstract
(只能是 public abstract,其他修饰符都会报错)。
接口中的变量会被隐式的指定为 public static final
变量(并且只能是 public,用 private 修饰会报编译错误)。
语法
[可见度] interface 接口名称 [extends 其他的接口名名] {
// 声明变量
// 抽象方法
}
示例:
interface I {
double PI=3.14; // public final 可以省略
void printPi(); // public abstract 可以省略
public abstract void printPi2();
}
public class Test implements I{
public static void main(String[] args) {
System.out.println(PI);
PI++; // 接口属性不可修改,否则报错:cannot assign a value to final variable PI
Test t = new Test();
t.printPi();
}
// 要实现接口的方法,只能是 public,否则编译报错
public void printPi() {
System.out.println(PI);
}
// 这里没有实现 printPi2,编译报错
}
多态
类型转换
转换类型
根据数据类型,可以分为两类:
- 基本类型转换:将值从一种类型转换成另一种类型
- 引用类型转换:将引用转换为另一种类型的引用,不改变对象本身的类型。
根据是否自动进行,也分为两类:
- 自动转换:编译器自动执行,例如加号操作符一侧有String类型时,另一侧自动转为String
- 显式(强制)转换:代码中指定要转换到的类型,例如
(int)3.14
、(Manager)emp
。
引用类型转换时,只能被转为:
- 任一个超类或接口(向上转换),转换后可用的接口受限
- 引用指向的对象的类型(唯一可以向下转型的情况)
示例:
Employee emp;
Manager man; // Manager 继承自 Employee
emp = new Manager(); // 自动向上转换,但实际类型不变
man = (Manager)emp; // 向下转换
方法查找
- 类方法查找:静态方法属于整个类,不属于任何对象,所以总是在引用变量声明时所属的类中进行查找
- 实例方法查找:从对象创建时的类开始,依次向上查找
多态
在实现了统一接口或从相同父类派生出来的不同类型的对象中,执行同一个方法时,会有不同的表现。
例如所有 Object 类的对象都可以响应 toString 方法,但是表现不同。
绑定
绑定:将方法调用与方法体结合起来。根据绑定时期的不同,可以分为:
- 早绑定:程序运行之前(编译时)执行绑定
- 晚绑定:程序运行时,基于对象类别,绑定应该执行的方法体,也叫动态绑定
动态绑定示例:
abstract class Shape {
public abstract void draw();
}
class Circle extends Shape {
public void draw() {
System.out.println("Circle draw");
}
}
class Rectangle extends Shape {
public void draw() {
System.out.println("Rectangle draw");
}
}
public class Test {
public static void main(String[] args) {
Shape[] s = new Shape[6];
for (int i = 0; i < s.length; i++) {
s[i] = Math.random()*2 > 1 ? new Circle() : new Rectangle();
}
for (int i = 0; i < s.length; i++) s[i].draw();
}
}
上面的例子,引入了随机数,编译时无法确定对象数组中的每一个对象的具体类型,需要在执行时动态绑定。每次执行的结果都不一样。
多态示例二:
abstract class Vehicle {
private String type;
public String getType(){return this.type;}
}
class Bus extends Vehicle {
public String getType(){return "bus";}
}
class Car extends Vehicle {
public String getType(){return "car";}
}
abstract class Driver {
public abstract void drives(Vehicle v);
}
class MaleDriver extends Driver {
public void drives(Vehicle v) {
System.out.println("a man drives a " + v.getType());
}
}
class FemaleDriver extends Driver {
public void drives(Vehicle v) {
System.out.println("a woman drives a " + v.getType());
}
}
public class Test {
public static void main(String[] args) {
Driver a = new MaleDriver();
Driver b = new FemaleDriver();
Vehicle x = new Bus();
Vehicle y = new Car();
a.drives(x);
b.drives(y);
}
}
多态示例二(二次分发,将请求进行两次分发):
abstract class Vehicle {
private String type;
public abstract void drivedByMale();
public abstract void drivedByFemale();
}
class Bus extends Vehicle {
public void drivedByMale() {
System.out.println("a man drive a bus");
}
public void drivedByFemale() {
System.out.println("a woman drive a bus");
}
}
class Car extends Vehicle {
public void drivedByMale() {
System.out.println("a man drive a car");
}
public void drivedByFemale() {
System.out.println("a woman drive a car");
}
}
abstract class Driver {
public abstract void drives(Vehicle v);
}
class MaleDriver extends Driver {
public void drives(Vehicle v) {
v.drivedByMale();
}
}
class FemaleDriver extends Driver {
public void drives(Vehicle v) {
v.drivedByFemale();
}
}
public class Test {
public static void main(String[] args) {
Driver a = new MaleDriver();
Driver b = new FemaleDriver();
Vehicle x = new Bus();
Vehicle y = new Car();
a.drives(x);
b.drives(y);
}
}