Java组合、继承
组合
为达到同时使用几个类的方法,可以采用组合的方法。就是将几个类的方法实例化成对象,并变为另一个类的成员。为实现某个功能,可直接在其他区域调用此类中的对象的方法(需注意访问限定符);或在此类的public方法中调用其成员的方法,参数由大类方法传至小类方法。要注意在类开头声明成员变量时只是创建在栈中的引用变量,需在外面的大类构造方法中用new小类构造方法创建引用变量指向的存在堆中的实际值,否则会出现空指向的错误。注意在创建一个由对象构成的数组时,例:Car[] cars = new Car[10]; 中,倘若只有这一句,仅仅让引用变量cars指向10个地址值,而没有在堆中开辟十个对象的真实值,调用Car[0]到Car[9],仍会出现空指向的错误。
public class Car {
private door theDoor;
private wheel theWheel;
public Car(){
theDoor = new door();
theWheel = new wheel();
}
public static void main(String[] args){
Car theCar = new Car();
theCar.theDoor.open();
theCar.theDoor.close();
theCar.theDoor.down();
theCar.theDoor.down();
theCar.theWheel.roll();
}
}
class window{
public window() { }
public void up(){
System.out.println("window up");
}
public void down(){
System.out.println("window down");
}
}
class wheel{
public void roll(){
System.out.println("wheel roll");
}
}
class door{
private window theWindow;
public door(){
theWindow = new window();
}
public void up(){
theWindow.up();
}
public void down(){
theWindow.down();
}
public void open(){
System.out.println("open door");
}
public void close(){
System.out.println("close door");
}
}
通过在Car类中定义其他类类型的成员变量,可以调用其方法,实现了组合。
继承
Java创建一个类时,不是隐式继承于Object类,就是显式继承于指定类,使用extends关键字,语法为在子类名后添加 extends 父类名称。而后子类拥有父类所有的成员变量和方法(private成员和方法除外)。final类不可被继承。
class goodDoor extends door{
int height;
public goodDoor(){
this.height = super.height + 10;
}
public void up(){
System.out.println("重写父类up()方法:");
super.up();
}
public int getHeight(){
return this.height;
}
}
紧接上文的door类,同时为door类添加了height成员变量,并在构造方法中赋值10。goodDoor类继承door类,同时重写父类up()方法。此处引出方法重写和super关键字的讲述:
方法重写
方法重写,就是在子类继承父类时,子类的一个方法名与父类中一个方法名称、参数列表、返回值相同,子类方法对父类方法进行重新写入,在调用子类对象该方法时,出现的是子类的该方法。
类方法的重写应满足下列条件:
1、 子类中该方法的返回值应与父类方法名称、返回值、参数列表相同。
名称不同必不为重写,由于返回值和参数列表足以区分两个方法,名称相同,但其他两项不同则相当于方法重载,此时需遵守重载规则。
2、 子类中重写方法的访问权限不能比基类小,权限顺序为:public > protected > private。
而若在子类中写入与父类private方法同名的方法,则两方法间非重写,没有任何关系。
3、 父类中final方法不能被子类重写。
4、 父类abstract方法必须被非抽象类(abstract)子类重写。
几个关键字
super关键字
1、在子类中可使用super关键字引用父类中的非private成员变量和方法。当父类中方法被子类重写时,在子类中使用super.方法名仍可调用父类未被重写的方法。
2、子类要调用父类的构造方法super(),super()只能在子类的构造方法中调用,且位于首行。即不能在对子类对象初始化后又使用父类的初始化方法。
protected 关键字
由protected修饰的变量和方法,对于在同一包内或其子类来说是可以访问的,而对于其他类来说不能访问。至此应该放上一张从别处拿来的图来加以说明几个访问修饰符。
在其他区域中调用:
goodDoor door1 = new goodDoor();
door1.up();
door1.down();
System.out.print(door1.getHeight());
输出为:
重写父类up()方法:window up
window down
20
可见重写方法执行了自己方法体内的语句。而打印door1.getHeight()结果为20,说明在对象初始化时先依照父类的方法初始化出height = 10; 后执行子类的构造方法子类的height等于父类的height + 10,得到最终20的结果。此处引出含有继承关系的几个类的初始化顺序:
含有继承关系的类的初始化顺序
用下面的代码查看类中各部分初始化顺序:
class test0{
test0(){
System.out.println("父类静态成员变量");
}
}
class test1{
test1(){
System.out.println("父类普通成员变量");
}
}
class test2{
test2(){
System.out.println("子类静态成员变量");
}
}
class test3{
test3(){
System.out.println("子类普通成员变量");
}
}
class father{
static test0 t0 = new test0();
test1 t1 = new test1();
static{
System.out.println("父类静态块");
}
{
System.out.println("父类实例块");
}
father(){
System.out.println("父类构造方法");
}
}
class son extends father{
static test2 t2 = new test2();
test3 t3 = new test3();
static {
System.out.println("子类静态块");
}
{
System.out.println("子类实例块");
}
son(){
System.out.println("子类构造方法");
}
}
在其他地方son theSon = new son(); 来查看运行结果:
父类静态成员变量
父类静态块
子类静态成员变量
子类静态块
父类普通成员变量
父类实例块
父类构造方法
子类普通成员变量
子类实例块
子类构造方法
可以看出,归类所有的静态部分被先初始化,而后是非静态部分,在静态部分和非静态部分都是先进行完父类全部有的,在进行子类全部有的。倘若上述代码中father类还有父类,则在静态部分和非静态部分的两个最前面加上father父类的初始化。
向上转型
即父类引用指向子类对象,在定义变量时语法为:父类名 对象 = new 子类名(参数); 传统的类继承图是从上向下画的,父类在上,其子类依次在下,因而由父类引用指向子类对象在类继承图上是向上移动的,称为向上转型。因为子类继承了父类,子类中至少含有父类的所有内容,或后续增加子类成员、方法使得子类内容多于父类。向上转型中引用所指向的内容只可能减少而不可能增加因而不会出现空指向的情况,因此向上转型可隐式发生。
向下转型也可发生于调用参数为父类对象的方法时,向其中传入子类对象,因为父类所含有的方法不比子类多,所以使用了父类对象的方法,对子类对象也适用。例如:
class animal{
animal(){}
protected void shout(){
System.out.println("shout");
}
}
class dog extends animal{
public void shout(){
System.out.println("dog bark");
}
}
此处dog类继承了animal类且重写了shout方法,在另一个类中写一个方法:
public static void shout(animal theAnimal){
theAnimal.shout();
}
此处可见参数为父类animal类型,在该类main方法new两个对象,再调用。
dog theDog = new dog();
animal theDog2 = new dog();
shout(theDog);
shout(theDog2);
结果为
dog bark
dog bark
不论引用是父类还是子类,传入shout()方法后都调用了dog类中的重写shout方法。若两对象直接调用shout()方法:
theDog.shout();
theDog2.shout();
结果仍为:
dog bark
dog bark
此时为dog类添加一个独有的方法:
public void eat(){
System.out.println("dog eat");
}
两对象调用:
theDog.eat();
((dog) theDog2).eat();
//!theDog2.eat();
向上转型的对象无法使用子类中后续添加的方法,必须强制转型才可使用该类方法。