java中类与类之间的关系

大部分的初学者只知道java中两个类之间可以是继承与被继承的关系,可是事实上,类之间的关系大体上存在五种—继承(实现)、依赖、关联、聚合、组合。

接下来,简单的分析一下这些关系。

继承(实现)

对于类来说,这种关系叫做继承,对于接口来说,这种关系叫做实现。继承上一篇文章已经详细的讲解过了,至于实现,我想大家也都知道是怎么回事,由于后面要专门讲接口,所以这里就先不说了。继承是一种“is-a”关系。

依赖

依赖简单的理解,就是一个类A中的方法使用到了另一个类B。

这种使用关系是具有偶然性的、临时性的、非常弱的,但是B类的变化会影响到A。

比如说,我用笔写字,首先需要一个类来代表我自己,然后需要一个类来代表一支笔,最后,‘我’要调用‘笔’里的方法来写字,用代码实现一下:

public class Pen {

public void write(){

System.out.println("use pen to write");

}

}

public class Me {

public void write(Pen pen){//这里,pen作为Me类方法的参数。 Me类依赖Pen类

pen.write();

}

}

看到这大家都懂了,因为这种代码你每天都会写。现在你知道了,这就是一种类与类之间的关系,叫做依赖。

这种关系是一种很弱的关系,但是pen类的改变,有可能会影响到Me类的结果,比如我把pen类write方法的方法体改了,me中再调用就会得到不同的结果。

一般而言,依赖关系在Java中体现为局域变量、方法的形参,或者对静态方法的调用。

关联

关联体现的是两个类、或者类与接口之间语义级别的一种强依赖关系。

这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的、关联可以是单向、双向的。

看下面这段代码:

//pen 还是上面的pen

public classYou {private Pen pen; //让pen成为you的类属性

publicYou(Pen p){this.pen =p;

}public voidwrite(){

pen.write();

}

}

被关联类B以类属性的形式出现在关联类A中,或者关联类A引用了一个类型为被关联类B的全局变量的这种关系,就叫关联关系。

在Java中,关联关系一般使用成员变量来实现。

聚合

聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系

看下面一段代码:

public classFamily {private List children; //一个家庭里有许多孩子//...

}

在代码层面,聚合和关联关系是一致的,只能从语义级别来区分。普通的关联关系中,a类和b类没有必然的联系,而聚合中,需要b类是a类的一部分,是一种”has-a“的关系,即 a has-a b; 比如家庭有孩子,屋子里有空调。

但是,has 不是 must has,a可以有b,也可以没有。a是整体,b是部分,整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。

不同于关联关系的平等地位,聚合关系中两个类的地位是不平等。

组合

组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。

先看一段代码:

public classPerson {private Eye eye = new Eye(); //一个人有鼻子有眼睛

private Nose nose = newNose();//....

}

组合同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束。

就像你有鼻子有眼睛,如果你一不小心结束了生命周期,鼻子和眼睛的生命周期也会结束,而且,鼻子和眼睛不能脱离你单独存在。

只看代码,你是无法区分关联,聚合和组合的,具体是哪一种关系,只能从语义级别来区分。

同样,组合关系中,两个类关系也是不平等的。

组合,聚合和继承

依赖关系是每一个java程序都离不开的,所以就不单独讨论了,普通的关联关系也没有什么特殊的地方,下面我们重点研究一下组合,聚合和继承。

聚合与组合

聚合与组合都是一种关联关系,只是额外具有整体-部分的意义。

部件的生命周期不同

聚合关系中,整件不会拥有部件的生命周期,所以整件删除时,部件不会被删除。再者,多个整件可以共享同一个部件。

组合关系中,整件拥有部件的生命周期,所以整件删除时,部件一定会跟着删除。而且,多个整件不可以同时间共享同一个部件。

这个区别可以用来区分某个关联关系到底是组合还是聚合。两个类生命周期不同步,则是聚合关系,生命周期同步就是组合关系。

聚合关系是【has-a】关系,组合关系是【contains-a】关系。

平时我们只讨论组合和继承的时候,认为组合是【has-a 】关系,而事实上,聚合才是真正的【has-a】关系,组合是更深层次的【contains-a】关系。

由于【contains-a】关系是一种更深的【has-a】关系,所以说组合是【has-a】关系也是正确的。

组合和继承

这个才是本文的重点。

学过设计模式的都知道,要“少用继承,多用组合”,这究竟是为什么呢?

我们先来看一下组合和继承各自的优缺点:

组合和继承的优缺点

组合

优点:

-不破坏封装,整体类与局部类之间松耦合,彼此相对独立-具有较好的可扩展性-支持动态组合。在运行时,整体对象可以选择不同类型的局部对象- 整体类可以对局部类进行包装,封装局部类的接口,提供新的接口

