Dive into Java -- (4) Object-Oriented

面向对象

面向对象的特征有三方面:

  • Encapsulation (封装)
  • Inheritence (继承)
  • Polymorphism (多态)

继承

Java 只支持单继承,但可以实现多个接口

abstract class VS interface

abstract class interface
设计目的 在代码实现方面发挥作用,可以实现代码的重用 在系统框架设计方法发挥作用,主要定义模块之间的通信
初始化块 可以 不可以
构造方法 可以
但是不能用于创建对象,而是让子类调用
不可以
方法 没有限制,可以同时包含抽象和非抽象的方法 只能并且默认是 public abstract 方法
属性 静态常量属性和普通属性 只能并且默认是 public static final 属性
继承/实现 一个类是只能继承一个抽象类 一个类可以可以实现多个接口
一个接口不能实现另一个接口,但它可以继承多个其他接口。
方法实现 重写父类的抽象方法或者将子类定义为抽象类 必须要实现接口声明的所有方法

示例:

1
2
3
4
interface A {}
interface B {}

interface C extends A, B {} // 接口可以继承多个接口, A 和 B 都是接口

多态

Java 中实现多态的机制是:

  1. Override(覆盖)
  2. Overload(重载)

对比:

Override(覆盖) Overloade(重载)
Scope 父类与子类之间多态性的一种表现 一个类中多态性的一种表现
基本概念 子类中定义的方法与其父类中的方法有相同的名称、参数列表、返回值类型 一个类中具有相同方法名的方法的参数列表在顺序、个数、类型上存在不同

(与方法的返回值类型无关)

Override (覆盖)

  1. 方法的参数签名和返回值类型必须相同,访问控制修饰符可以不同,但是子类方法不能缩小父类方法的访问权限。
  2. 子类方法抛出的异常必须和父类方法抛出的异常相同,或者是父类方法抛出的异常类的子类。
  3. 父类的静态方法是不能被子类覆盖为非静态方法。父类的非静态方法不能被子类覆盖为静态方法。
  4. 子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。区别:运行时,JVM 把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。
  5. 父类的私有方法不能被覆盖
  6. 父类的非抽象方法可以被覆盖为抽象方法

JVM 实现

原则:“编译看左边,运行看右边”,即:在编译时,看引用类型的类是否有此方法;在运行时,看对象所在的类是否有调用的方法。

1) 对于一个引用类型的变量,Java编译器按照它声明的类型来处理。

1
2
Base base = new Sub();
base.subMethod();

编译出错,编译器认为 base 是 Base 类型的引用变量,Base 类没有 subMethod() 方法。必须使用强制转换。

1
(Sub)(base).subMethod();

2) 对于一个引用类型的变量,运行时 JVM 按照它实际引用的对象来处理。

1
2
Base base = new Base();
Sub sub = (Sub)base;

编译通过,但是运行时抛出 ClassCastException。在运行时,子类的引用类型变量可以转换为父类的引用类型,而相反的过程却不可以。

3) 在运行时环境中,通过引用类型变量来访问所引用对象的方法和属性时,JVM 采用以下的绑定规则。

  1. 实例方法与引用变量实际引用的对象的方法绑定,即动态绑定。
  2. 静态方法与引用变量所声明的类型的方法绑定,即静态绑定。 编译阶段即绑定完成。
  3. 成员变量(静态变量和实例变量) 与引用类型所声明的类型的成员变量绑定。静态绑定。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Base {
public String a = "Base";

public static void hello() {
System.out.println("Base.hello");
}

public void say() {
this.hello();
System.out.println("Base.x = " + this.a);
}
}

public class Sub extends Base {
public String a = "Sub";

public static void hello() {
System.out.println("Sub.hello");
}

public void say() {
this.hello();
System.out.println("Sub.x = " + this.a);
}
}

public class Test {
public static void main(String[] args) {
Sub sub = new Sub();

Base base = (Base)sub;

// 覆盖测试:普通方法
sub.say();
base.say();

// 覆盖测试:静态方法
sub.hello();
base.hello();

// 覆盖测试:属性
System.out.println(sub.a);
System.out.println(base.a);
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
Sub.hello
Sub.x = Sub

Sub.hello // 动态绑定中调用的静态方法,也变成动态绑定
Sub.x = Sub // 普通方法,动态绑定

Sub.hello
Base.hello // 静态方法,静态绑定

Sub
Base // 成员变量,静态绑定

Overload(重载)

如果在一个类中定义了多个重载的方法,只需根据所传参数列表的不同来调用不同的重载方法。