本文将通过如下所示类图,在IDE中建一个工程,编写对应代码,在实际操作中认识Java的继承、聚合、合成(组合)、关联、依赖关系,在动手完成这个项目后,对这几个关系的认识会有深刻体会,以后如果别人给你一个类图也就知道如何动手了,画类图也自然不在话下。
继承
这里面继承最多,分为继承类和接口,先将动物类、鸟类、大雁、鸭类、企鹅类、飞翔接口、讲人话接口创建好,并将继承关系一一实现。类图和代码的类转换关系如下:
将其中已有属性和方法实现,各个类中与其他关系有关的方法暂时不实现。其中需要说的是这个鸟类本身有个下蛋行为(方法),继承它的大雁、鸭子、企鹅类中的下蛋行为(方法)其实是对鸟类这个父类下蛋行为的重写。
重写有关继承的多态性,重写也叫作方法覆盖,常常与方法重载进行区分,容易混淆。
重载:在同一个类中,方法名字相同(出现同名方法),由参数列表区别具体调用哪个方法。
覆盖:在类层次结构中,如果子类中的一个方法与父类中的方法有相同的方法名、相同的返回值类型并具有相同数量和类型的参数列表,这种情况称为方法覆盖。当一个覆盖方法通过父类引用被调用,Java根据当前被引用对象的类型来决定执行哪个版本的方法。
引用父类成员的形式为super关键字来引用:
super.成员变量或super.成员方法(参数)
对于构造方法而言,引用方式为: super(参数)
class SuperClass {
int a;
SuperClass(){
a = 10;
}
public void printA() {
System.out.println("父类中a ="+a);}
}
class SubClass extends SuperClass {
int a;
SubClass(int a) {
this.a = a;
}
public void printA() {
System.out.println("子类中a = "+a);
}
}
public class OverrideDemo {
public static void main(String args[]) {
SubClass s1 = new SubClass(10);
s1.printA();
}
}
如上所代码结果将调用子类的printA方法。
如果子类中的方法,仅方法名、返回类型与父类方法一致,但参数列表有区别,则子类中此类情况算方法的重载,而不是覆盖。
覆盖时:子类中与父类同名的成员变量时注意“继承自父类的那些方法,只能操作父类的成员变量。子类中的成员变量与父类中定义的成员变量同名,子类在引用这个变量时,默认是引用它自己定义的成员变量,将从父类那里继承而来的成员变量“覆盖”,覆盖的涵义为隐藏父类同名成员。
关于多态性注意事项我有这些总结:
1、成员变量没有多态性
2、子类与父类对象可以完成类型转换
3、从子类引用的角度看待多态性,主要是理解成员变量和成员方法如何完成覆盖。
4、从父类引用调用子类对象的角度看待多态性,会发现即使父类引用指向的子类对象,父类引用和父类的成员方法依然只能直接操作父类自己的成员变量。父类引用必须调用子类方法才能间接操作子类成员变量(这里的方法必须是子类覆盖了父类的方法,不能是子类新增的方法)。一个重要用法:动态绑定方法。最后注意关键字instanceof。
这里关于Java多态性多说了点,从父类引用调用子类对象的角度看待多态性在本实例中没有体现,所以讲得较少,只有最后一段总结提了一下。继续本实例。
依赖关系
顾名思义,依赖关系是A做xx需要B,如这里的实例中动物的新陈代谢需要氧气和水,是借用关系,成为A依赖B,当B改变时可能A也会被要求改变。表现在代码上,为依赖的类的某个方法以被依赖的类作为其参数,或者是class A 的某个方法创造了 class B 的实例,或对class B的静态方法的调用。
那么本实例的依赖关系就可以这样实现(当然我这只是一种方法,自己想的,还有其他很多种方法,如果有新的想法可以自己思考)
Animal.java
public class Animal {
public char living; //是否有生命
public void metabolism(Oxygen oxygen,Water water) { //新陈代谢
System.out.print("进行新陈代谢 ");
oxygen.needOxygen();
water.needWater();
}
public void reproduce() {
System.out.print("我是可以繁殖的!"); //繁殖
}
Oxygen.java
package edu.hut.software.zqh;
class Oxygen{ //依赖关系空气
public void needOxygen() {
System.out.print("被依赖类氧气 ");
}
}
class Water{ //依赖关系水
public void needWater() {
System.out.print("被依赖类水 ");
}
}
关联关系
关联体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,它使得一个类知道另外一个类的属性和方法,这种关系很强烈,比依赖更强,不是偶然性的,也不是临时性的,而是一种长期性,相对平等的关系,表现在代码层面,为被关联的类B以类属性的形式出现在类A中,也可能是关联类A引用了被关联类B的全局变量。如本实例中企鹅和气候,他们是关联关系,那企鹅类里就应该有气候类的类属性,他们是一种“拥有”的关系。
实现代码就是:
Climate.java
package edu.hut.software.zqh;
public class Climate {
public void changeClimate() {
System.out.print("关联关系“天气”");
}
}
Penguin.java
package edu.hut.software.zqh;
public class Penguin extends Bird{
private Climate climate; //关联关系
public Penguin(Climate climate) {
this.climate=climate;
}
public void layEggs() {
System.out.print("我是企鹅所以我生企鹅蛋 ");
climate.changeClimate();
}
}
关联关系
聚合是关联关系的特例,是强的关联关系,聚合是整个与个体的关系,此时整体和部分是可以分离的,他们具有各自的生命周期,部分可以属于多个对象,也可以被多个对象共享;比如计算机和CPU,公司与员工的关系;在代码层面聚合与关联是一致的,只能从语义上来区分。
聚合关系也是使用实例变量来实现的,在java语法上区分不出关联和聚合,关联关系中类出于一个层次,而聚合则明显的在两个不同的层次。
如本实例中的雁群和大雁,多个个体大雁组成了雁群,大雁离开了雁群,两者都能独立存在。
实现代码:
WideGoose.java
package edu.hut.software.zqh;
public class WideGoose extends Bird implements Fly{
public void layEggs() {
System.out.print("我是大雁所以我生大雁蛋");
}
public void fly() {
System.out.print("我能够飞翔");
}
}
WideGooseAggregate.java
package edu.hut.software.zqh;
public class WideGooseAggregate {
private WideGoose[] arrayWideGoose; //这里暂时不知道应该怎么给那些属性赋值,感觉没太大用,就不赋值了
public void horizontalFly() {
System.out.printf("雁群一字型飞行队列");
}
public void vFiy() {
System.out.printf("雁群一字型飞行队列");
}
}
从通过Java实现的代码上看的确和关联区分不了,但是两个类不在一个层次。
合成(组合)关系
组合也是关联关系的一种特例,比聚合更强,是一种强聚合关系。它同样体现整体与部分的关系,但此时整体与部分是不可分的,整体生命周期的结束也意味着部分生命周期的结束,反之亦然。如大脑和人类,也如本例中的鸟和翅膀,鸟没了翅膀还存在就真的太恐怖了。
实现代码如下:
Wing.java
package edu.hut.software.zqh;
public class Wing {
public void wing() {
System.out.print("合成关系“有翅膀”");
}
}
Animal.java
package edu.hut.software.zqh;
public class Bird extends Animal{
public int feather; //是否有羽毛
public int mouse; //嘴巴是否是有角质喙而没有牙齿
private Wing wing;
public Bird() {
wing=new Wing();
wing.wing();
}
public void layEggs() { //下蛋
System.out.print("不同种类的鸟下蛋种类不同!");
}
}
也和关联在代码上没啥区别
最后
在过程中我已经给了大部分代码,剩下的是实现继承的类,如下:
package edu.hut.software.zqh;
public class DonaldDuck extends Duck implements Speak{
public void speak() {
System.out.print("大家好,我是唐老鸭,我和其他鸭子不一样的是我继承了讲人话接口,会说话");
}
}
package edu.hut.software.zqh;
public class Duck extends Bird{
public void layEggs()
{
System.out.print("我是鸭子所以我生鸭蛋");
}
}
package edu.hut.software.zqh;
public interface Speak {
public abstract void speak();
}
interface Fly{
public abstract void fly();
}
public static void main(String args[]) { //调用继承的方法并输出相关语句验证各种关系是否实现
Penguin penguin=new Penguin(new Climate());
System.out.print(" ");
penguin.layEggs();
System.out.print(" ");
penguin.metabolism(new Oxygen(), new Water());
System.out.print(" ");
penguin.reproduce();
System.out.println();
DonaldDuck donaldduck=new DonaldDuck();
System.out.print(" ");
donaldduck.layEggs();
System.out.print(" ");
donaldduck.metabolism(new Oxygen(), new Water());
System.out.print(" ");
donaldduck.reproduce();
System.out.print(" ");
donaldduck.speak();
System.out.println();
WideGoose widegoose=new WideGoose();
System.out.print(" ");
widegoose.layEggs();
System.out.print(" ");
widegoose.metabolism(new Oxygen(), new Water());
System.out.print(" ");
widegoose.reproduce();
System.out.print(" ");
widegoose.fly();
}
}
所有代码都已经上传,运行后可以看到执行顺序