- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章关于为何说JAVA中要慎重使用继承详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
这篇文章的主题并非鼓励不使用继承,而是仅从使用继承带来的问题出发,讨论继承机制不太好的地方,从而在使用时慎重选择,避开可能遇到的坑.
java中使用到继承就会有两个无法回避的缺点:
继承打破了封装性 。
关于这一点,下面是一个详细的例子(来源于effective java第16条) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
myhashset<e>
extends
hashset<e> {
private
int
addcount =
0
;
public
int
getaddcount() {
return
addcount;
}
@override
public
boolean
add(e e) {
addcount++;
return
super
.add(e);
}
@override
public
boolean
addall(collection<?
extends
e> c) {
addcount += c.size();
return
super
.addall(c);
}
}
|
这里自定义了一个hashset,重写了两个方法,它和超类唯一的区别是加入了一个计数器,用来统计添加过多少个元素.
写一个测试来测试这个新增的功能是否工作:
1
2
3
4
5
6
7
8
9
10
|
public
class
myhashsettest {
private
myhashset<integer> myhashset =
new
myhashset<integer>();
@test
public
void
test() {
myhashset.addall(arrays.aslist(
1
,
2
,
3
));
system.out.println(myhashset.getaddcount());
}
}
|
运行后会发现,加入了3个元素之后,计数器输出的值是6.
进入到超类中的addall()方法就会发现出错的原因:它内部调用的是add()方法。所以在这个测试里,进入子类的addall()方法时,数器加3,然后调用超类的addall(),超类的addall()又会调用子类的add()三次,这时计数器又会再加三.
问题的根源 。
将这种情况抽象一下,可以发现出错是因为超类的可覆盖的方法存在自用性(即超类里可覆盖的方法调用了别的可覆盖的方法),这时候如果子类覆盖了其中的一些方法,就可能导致错误.
比如上图这种情况,father类里有可覆盖的方法a和方法b,并且a调用了b。子类son重写了方法b,这时候如果子类调用继承来的方法a,那么方法a调用的就不再是father.b(),而是子类中的方法son.b()。如果程序的正确性依赖于father.b()中的一些操作,而son.b()重写了这些操作,那么就很可能导致错误产生.
关键在于,子类的写法很可能从表面上看来没有问题,但是却会出错,这就迫使开发者去了解超类的实现细节,从而打破了面向对象的封装性,因为封装性是要求隐藏实现细节的。更危险的是,错误不一定能轻易地被测出来,如果开发者不了解超类的实现细节就进行重写,那么可能就埋下了隐患.
超类更新时可能产生错误 。
这一点比较好理解,主要有以下几种可能:
1、超类更改了已有方法的签名。会导致编译错误.
2、超类新增了方法:
设计可继承的类 。
设计可以用来继承的类时,应该注意:
详细解释下第三点。它实际上和 继承打破了封装性 里讨论的问题很相似,假设有以下代码:
1
2
3
4
5
6
7
8
|
public
class
father {
public
father() {
somemethod();
}
public
void
somemethod() {
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
son
extends
father {
private
date date;
public
son() {
this
.date =
new
date();
}
@override
public
void
somemethod() {
system.out.println(
"time = "
+ date.gettime());
}
}
|
上述代码在运行测试时就会抛出nullpointerexception :
1
2
3
4
5
6
7
8
|
public
class
sontest {
private
son son =
new
son();
@test
public
void
test() {
son.somemethod();
}
}
|
因为超类的构造函数会在子类的构造函数之前先运行,这里超类的构造函数对somemethod()有依赖,同时somemethod()被重写,所以超类的构造函数里调用到的将是son.somemethod(),而这时候子类还没被初始化,于是在运行到date.gettime()时便抛出了空指针异常.
因此,如果在超类的构造函数里对可覆盖的方法有依赖,那么在继承时就可能会出错.
结论 。
继承有很多优点,但使用继承时应该慎重并多加考虑。同样用来实现代码复用的还有复合,如果使用继承和复合皆可(这是前提),那么应该优先使用复合,因为复合可以保持超类对实现细节的屏蔽,上述关于继承的缺点都可以用复合来避免。这也是所谓的复合优先于继承.
如果使用继承,那么应该留意重写超类中存在自用性的可覆盖方法可能会出错,即使不进行重写,超类更新时也可能会引入错误。同时也应该精心设计超类,对任何相互调用的可覆盖方法提供详细文档.
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:https://www.cnblogs.com/xz816111/p/9080173.html 。
最后此篇关于关于为何说JAVA中要慎重使用继承详解的文章就讲到这里了,如果你想了解更多关于关于为何说JAVA中要慎重使用继承详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
全称“Java Virtual Machine statistics monitoring tool”(statistics 统计;monitoring 监控;tool 工具) 用于监控虚拟机的各种运
主要是讲下Mongodb的索引的查看、创建、删除、类型说明,还有就是Explain执行计划的解释说明。 可以转载,但请注明出处。  
1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发。 2>循环服务器和并发服务器
详解 linux中的关机和重启命令 一 shutdown命令 shutdown [选项] 时间 选项: ?
首先,将json串转为一个JObject对象: ? 1
matplotlib官网 matplotlib库默认英文字体 添加黑体(‘SimHei')为绘图字体 代码: plt.rcParams['font.sans-serif']=['SimHei'
在并发编程中,synchronized关键字是常出现的角色。之前我们都称呼synchronized关键字为重量锁,但是在jdk1.6中对synchronized进行了优化,引入了偏向锁、轻量锁。本篇
一般我们的项目中会使用1到2个数据库连接配置,同程艺龙的数据库连接配置被收拢到统一的配置中心,由DBA统一配置和维护,业务方通过某个字符串配置拿到的是Connection对象。  
实例如下: ? 1
1. MemoryCahe NetCore中的缓存和System.Runtime.Caching很相似,但是在功能上做了增强,缓存的key支持object类型;提供了泛型支持;可以读缓存和单个缓存
argument是javascript中函数的一个特殊参数,例如下文,利用argument访问函数参数,判断函数是否执行 复制代码 代码如下: <script
一不小心装了一个Redis服务,开了一个全网的默认端口,一开始以为这台服务器没有公网ip,结果发现之后悔之莫及啊 某天发现cpu load高的出奇,发现一个minerd进程 占了大量cpu,googl
今天写这个是为了 提醒自己 编程过程 不仅要有逻辑 思想 还有要规范 代码 这样可读性 1、PHP 编程规范与编码习惯最主要的有以下几点: 1 文件说明 2 funct
摘要:虚拟机安装时一般都采用最小化安装,默认没有lspci工具。一台测试虚拟网卡性能的虚拟机,需要lspci工具来查看网卡的类型。本文描述了在一个虚拟机中安装lspci工具的具体步骤。 由于要测试
1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统
目录 算术运算符 基本四则运算符 增量赋值运算符 自增/自减运算符 关系运算符 逻
如下所示: ? 1
MapperScannerConfigurer之sqlSessionFactory注入方式讲解 首先,Mybatis中的有一段配置非常方便,省去我们去写DaoImpl(Dao层实现类)的时间,这个
Linux的网络虚拟化是LXC项目中的一个子项目,LXC包括文件系统虚拟化,进程空间虚拟化,用户虚拟化,网络虚拟化,等等,这里使用LXC的网络虚拟化来模拟多个网络环境。 本文从基本的网络设备讲
? 1
我是一名优秀的程序员,十分优秀!