java 高质量_《编写高质量Java》(五)

四十一、让多重继承成为现实

在Java中一个类可以多重实现,但不能多重继承,也就是说一个类可以同时实现多个接口,但不能同时继承多个类。但有时候我们确实需要继承多个类,比如希望拥有两个类的行为功能,就很难使用单继承来解决了。幸运的是Java提供的内部类可以曲折的解决此问题。

内部类的一个重要特性:内部类可以继承一个与外部类无关的类,保证了内部类的独立性,正是基于这一点,多继承才能成为可能。

四十二、让工具类不可实例化

设置其构造函数位private访问权限。同时为了防止反射实例化该类,还应该抛出异常,代码如下:

public class UtilsClass{

private UtilsClass{

throw new Error("不要实例化我!");

}

}

如此做才能保证一个工具类不会实例化,并且保证所有的访问都是通过类名来进行的。需要注意的一点是:此工具类最好不要做继承的打算,因为如果子类可以实例化的话,那就要调用父类的构造函数,可是父类并没有可被访问的构造函数,于是就会出现问题。

注意:如果一个类不允许实例化,就要保证“平常”渠道都不能实例化它。

四十三、避免对向的浅拷贝

我们知道一个类实现了Cloneable接口就表示它具备被拷贝的能力,如果再覆写clone()方法就会完全具备拷贝能力。拷贝是在内存中进行的,所以在性能方面要比直接new生成对象要快的多,特别是在大对象的生成上,这会使性能的提升非常显著。但是对象拷贝也有一个比较容易忽略的问题:浅拷贝(shadow clone,也叫作影子拷贝)存在对象拷贝不彻底的问题。

Object提供了一个对象拷贝的默认方法,即super.clone()方法,但是该方法是有缺陷的,它提供的是一种浅拷贝方式,也就是说它不对把对象所有的属性都拷贝一份,而是有选择性的拷贝,拷贝规则如下:

基本类型。如果变量是基本类型,则拷贝其值,比如int,float等

对象。如果变量是一个实例对象,则拷贝地址引用,也就是说此时新拷贝出来的对象与原有的对象共享该实例变量,不受访问权限的限制。

String字符串。这个比较特殊,拷贝的也是一个地址,是个引用,但在修改时, 它会从字符串池中重新生成新的字符串,原有的字符串对象保持不变,此处我们可以认为String是一个基本类型。

注意:浅拷贝只是Java提供的一种简单拷贝机制,不便于直接使用。

四十四、推荐使用序列化实现对象的拷贝

可以通过序列化方式,在内存中通过字节流的拷贝来实现,也就是把母对象写到一个字节流中,再从字节流中将其读出来,这样就可以重建一个新对象了,该对象与母对象之间不存在引用共享的问题,也就相当于深拷贝了一个新对象。代码如下:

public class CloneUtils{

//拷贝一个对象

public static T clone(T obj){

T clonedObj = null;

try{

ByteArrayOutputStream baos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream(baos);

oos.writeObject(obj);

oos.close();

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

ObjectInputStream ois = new ObjectInputStream(bais);

clonedObj = (T)ois.readObject(obj);

ois.close();

}catch(Exception e){

e.printStackTrace();

}

return clonedObj;

}

}

此工具类要求被拷贝的对象必须实现 Serializable接口,否则是没有办法拷贝的(反射除外),当然,serialVersionUID常量还是要加上去的,然后我们就可以通过CloneUtils工具进行对象的深拷贝。需要注意两点:

对象的内部属性都是可序列化的。如果有内部属性不可序列化,则会抛出序列化异常。

注意方法和属性的特殊修饰符。比如final、static变量的序列化问题会被引入到对象拷贝中来,这点需要特别注意,同时transient变量(瞬态变量,不进行序列化的变量)也会影响到拷贝的效果。

当然,采用序列化方式拷贝时还有一个更简单的方式,即使用Apache下的Commons工具包中的SerializationUtils类,直接使用更加简洁方便。

四十五、覆写equals方法时不要识别不出自己

我们在写一个JavaBean时,经常会覆写equals方法,其目的是根据业务规则判断两个对象是否相等,这在DAO层是经常用到的。

equals方法的自反性原则:

对于任何非空引用X, X.equals(X)应该返回true

四十六、equals应该考虑null值情景

equals对称性原则:对于任何引用x和y的情形,如果x.equals(y)返回true, 那么y.equals(x)也应该返回true。

注意:在比较之前先判断要比较的引用是否为null

四十七、在equals中使用getClass进行类型判断

使用getClass代替instanceof进行类型判断。

四十八、覆写equals方法必须覆写hashCode方法

HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组的下标处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目则插入,并加入到Map条目的链表中。

重写hashCode代码如下:

@Override

public int hashCode(){

return new HashCodeBuilder.append(name).toHashCode();

}

其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,可以直接在项目中集成。

四十九、推荐覆写toString()方法

当Bean属性较多时,可以使用apache的commons工具包中的ToStringBuilder类,简洁,方便。

五十、 使用package-info类为包服务

Java中有一个特殊的类:package-info类,它是专门为本包服务的。它的特殊主要体现在三个方面:

它不能随便被创建。IDE直接创建会报错,可以用记事本创建一个,然后拷贝进去改一下即可,或者是从别的项目中拷贝过来。

它服务的对象很特殊。它主要描述和记录本包信息的。

package-info类不能有实现代码

另外它不可以继承,没有接口,没有类间关系(关联、组合、聚合)等。

主要有三个作用:

声明友好类和包内访问常量。

为在包上标注注解提供便利。

提供包的整体注释说明。

五十一、不要主动进行垃圾回收

System.gc是一个非常危险的动作,因为它要停止所有的响应,才能检查出内存中是否有可回收的对象,这对一个应用系统来说风险很大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值