缺点:

-整体类不能自动获得和局部类同样的接口- 创建整体类的对象时,需要创建所有局部类的对象

缺点分析:

1、整体类不能自动获得和局部类同样的接口

如果父类的方法子类中几乎都要暴露出去,这时可能会觉得使用组合很不方便,使用继承似乎更简单方便。

但从另一个角度讲,实际上也许子类中并不需要暴露这些方法,客户端组合应用就可以了。

所以上边推荐不要继承那些不是为了继承而设计的类,一般为了继承而设计的类都是抽象类。

创建整体类的对象时,需要创建所有局部类的对象

继承

优点

-子类能自动继承父类的接口- 创建子类的对象时,无须创建父类的对象

缺点:

-破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性-支持扩展,但是往往以增加系统结构的复杂度为代价-不支持动态继承。在运行时,子类无法选择不同的父类- 子类不能改变父类的接口

缺点分析:

1、为什么继承破坏封装性?

鸭子中不想要“飞”的方法,但因为继承无法封装这个无用的“飞”方法 。

2、为什么继承紧耦合:

当作为父类的BaseTable中感觉Insert这个名字不合适时,如果希望将其修改成Create方法,那使用了子类对象Insert方法将会编译出错,可能你会觉得这改起来还算容易,因为有重构工具一下子就好了并且编译错误改起来很容易。但如果BaseTable和子类在不同的程序集中,维护的人员不同,BaseTable程序集升级,那本来能用的代码忽然不能用了,这还是很难让人接受的

3、为什么继承扩展起来比较复杂

当图书和数码的算税方式和数码产品一样时,而消费类产品的算税方式是另一样时,如果采用继承方案可能会演变成如下方式:

这样如果产品继续增加,算税方式继续增加,那继承的层次会非常复杂,而且很难控制,而使用组合就能很好的解决这个问题

4、继承不能支持动态继承

这个其实很好理解,因为继承是编译期就决定下来的,无法在运行时改变,如3例中,如果用户需要根据当地的情况选择计税方式,使用继承就解决不了,而使用组合结合反射就能很好的解决。

5、为什么继承,子类不能改变父类接口

如2中的图,子类中觉得Insert方法不合适,希望使用Create方法,因为继承的原因无法改变

组合与继承的区别和联系

在继承结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种 白盒式代码复用。(如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性)

组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说这种方式的代码复用是黑盒式代码复用 。(因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法)

继承在写代码的时候就要指名具体继承哪个类,所以,在编译期就确定了关系。(从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。)

组合,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定。

组合(has-a)关系可以显式地获得被包含类(继承中称为父类)的对象,而继承(is-a)则是隐式地获得父类的对象,被包含类和父类对应,而组合外部类和子类对应。

组合是在组合类和被包含类之间的一种松耦合关系,而继承则是父类和子类之间的一种紧耦合关系。

当选择使用组合关系时,在组合类中包含了外部类的对象,组合类可以调用外部类必须的方法,而使用继承关系时,父类的所有方法和变量都被子类无条件继承,子类不能选择。

最重要的一点,使用继承关系时,可以实现类型的回溯,即用父类变量引用子类对象,这样便可以实现多态,而组合没有这个特性。

还有一点需要注意,如果你确定复用另外一个类的方法永远不需要改变时,应该使用组合,因为组合只是简单地复用被包含类的接口,而继承除了复用父类的接口外,它甚至还可以覆盖这些接口,修改父类接口的默认实现,这个特性是组合所不具有的。

从逻辑上看,组合最主要地体现的是一种整体和部分的思想,例如在电脑类是由内存类,CPU类,硬盘类等等组成的,而继承则体现的是一种可以回溯的父子关系,子类也是父类的一个对象。

这两者的区别主要体现在类的抽象阶段,在分析类之间的关系时就应该确定是采用组合还是采用继承。

引用网友的一句很经典的话应该更能让大家分清继承和组合的区别:组合可以被说成“我请了个老头在我家里干活” ,继承则是“我父亲在家里帮我干活”。

继承还是组合?

首先它们都是实现系统功能重用,代码复用的最常用的有效的设计技巧,都是在设计模式中的基础结构。

很多人都知道面向对象中有一个比较重要的原则『多用组合、少用继承』或者说『组合优于继承』。从前面的介绍已经优缺点对比中也可以看出,组合确实比继承更加灵活,也更有助于代码维护。

所以,建议在同样可行的情况下,优先使用组合而不是继承。因为组合更安全,更简单,更灵活,更高效。

注意,并不是说继承就一点用都没有了,前面说的是【在同样可行的情况下】。有一些场景还是需要使用继承的,或者是更适合使用继承。

继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。

只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在 is-a 关系的时候,类B才应该继承类A。

发生多态的三个前提条件:

