在日常开发当中我们可能会遇到有些需求需要对一个对象进行拷贝操作,这里就涉及到深拷贝还是浅拷贝的问题,深拷贝是在内存当中创建出一个新的内存空间用于存储原来这个对象相同的内容,浅拷贝就只是拷贝了原来对象的地址值,此时如果使用拷贝操作,那么操作的本质上依旧是原来的对象. 在JDK当中有些集合也提供了Deep Copy的操作,可以根据需要来进行操作,一般情况下建议大家使用谷歌提供的common.lang3下的BeanUtils当中通过反射来对对象进行拷贝操作,这样是相对比较安全的
在日常开发当中遇到对象集合需要根据特定顺序来进行排序时,就可以考虑实现Comparable接口,这个接口是JDK比较早出的在java.lang包下的一个接口,当中只有一个比较方法,为了让所有实现这个接口的类都能具有比较的功能,然而效果并不好, 所以后来在JDK1.8中java.util包下面加强了Comparator的接口,这个接口的可以实现的功能更多,事实上对于顺序比较的接口Spring社区也出了很多对于这个接口的封装,在谷歌的工具类当中也有,开发者可以根据自己的需要来选择对象需要实现的接口
好处:访问性越小,低耦合性和高内聚性越好,更利于模块化开发,方便于我们独立的开发,测试,优化,使用,理解,修改.
总结一句话就是所有字段私有化,提供对应的setter和getter方法来进行访问
原文当中对于这段的描述是根据java.math下的一些类来进行阐述的,所以提出了以下5个观点的要求,我们在开发当中对于这样的一些工具实用类,也是需要遵从这样的设计原则,有兴趣的请详细参照Bigdecimal或者是Integer等类.
复合就是先实现接口创建一个父类,然后再继承这个实现类.继承的好处是提升了代码的重用性,但是也会破坏了封装性.除此之外继承还会导致父类当中有代码相互调用时产生了误差,具体请看下面的代码示例
//需求是要记录Hashset当中添加了多少个元素,所以继承了HashSet,并且新增了字段count
public class ChildHashSet<T> extends HashSet<T> {
private int count = 0;
public ChildHashSet() {
}
public ChildHashSet(int initCap , int loadFactory) {
super(initCap,loadFactory);
}
@Override
public boolean add(T t) {
count++;
return super.add(t);
}
@Override
public boolean addAll(Collection<? extends T> c) {
count = c.size();
return super.addAll(c);
}
public int getCount() {
return count;
}
}
测试用例
//我们期望的结果应该count=3,但是结果是count=6,因为父类的addAll是根据add方法实现的,所以结果就多了一倍
public class Test {
public static void main(String[] args) {
ChildHashSet<String> strings = new ChildHashSet();
strings.addAll(Arrays.asList("1", "2", "3"));
int count = strings.getCount();
System.out.println(count);
}
}
解决方案就是自己先实现Set接口,然后自己写它对应的add和addAll方法,避免他们之间的相互调用,再使用一个想要实现具体类去继承实现类,并且也可以拓展自己想要的一些方法
简单来说继承可能带来的问题在第16条已经有说明,所以当我们在对继承体系进行设计的时候必须要给出相对应的说明文档,说明哪些方法可以覆盖,哪些方法覆盖了会有怎样的危害,让开发者更加简洁的明白,要进行继承的时候应该注意什么
在原文当中主要想说明的就是抽象类只能被继承,但不能被实现.在Java当中支持多实现,但是不支持多继承.所以在拓展性上抽象类要比接口差,但是抽象类当中可以写普通方法的拓展比接口好一些.事实在JDK1.8开始,为了支持对接口的拓展,接口当中已经可以写默认方法和静态方法,用于弥补这部分的缺陷,可以说接口基本上完全可以取代抽象类了
在JDK1.5之前很多人会使用接口来替代现在的枚举类,也就是我们所常说的常量接口,这样的接口是对接口的不良使用.我们并不提倡这样的方法来写常量.接口应该只是用于定义实现了这个接口的类型是什么样的
类层次就是抽象类和普通类搭配形成的一个类的结构,标签类就是将抽象类和普通类柔和成一个类,并且一个类当中存在着多种类型的一种做法,大大降低了可读性的一种写法.具体请看下面的代码示例
//标签类
public class Figure {
public Figure(Shape shape) {
this.shape = shape;
}
private enum Shape {RECTANGLE,CIRCLE};
private final Shape shape;
private double length;
private double wideth;
private double radius;
public Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
public Figure(double length, double wideth) {
shape = Shape.RECTANGLE;
this.length = length;
this.wideth = wideth;
}
double area(){
switch(shape){
case RECTANGLE :
return length * wideth;
case CIRCLE :
return radius * radius;
default:
throw new AssertionError();
}
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWideth() {
return wideth;
}
public void setWideth(double wideth) {
this.wideth = wideth;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
}
类层次代码示例,在实际开发当中将抽象类转成接口会更好一些,这里为了更好解释原文当中的类层次,所以使用了抽象类来进行描述
abstract class FigureTest{
abstract double area();
}
class Rectangle extends FigureTest{
private double length;
private double wideth;
@Override
double area() {
return length * wideth;
}
}
class Circle extends FigureTest{
private double radius;
@Override
double area() {
return radius * radius;
}
}