学习问题记录(3) -- 面对对象高级
1. 如何实现两个对象之间互发消息,请举例说明。
两个对象的相互引用对方的对象,来达到发消息的目的。
class FighterPlane { private String name; private int missileNum; private A a; public void setA(A _a){ if (_a !=null) { a = _a ;} } public A getA(){ if (a !=null) { return a;} else return null; } public void fire(){ ……} public FighterPlane(String _name,int _missileNum){ ……} } class A { FighterPlane fp; public A(FighterPlane fpp){ this.fp = fpp; fpp.setA(this);//将当前对象传给FighterPlane } public void invoke(){ //A中对象发送消息给FighterPlane的对象 fp.fire(); } } public class Run{ public static void main(String[] args) { FighterPlane ftp = new FighterPlane("su35",10); A a = new A(ftp); a.invoke(); } }
2. 谈谈组合与继承的区别以及两者的使用场景(即什么时候宜用组合?什么时候宜用继承?)。
优先考虑组合,如果一个类明显和另一个类有继承关系那么就用继承关系。
3. Java中的运行时多态的含义是什么?有什么作用?请举例说明。
编译时多态:主要是方法的重载和覆盖,通过参数列表的不同来区分不同的方法。
运行时多态:也叫作动态绑定,一般是指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象。
一、通过继承中超类对象引用变量引用子类对象来实现
举例说明:
//定义超类superA class superA { int i = 100; void fun() { System.out.println(“This is superA”); } } //定义superA的子类subB class subB extends superA { int m = 1; void fun() { System.out.println(“This is subB”); } } //定义superA的子类subC class subC extends superA { int n = 1; void fun() { System.out.println(“This is subC”); } } class Test { public static void main(String[] args){ superA a; subB b = new subB(); subC c = new subC(); a=b; a.fun(); (1) a=c; a.fun(); (2) } }
运行结果为:
This is subB
This is subC
上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b, c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:“为什么(1)和(2)不输出:This is superA”。java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值,指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。
另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。
不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,否则子类必须被abstract修饰符修饰,当然也就不能被实例化了。
二、通过接口类型变量引用实现接口的类的对象来实现
接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似。
举例说明:
1 //定义接口InterA 2 interface InterA 3 { 4 void fun(); 5 } 6 //实现接口InterA的类B 7 class B implements InterA 8 { 9 public void fun() 10 { 11 System.out.println(“This is B”); 12 } 13 } 14 15 //实现接口InterA的类C 16 class C implements InterA 17 { 18 public void fun() 19 { 20 System.out.println(“This is C”); 21 } 22 } 23 24 class Test { 25 public static void main(String[] args) { 26 InterA a; 27 a= new B(); 28 a.fun(); 29 a = new C(); 30 a.fun(); 31 } 32 }
输出结果为:
This is B
This is C
上例中类B和类C是实现接口InterA的两个类,分别实现了接口的方法fun(),通过将类B和类C的实例赋给接口引用a而实现了方法在运行时的动态绑定,充分利用了“一个接口,多个方法”展示了Java的动态多态性。
需要注意的一点是:Java在利用接口变量调用其实现类的对象的方法时,该方法必须已经在接口中被声明,而且在接口的实现类中该实现方法的类型和参数必须与接口中所定义的精确匹配。
4. 使用接口改写例6.8中的程序。
package Interface; import java.applet.Applet; import java.awt.*; interface Shape { public abstract double getArea(); public abstract double getPerimeter(); } class Rect implements Shape { public int x, y, k; public double m; public Rect(int x,int y,int k,double m) { this.x = x; this.y = y; this.k = k; this.m = m; } public double getArea() { return (k*m); } public double getPerimeter() { return (2*(x+y)); } } class Triangle implements Shape { public int a, b, c; public double m; public Triangle(int a,int b,int c) { this.a = a; this.b = b; this.c = c; this.m = (a + b + c)/2.0; } public double getArea() { return (Math.sqrt(m*(m-a)*(m-b)*(m-c))); } public double getPerimeter() { return (a+b+c); } } class Circle implements Shape { public int x, y, d; public double r; public Circle(int x,int y,int d) { this.x = x; this.y = y; this.d = d; this.r = d/2.0; } public double getArea() { return (Math.PI*r*r); } public double getPerimeter() { return (2*Math.PI*r); } } class RunShape extends Applet { Rect rect = new Rect(5,15,25,25); Triangle tri = new Triangle(5,5,8); Circle cir = new Circle(13,90,25); public void init(){ } private void drawArea(Graphics g,Shape s,int a,int b) { g.drawString(s.getClass().getName()+" Area"+s.getArea(),a,b); } private void drawPerimeter (Graphics g,Shape s,int a,int b) { g.drawString(s.getClass().getName()+" Perimeter"+s.getPerimeter(),a,b); } public void paint(Graphics g) { g.drawRect(rect.x,rect.y,rect.k,(int)rect.m); g.drawString("Rect Area:"+rect.getArea(),50,35); g.drawString("Rect Perimeter:"+rect.getPerimeter(),50,55); g.drawString("Triangle Area:"+tri.getArea(),50,75); g.drawString("Triangle Perimeter:"+tri.getPerimeter(),50,95); g.drawOval(cir.x-(int)cir.d/2,cir.y-(int)cir.d/2,cir.d,cir.d); g.drawString("Circle Area:"+cir.getArea(),50,115); g.drawString("Circle Perimeter:"+cir. getPerimeter(),50,135); } }
5. 简述运算符instanceof的使用场景。
a instanceof A
a是对象引用,A是类
- 如果a是A或A的子类的实例化,return true;
- 如果a是A的父类的实例化, return false;
- 如果a与A无关系,则编译不通过。
在实际运用中,instanceof多是与运行时多态相关联使用,如
A a = new A(); if( a instanceof B) { B(a).getNameB(); } else if( a instanceof C ) { C(a).getNameC(); } else { System.out.println("No name"); }