继承。多态发生一定要子类和父类之间。

覆盖。子类覆盖了父类的方法。

声明的变量类型是父类类型,但实例则指向子类实例。

参考文章: https://blog.csdn.net/qq_31655965/article/details/54645220

java依赖_java 依赖、组合、聚合与继承相关推荐

  1. maven java管理_java – 依赖管理与maven

    我最近成了Maven的大粉丝,用于控制我的应用程序的构建周期.然而,我遇到了一些粗暴的边缘与Maven的依赖管理.我想知道这些是否是工具和范例的限制,依赖管理的必要的邪恶,或者我是否使用错误的工具. ...

  2. 02-Maven高级-分模块开发、依赖传递、聚合、继承(SpringBoot的部分底层原理)、多模块开发(环境切换)、Nexus私服搭建与使用

    文章目录 学习目标 一.分模块开发与设计 1. 分模块开发的意义 问题导入 模块拆分原则 2. 分模块开发(模块拆分) 问题导入 2.1 创建Maven模块 2.2 书写模块代码 2.3 通过mave ...

  3. 继承、实现、依赖、关联、聚合、组合的联系与区别

    继承 指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系:在Java中此类关系通过关键字extend ...

  4. [zz] 几种类间关系:继承、实现、依赖、关联、聚合、组合及UML实现图

    继承 指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间 最常见的关系:在Java中此类关系通过关键字exten ...

  5. UML中几种类间关系:继承、实现、依赖、关联、聚合、组合的联系与区别

    zz from: http://dev.firnow.com/course/3_program/java/javajs/20090219/155939.html UML中几种类间关系:继承.实现.依赖 ...

  6. UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)

    在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregation),组合(Composit ...

  7. 【UML】UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)

    http://www.cnblogs.com/olvo/archive/2012/05/03/2481014.html 继承.实现.依赖.关联.聚合.组合的联系与区别 分别介绍这几种关系: 继承 指的 ...

  8. 【转】 UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合

    目录(?)[-] 继承 实现 依赖 关联 聚合 组合 类图及类图中的关系 继承.实现.依赖.关联.聚合.组合的联系与区别 分别介绍这几种关系: 继承 指的是一个类(称为子类.子接口)继承另外的一个类( ...

  9. UML中几种类间关系:继承、实现、依赖、关联、聚合、组合的联系与区别 - 老唐 的专栏 -... (转载)

    这是一堂关于UML基础知识的补习课:现在我们做项目时间都太紧了,基本上都没有做过真正的class级别的详细设计,更别提使用UML来实现规范建模了:本篇主要就以前自己一直感觉很迷糊的几种class之间的 ...

最新文章

  1. 苹果微信更新不了最新版本_微信版本又双叒叕更新了,这个功能正式下线
  2. spring cloud集成 consul源码分析
  3. VirtualBox 启动时提示“获取 VirtualBox COM 对象失败”的解决
  4. bminfowindow是什么_三步实现地图自定义InfoWindow
  5. c# getresponsestream返回byte[]_C++模版和C#范型求同存异录(一)sizeof(T)
  6. vs2010 mysql linq to sql 系列_LINQ to SQL 系列 如何使用LINQ to SQL插入、修改、删除数据...
  7. 项目过程总结 和某个字段的更新
  8. VS2015 C#程序打包成.exe之installshield使用教程
  9. Less + HTML + JS实现流星划过星空动画
  10. jq模仿雨滴下落的动画
  11. 2、什么是软件过程?它与软件工程方法学有何关系?
  12. golang远程桌面服务器,go+govcl+go-ole实现的windows远程桌面
  13. windows如何获取端口号
  14. uni-app第三方登录
  15. Android自定义九宫格手势解锁组件
  16. 微软全球副总裁李开复给中国青年的一封信
  17. 做了5年软件测试,对游戏UI自动化测试的一些理解和认识
  18. 格创东智加入“长三角数字干线建设·合伙人行动”
  19. C语言简单数独游戏终盘生成
  20. 好系统重装助手教你win10快速开机的方法

热门文章

  1. 部署被测软件应用和中间件_使用FlexDeploy对融合中间件应用程序进行自动化软件测试...
  2. 枚举对象注释_如何以及何时使用枚举和注释
  3. java cuba_CUBA平台–新的Java企业应用程序框架
  4. 给oim_对OIM Web(UI)层进行压力测试
  5. mysql重置增量_摆脱困境:在每种测试方法之前重置自动增量列
  6. 使用UriBuilder快速创建URI
  7. 使用Selenium自动化测试处理多个浏览器选项卡
  8. 非一致性访存系统_Hibernate事实:访存策略的重要性
  9. 使用Spring Data Cassandra缓存的预备语句
  10. 在Java 8之前,您编写了几行代码来对对象集合进行排序?