>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 24611 个阅读者 刷新本主题
 * 贴子主题:  【Java基础编程专题】为什么说:继承关系最大的弱点就是打破了封装? 回复文章 点赞(0)  收藏  
作者:sunweiqin    发表时间:2017-06-28 12:39:41     消息  查看  搜索  好友  邮件  复制  引用

本文参考:
《Java面向对象编程》,作者:孙卫琴
《漫画Java编程》,作者:孙卫琴,杜聚宾

继承关系最大的弱点就是打破了封装。在设计对象模型时,每个类都应该封装它的属性以及实现细节,这样,当这个类的实现细节发生变化时,不会对其他依赖它的类造成影响。而在继承关系中,子类能够访问父类的属性和方法,也就是说,子类会访问父类的实现细节,子类与父类之间是紧密耦合关系,当父类的实现发生变化,子类的实现也不得不随之变化,这削弱了子类的独立性。

由于继承关系会打破封装,这增加了维护软件的工作量。尤其是在一个Java软件系统还使用了一个第三方提供的Java类库的场合。例如在基于Web的Java应用中,曾经流行使用Apache开源软件组织提供的Struts框架,这个框架的一个扩展点为Action类,在Struts的低版本中,Action类有以下两个方法perform()和saveErrors():
public ActionForward perform(
        ActionMapping mapping,
        ActionForm form,
        ServletRequest request,
        ServletResponse response)throws Exception

protected void saveErrors(HttpServletRequest request, ActionErrors errors)

在Java应用中,可以创建继承Action类的子类,例如LoginAction,然后在LoginAction类中覆盖Action类的perform()方法,在perform()方法中则会调用saveErrors()方法:

public class LoginAction extends Action{

  public ActionForward perform(…){  //覆盖Action类的perform方法
    ActionErrors errors=new ActionErrors();
    ……
    saveErrors(request ,errors);  //调用Action类的saveErrors()方法
  }
}

而在Struts的升级版本中,Action类的perform()方法改名为execute()方法,并且saveErrors(HttpServletRequest request, ActionErrors errors)改为:

saveErrors(HttpServletRequest request, ActionMessages errors)

假如现有的Java应用希望改为使用Struts的升级版本,就必须对自定义的所有Action子类进行修改:

public class LoginAction extends Action{
  public ActionForward execute(…){  //把perform()方法改为execute()方法
    ActionMessages errors=new ActionMessages();  //把ActionErrors改为ActionMessages
    ……
    saveErrors(request ,errors);  
  }
}

从以上例子可以看出,当由第三方提供的Struts框架的Action类做了修改,软件系统中所有Action的子类也要做相应地修改。
由于继承关系会打破封装,还会导致父类的实现细节被子类恶意篡改的危险。例如以下Account类的withdraw()方法和save()方法分别用于取款和存款:

public class Account{
  protected double balance;  //余额
  protected boolean isEnough(double money){
    return balance>=money;
  }
  public void withdraw(double money)throws Exception{  //取款
    if(isEnough(money)) balance-= money;
    else throw new Exception("余额不足!");
  }

  public void save(double money)throws Exception{  //存款
    balance+=money;
  }
}

它的子类SubAccount覆盖了Account类的isEnough()方法和save()方法的实现,使得该账户允许无限制的取款,并且按照实际存款数额的10倍来存款:

public class SubAccount extends Account{
  protected boolean isEnough(double money){  //覆盖父类的isEnough()方法
    return true;
  }

  public void save(double money)throws Exception{  //覆盖父类的save()方法
   balance+=money*10;
  }
}

以下程序定义了Account类型的引用变量account,实际引用SubAccount实例,根据Java虚拟机的动态绑定规则,account.save()和account.withdraw()方法会和SubAccount实例的相应方法绑定:

Account account=new SubAccount();
account.withdraw(2000);  //调用SubAccount实例的withdraw()方法
account.save(100);  //调用SubAccount实例的save()方法

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 admin 在 2022-03-15 14:47:23 重新编辑]
  Java面向对象编程-->图形用户界面(下)
  JavaWeb开发-->自定义JSP标签(Ⅱ)
  JSP与Hibernate开发-->持久化层的映射类型
  Java网络编程-->对象的序列化与反序列化
  精通Spring-->虚拟DOM和render()函数
  Vue3开发-->Vue简介
  【Vue.js技术专题】注册全局组件和局部组件
  【Vue.js技术专题】Vuex中异步操作
  【Spring Cloud Alibaba专题】按照集群模式搭建Redis集群
  【Spring Cloud Alibaba专题】GateWay与Nacos整合
  【Spring Cloud Alibaba专题】OpenFeign开启对请求和响应数据...
  【Spring Cloud Alibaba专题】Nacos配置属性的持久化
  【Java基础编程专题】浮点数的格式化以及运算精度
  【持久化专题】映射一对多双向关联关系
  【持久化专题】用@MapsId注解映射派生主键
  【持久化专题】@Enumerated注解映射枚举类型
  【持久化专题】映射Bag包(值类型的集合)
  【持久化专题】JPA的事件处理API的用法
  【Java网络编程专题】用Java套接字编写基本的客户/服务器程序
  【持久化专题】通过JPQL查询语句进行批量处理数据
  【Java基础编程专题】Java继承的利弊和使用原则
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。