7.1 组合语法
将对象引用置于新类
class WaterSource {
private String s;
WaterSource() { }
public String toString() { return s; }
}
public class SprinklerSystem {
private WaterSource ws = new WaterSource();
}
- toString() 方法,当打印一个对象的时候,调用 toString() 方法
- 对象初始化的4种方法
- 定义的地方初始化
- 构造器中初始化
- 用到的时候才初始化,惰性初始化
- 使用实例初始化
7.2 继承语法
- 数据成员都写成 private , 继承的方法都写成 public 的
class Cleanser {
private String s = "Cleanser";
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public String toString() { return s; }
}
public class Detergent extends Cleanser() {
public void apply() {
append(" Detergent.scrub()");
super.scrub();
}
public void foam() {}
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
}
}
- 创建一个导出类对象的时候,该对象包含一个基类的子对象,它和基类直接创建的对象是一样的,二者区别在于,基类对象来自于外部,而这个子对象被包装在导出类的内部
- Java 自动在导出类的构造器中调用基类的构造器来执行基类子对象的初始化
- 先进行基类的初始化,再进行导出类的初始化
带参数的构造器
如果是带参数的构造器,就必须用关键字 super 显式地编写调用基类构造器的语句
class Game {
Game(int i) {}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i); //如果不在 Board 中调用构造器,编译器会抱怨找不到 Game() 形式的构造器
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
}
public static void main(String[] args) {
Chess x = new Chess();
}
}
7.3 代理
将一个成员对象置于所要构造的类中(像组合),但与此同时我们在新类中暴露了该对象的所有方法(像继承)
public class Space {
void up() {}
void down() {}
}
public class SpaceShipDelegation {
private String name;
private Space space = new Space(); //代理 Space 类
public void up() {
space.up();
}
public void down() {
space.down();
}
}
//形状
class Shape {
Shape(int i) { System.out.println("Shape Constructor"); }
void dispose() { System.out.println("Shape dispose"); }
}
//圆
class Circle extends Shape {
Circle(int i) {
super(i);
System.out.println("Drawing Circle");
}
void dispose() {
System.out.println("Erasing Circle");
}
}
//三角
class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Drawing Triangle");
}
void dispose() {
System.out.println("Erasing Triangle");
}
}
//线
class Line extends Shape {
private int start,end;
Line(int start,int end) {
super(start);
this.start = start;
this.end = end;
System.out.println("Drawing Line: " + start + " , " + end);
}
void dispose() {
System.out.println("Erasing Line: " + start + " , " + end);
}
}
//绘图
public class CADSystem extends Shape {
private Circle c;
private Triangle t;
private Line[] lines = new Line[3];
public CADSystem(int i) {
super(i + 1);
for(int j = 0;j < lines.length;j++)
lines[j] = new Line(j, j*j);
c = new Circle(1);
t = new Triangle(1);
System.out.println("Combined constructor");
}
public void dispose() {
System.out.println("CADSystem.dispose");
t.dispose();
c.dispose();
for(int i = lines.length-1;i>=0;i--)
lines[i].dispose();
super.dispose();
}
public static void main(String[] args) {
CADSystem x = new CADSystem(47);
try {
} finally {
x.dispose();
}
}
}/*
Shape Constructor //CADSystem
Shape Constructor //lines[0]
Drawing Line: 0 , 0
Shape Constructor //lines[1]
Drawing Line: 1 , 1
Shape Constructor //lines[2]
Drawing Line: 2 , 4
Shape Constructor //Circle
Drawing Circle
Shape Constructor //Triangle
Drawing Triangle
Combined constructor
CADSystem.dispose
Erasing Triangle
Erasing Circle
Erasing Line: 2 , 4
Erasing Line: 1 , 1
Erasing Line: 0 , 0
Shape dispose
*/
//构造时,先调用基类构造器,销毁时,顺序反过来,先调用导出类dispose()方法
- 名称屏蔽
在基类中已经拥有某个被重载的方法,在导出类中重新定义该名称的方法,并不会屏蔽其在基类中的任何版本
class Homer {
char doh(char c) {
print(c);
return c;
}
float doh(float f) {
print(f);
return 1.0f;
}
}
class Milhouse {}
class Bart extends Homer {
void doh(Milhouse m) {
print("doh(Milhouse)");
}
}
public class Hide {
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1);
b.doh('x');
b.doh(1.0f);
b.doh(new Milhouse());
}
}
/*
1.0
x
1.0
doh(Milhouse)
*/
7.7 向上转型
- 继承最重要是表现新类和基类的关系,新类是现有类的一种类型
- 能够向基类发出的所有信息也可以向导出类发出
class Instrument {
public void play() {}
static void tune(Instrument i) {
i.play();
}
}
public class Wind extends Instrument {
Wind flute = new Wind();
Instrument.tune(flute); //Wind 类对象也可以调用Instrument 类方法
}
- 如果必须向上转型,继承才是必要的,否则尽量使用组合
7.8 final 关键字
- final 的基本类型,保持值不变
- final 的对象,保持引用不变,对象本身是可以被改变的
public static final int VALUE = 39;
//既是 static 又是 final 的变量用全大写表示,视作编译时常量
//定义为 static ,强调只有一份,定义为 final ,表示是常量
- private 的方法默认是 final 的
- 覆盖只有当方法是接口的一部分时才会出现,必须能将一个对象向上转型成它的基类并调用相同的方法
- final 类,不被继承的类,这个类一旦被创建,你就希望它永远不变,而 final 类中的所有方法都被隐式地指定为是 final 的
7.9 初始化及类的加载
- 每个类的编译代码都存在于它自己的独立文件中,该文件只有在需要使用程序代码时才会被加载,通常发生在创建第一个对象时或者访问 static 成员时
- 执行一个 java 类,先去找它的 main() 方法,创建该类对象时,去找这个类的编译代码,如果这个类有基类,就先去加载基类,以此类推,递归加载,接下来,基类的静态成员初始化,非静态成员初始化,导出类的静态成员初始化,导出类的非静态成员初始化,基类调用构造器,导出类调用构造器,总之
- 先加载基类后加载导出类
- 构造器在所有成员初始化之后
- 先初始化静态成员,后初始化非静态成员
- 基类构造器会被自动调用,也可以用 super() 在导出类构造器中手动调用
- 组合一般是将现有类型作为新类型底层实现的一部分来加以复用
- 继承复用的是接口