多态的作用是消除类型间的耦合。对象可以作为自己本身的类型使用,也可以作为他的基类使用。
练习一:
class Cycle{
void print() {
System.out.println("Cycle");
}
}
class Unicycle extends Cycle{
void print() {
System.out.println("Unicycle");
}
}
class Bicycle extends Cycle{
void print() {
System.out.println("Bicycle");
}
}
class Tricycle extends Cycle{
void print() {
System.out.println("Tricycle");
}
}
public class Test1 {
static void print(Cycle temp) {
temp.print();
}
public static void main(String[] args) {
Unicycle uc=new Unicycle();
Bicycle bc=new Bicycle();
Tricycle tc=new Tricycle();
print(uc);
print(bc);
print(tc);
}
}
这个代码可以运行,需要了解绑定的知识。绑定是将一个方法调用同一个方法主体关联起来,分为前期绑定和后期绑定。c语言只有前期绑定。
这个程序得益于后期绑定,根据运行时对象的类型进行绑定。java中除了static和final方法,其他方法都是后期绑定的。
练习二:
import java.util.Random;
class Shape{
void draw() {}
void erase() {}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Circle.draw()");
}
@Override
void erase() {
System.out.println("Circle.erase()");
}
}
class Square extends Shape{
@Override
void draw() {
System.out.println("Square.draw()");
}
@Override
void erase() {
System.out.println("Square.draw()");
}
}
class Triangle extends Shape{
@Override
void draw() {
System.out.println("Triangle.draw()");
}
@Override
void erase() {
System.out.println("Triangle.erase()");
}
}
class RandomShapeGenerator{
private Random rand =new Random(42);
Shape next() {
switch(rand.nextInt(3)) {
default:
case 0:return new Square();
case 1:return new Circle();
case 2:return new Triangle();
}
}
}
public class Test2 {
private static RandomShapeGenerator gen=new RandomShapeGenerator();
public static void main(String[] args) {
Shape[] s=new Shape[9];
for(int i=0;i<s.length;i++) {
s[i]=gen.next();
}
for(Shape i:s) {
i.draw();
i.erase();
}
}
}
练习九:
import java.util.Random;
class Rodent{
void voice() {}
void eat() {}
}
class Mouse extends Rodent {
@Override
void voice() {
System.out.println("Zhi");
}
@Override
void eat(){
System.out.println("everything");
}
}
class Gerbil extends Rodent{
@Override
void voice() {
System.out.println("perhaps Zhi");
}
@Override
void eat() {
System.out.println("perhaps everything");
}
}
class Hamster extends Rodent{
@Override
void voice() {
System.out.println("I don't know");
}
@Override
void eat() {
System.out.println("I don't know, maybe everything");
}
}
class RandomRodentGenerator{
private Random rand =new Random(42);
Rodent next() {
switch(rand.nextInt(3)) {
default:
case 0: return new Mouse();
case 1: return new Gerbil();
case 2: return new Hamster();
}
}
}
public class Test9 {
private static RandomRodentGenerator gen=new RandomRodentGenerator();
public static void main(String[] args) {
Rodent[] array=new Rodent[9];
for(int i=0;i<array.length;i++) {
array[i]=gen.next();
}
for(Rodent temp:array) {
temp.eat();
temp.voice();
}
}
}
练习十:
class Base{
String out() {
return "hello";
}
void print() {
System.out.println(out());
}
}
class Inherit extends Base{
@Override
String out() {
return "nihao";
}
}
public class Test10 {
public static void main(String[] args) {
Base sample =new Inherit();
sample.print();
}
}
private被自动认为是final方法,对导出类是屏蔽的。只有非private方法才能被覆盖。导出类中基于基类中的private方法,最好采用不同的名字。
注意:只有普通的方法调用可以是多态的。直接访问某个域,这个访问将在编译期进行解析,此时不是多态的。如果某个方法是静态的,他的行为也不具有多态性。切记,静态方法是和类,而非单个对象相关联。
构造器其实是static方法,只不过隐藏起来了。基类的构造器总在导出类的构造过程中被调用,且按照继承层次逐渐向上链接,以使得每个基类的构造器被调用。对象调用遵循下面规则
1.调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,以此类推。
2.若构造器内部有多态方法,则会调用。(注意在所有事情发生前,分配给对象的储存空间均是初始化为二进制的0)
3.按声明顺序调用成员的初始化方法
4.调用导出类构造器主体。
清理中销毁顺序和初始化顺序相反。
编写构造器有效的准则:用尽可能简单的方法使得对象进入正常状态;如果可以,避免调用其他方法。构造器内唯一能安全调用的是基类中final方法(private也可以),原因是这些方法不能被覆盖。
协变返回类型,导出类中的被覆盖方法可以返回基类方法的导出类型。
注意导出类中接口的扩展部分不能被基类访问,一旦向上转型,就不能调用新方法。java中所有类型转换都会得到检查(RTTI运行时类型识别),如果想通过父类引用访问导出类对象的扩展,要向下转型。