7.2 继承语法
//: reusing/Detergent.java
// Inheritance syntax & properties.
import static net.mindview.util.Print.*;
class Cleanser {
private String s = "Cleanser";
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public String toString() { return s; }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
print(x);
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
// Add methods to the interface:
public void foam() { append(" foam()"); }
// Test the new class:
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
print(x);
print("Testing base class:");
Cleanser.main(args);
}
} /* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Testing base class:
Cleanser dilute() apply() scrub()
*///:~
cleaner和Detergent均含有main方法(),可以为每一个类都创建一个main()方法的技术可以使每个类的单元测试变得简单,单元测试后,也无需删除main(),可以将其留待下次测试。
即使一个程序中含有多个类,也只有命令行所调用的那个类的main()方法被调用,在此例中,可以看到Detergent.main()明确调用Cleaner.main().并将命令行获取的参数传递给了他,当然也可以向其传递任意的String数组。
7.2.1 初始化基类
当创建了一个导出类的对象时,该对象包含了一个基类的对象,这个基类对象和用基类直接创造对象是一样的,区别在于后者来自于外部,而导出类的对象被包装在导出类的内部。
//: reusing/Cartoon.java
// Constructor calls during inheritance.
import static net.mindview.util.Print.*;
class Art {
Art() { print("Art constructor"); }
}
class Drawing extends Art {
Drawing() { print("Drawing constructor"); }
}
public class Cartoon extends Drawing {
public Cartoon() { print("Cartoon constructor"); }
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} /* Output:
Art constructor
Drawing constructor
Cartoon constructor
*///:~
如果没有默认的基类构造器,或者想要调用一个基类的构造器,就必须用关键字super显式的调用基类的构造器的语句,并且配以对应的参数,否则会报错。
//: reusing/Chess.java
// Inheritance, constructors and arguments.
import static net.mindview.util.Print.*;
class Game {
Game(int i) {
print("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
print("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
print("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~
7.4.2 名称屏蔽
如果java的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法,不会导致基类的该方法被屏蔽
//: reusing/Hide.java
// Overloading a base-class method name in a derived
// class does not hide the base-class versions.
import static net.mindview.util.Print.*;
class Homer {
char doh(char c) {
print("doh(char)");
return 'd';
}
float doh(float f) {
print("doh(float)");
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());
}
} /* Output:
doh(float)
doh(char)
doh(float)
doh(Milhouse)
*///:~
7.7.2 再论组合与继承
到底该用组合还是继承,一个最清晰的判断方法就是问一问自己是否需要从新类向基类向上转型。如果必须向上转型,则继承是必要的,如果不需要,则要自选考虑自己是否需要继承。
7.8 final关键字
定义为static强调只有一封, 定义为final强调为常量。
一个既是static又是final的域只占据一段不能改变的存储区域。
对于基本类型来使用final使数值恒定不变,而对对象引用,final使其引用恒定不变,一旦引用被指向一个对象,就不能把它再指向另一个对象,对象本身是可以被改变。
空白final
必须在域的定义处或者在每个构造器用表达式fianl进行赋值,这正是final域在使用前总是被赋值的原因。
final参数
java允许在参数列表中以声明的方式将参数指明为final,这意味着你无法在方法中更改参数引用所指向的对象
//: reusing/FinalArguments.java
// Using "final" with method arguments.
class Gizmo {
public void spin() {}
}
public class FinalArguments {
void with(final Gizmo g) {
//! g = new Gizmo(); // Illegal -- g is final
}
void without(Gizmo g) {
g = new Gizmo(); // OK -- g not final
g.spin();
}
// void f(final int i) { i++; } // Can't change
// You can only read from a final primitive:
int g(final int i) { return i + 1; }
public static void main(String[] args) {
FinalArguments bf = new FinalArguments();
bf.without(null);
bf.with(null);
}
} ///:~
final方法
将方法锁定,以防止任何继承类修改他的含义。类中所有的private方法都隐式的被指定为final的,由于无法取用private方法,所以也就无法覆盖他,可以对private方法添加final,但这没有添加任何含义。
private与final
如果某方法为private,如果在子类中生成一个相同名称的public或protected方法,此时就不是覆盖方法,而是生成了一个新的方法
7.8.3 final类
当你将某个类定义为fina了时,就表明了你不打算继承该类,而且也不允许别人这么做。