博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
继承和组合
阅读量:4314 次
发布时间:2019-06-06

本文共 4065 字,大约阅读时间需要 13 分钟。

继承和组合都可以实现复用的目的,要根据现实意义来区分用哪一种,就像接口和抽象类的区别,其实这两种方式的开销是差不多的,举个列子如下(摘自《疯狂java讲义》):

  ①继承方式

package 疯狂java讲义;public class InheritTest {    public static void main(String[] args) {        // TODO Auto-generated method stub        Bird bird = new Bird();        bird.breath();        bird.fly();        Wolf wolf = new Wolf();        wolf.breath();        wolf.run();    }}class Animal{ private void beat(){ System.out.println("心脏跳动"); } public void breath(){ beat(); System.out.println("呼吸……"); } } class Bird extends Animal{ public void fly(){ System.out.println("鸟会飞行……"); } } class Wolf extends Animal{ public void run(){ System.out.println("狐狸会跑……"); } }

  ②组合方式

package 疯狂java讲义;public class CompositeTest {    public static void main(String[] args) {        // TODO Auto-generated method stub        Animal1 a1 = new Animal1();        Bird2 bird = new Bird2(a1);        Wolf2 wolf = new Wolf2(a1); bird.Breath(); bird.fly(); wolf.Breath(); wolf.run(); } } class Animal1{ private void beat(){ System.out.println("心脏跳动"); } public void breath(){ beat(); System.out.println("呼吸……"); } } class Bird2{ private Animal1 a; public Bird2(Animal1 a){ this.a = a; } public void Breath(){ this.a.breath(); } public void fly(){ System.out.println("鸟会飞行……"); } } class Wolf2{ private Animal1 a; public Wolf2(Animal1 a){ this.a = a; } public void Breath(){ this.a.breath(); } public void run(){ System.out.println("狐狸会跑……"); } }

  两种方式达到的效果是一致的。但是对于例子中的情况来说是应该用继承的方式的。因为理解应该是:鸟和狐狸是动物的一种,并非说鸟和狐狸是由动物组成的。如果一个人由手,脚,头,身体组成的,这时候就应该使用组合的方式,可以定义五个类,class 人里面是由class 手,class 脚, class 头,class 身体组合而成。继承是is-a的思想,组合是has-a的思想。

  另外,想实现以上的功能,其实也可以不用继承,也不用组合方式,如下:

package 疯狂java讲义;public class CommonTest {    public static void main(String[] args) {        Animal3 a = new Animal3();        Bird3 bird = new Bird3();        Wolf3 wolf = new Wolf3(); bird.Breath(a); bird.fly(); wolf.Breath(a); wolf.run(); } } class Animal3{ private void beat(){ System.out.println("心脏跳动"); } public void breath(){ beat(); System.out.println("呼吸……"); } } class Bird3{ public void Breath(Animal3 a){ a.breath(); } public void fly(){ System.out.println("鸟会飞行……"); } } class Wolf3{ public void Breath(Animal3 a){ a.breath(); } public void run(){ System.out.println("狐狸会跑……"); } }

  那当这几种方法如何取舍呢?其实这就是考究一个人的面向对象的思想,要站在思想的高度去理解问题的本质。如果是继承关系的就用继承,如果是组合关系的就用组合,如果是工具类,就用第三种方法,都是看个人对问题的理解以及考虑以后的扩展去设计。有些人会说,如果这样的话,我何必还要通过传参数的方式把Animal传进来,一下这样也可以啊:

  

package 疯狂java讲义;public class CommonTest2 {    public static void main(String[] args) {        Bird5 bird = new Bird5();        Wolf4 wolf = new Wolf4();        bird.Breath();        bird.fly();        wolf.Breath();        wolf.run();    }}class Animal5{ private void beat(){ System.out.println("心脏跳动"); } public void breath(){ beat(); System.out.println("呼吸……"); } } class Bird5{ public void Breath(){ Animal5 a = new Animal5(); a.breath(); } public void fly(){ System.out.println("鸟会飞行……"); } } class Wolf4{ public void Breath(){ Animal5 a = new Animal5(); a.breath(); } public void run(){ System.out.println("狐狸会跑……"); } }

  的确这样也是可以的如果Animal是一个通用类也可以说是工具类的时候,的确是可以这样的,并且这样和方式上一种方式也并没有什么区别,都不是属于继承和组合的方式,只是把Animal当做一个工具来使用,需要的时候就拿过来使用一下,完全是和面向对象无关的,和当前主业务是无关的,只是一个辅助品,就是一个手机,并非我们的必需品,你能拿手机跟我们的手相提并论么,手机要的时候我可以用一下,不需要的时候就丢掉,你自己的手你可以要就接上,不需要的时候就砍掉么?(当然,爱疯除外,很多人要爱疯不要肾……)就是因为手机是一个工具,对人来说只是一个附加品。如果手机只有一个或者要向别人借的,那就通过类外实例化传参数的方式(毕竟不能经常向别人借,这样总不好)。如果手机遍地都是,那就可以需要的时候才实例化一个手机出来用,自己慢慢理解了。既然说到了这里,那就再顺便说下,这就向是线程池或者数据库连接的连接池或者说是内存了,这些都是比较珍贵的资源或者说是对性能影响比较大的,这时候就最好是创建一个,然后能够重复利用,而每次需要的时候又重新创建一个的话,会导致开销很大,浪费很多资源。比如所Animal很大,new那么多对象出来就会很影响性能了。可能有人说,new 一个,在那个函数结束的时候,就已把Animal占用的资源释放了呀,哪有什么影响。其实虽然说作用域是结束了,但是对于像java或.net这些平台来说,作用域结束了,只是没有指针再指向堆里面的这个对象了,要等待GC回收才是真正释放了他所占的内存,所以,如果Animal很大的话,就会导致平台上的线程默认堆栈很快就到达了极限大小,然后触发GC回收,GC也是一个程序,也需要占用CPU,并且还会引起内存重组,防止出现内存碎片,所以如果是大对象或者是比较耗资源的对象就不要频繁地new一个出来,要有线程池和连接池的思想,new一个出来重复利用,这样能减少很多消耗,提高很多性能,所以告诫各位,千万别再一个循环里面new一个大对象,很容易导致宕机的,哈哈~~。但是如果像是string这些类,很小压根不占什么内存的,就可以在需要的时候就直接new一个出来就可以了。(注意:类大不大,是看类的属性多不多,并非方法;重不重要是看类对应管理的计算机资源缺不缺)

  其实这些模式都不是定死的,都是根据具体问题具体分析,不断变化不断发展不断完善的。

转载于:https://www.cnblogs.com/ismallboy/p/5378379.html

你可能感兴趣的文章
influxdb 命令行输出时间为 yyyy-MM-dd HH:mm:ss(年月日时分秒)的方法
查看>>
FFmpeg 新旧版本编码 API 的区别
查看>>
RecyclerView 源码深入解析——绘制流程、缓存机制、动画等
查看>>
Android 面试题整理总结(一)Java 基础
查看>>
Android 面试题整理总结(二)Java 集合
查看>>
学习笔记_vnpy实战培训day02
查看>>
学习笔记_vnpy实战培训day03
查看>>
VNPY- VnTrader基本使用
查看>>
VNPY - CTA策略模块策略开发
查看>>
VNPY - 事件引擎
查看>>
MongoDB基本语法和操作入门
查看>>
学习笔记_vnpy实战培训day04_作业
查看>>
OCO订单(委托)
查看>>
学习笔记_vnpy实战培训day06
查看>>
回测引擎代码分析流程图
查看>>
Excel 如何制作时间轴
查看>>
matplotlib绘图跳过时间段的处理方案
查看>>
vnpy学习_04回测评价指标的缺陷
查看>>
iOS开发中遇到的问题整理 (一)
查看>>
Linux(SUSE 12)安装jboss4并实现远程访问
查看>>