16.复合优于继承
不当的继承会导致软件的脆弱;在包内使用继承是安全的,因为都在程序员的掌控下;而对于普通的类,进行跨越包边境的继承是危险的(继承打破了封装性,如果父类的代码改变了,子类的代码虽然不变,但是也会被改变,遭到破坏),继承那些专门为继承设计的类是安全的。
只有在存在is-a关系的时候,才使用继承。has-a就用复合。
继承会把有缺陷的api传播给子类,而复合则可以写新的api来隐藏有缺陷的方法。
17.要么为继承设计,并提供文档说明,要么就禁止继承
对于为了被继承而设计的类,唯一的测似方法就是编写子类,根据经验,3个子类就可以测试一个可扩展的类。
构造器绝不能调用可被覆盖的方法,无论是直接还是间接。
18.接口优于抽象类
接口是定义mixin(混合类型)的理想选择。mixin指这样的类型:类除了实现它的“基本类型”之外,还可以实现这个mixin类型,以表明它提供了某些可供选择的行为。Mixin是一种思想,用部分实现的接口来实现代码复用。可以用来解决多继承的问题,又可以用来扩展功能。Mixin在不同的编程语言中又不同的使用形式或者命名,但其本质都是一样的。、
骨架AbstractInterface:
必须在初次设计接口的时候一步到位,因为想在接口里面增加方法基本是不可能的。
19.接口只用于定义类型
常量接口模式是对接口的不良使用。
20.类层次优于标签类
类层次:根类,超类,子类,考虑结构层次,我也不知道怎么说,“高内聚,低耦合,结构清明”。
什么是标签类,一个类中有许多样板代码充斥在一个单类中,破坏可读性,内存占用增加,因为实例承担着不相关的域,实例化域不当,会引起程序bug,
总之标签类过于冗余,容易出错,并且效率低下。
21.用函数对象表示策略
某类只含有几个方法,则该类的实例可以作为函数指针;
https://blog.csdn.net/u012401711/article/details/52463347(策略模式)
22.优先考虑静态成员类
嵌套类:定义在一个类的内部的类。
嵌套类有4种:静态成员类、非静态成员类、匿名类、局部类。除了第一种外,其他都被称为内部类。
静态成员类是最简单的一种嵌套类,最好把它看成普通类,只是碰巧被声明在另一个类的内部而已,它可以访问外围类的所有成员,包括那些私有成员。静态成员类是外围类的一个静态成员,与静态成员一样,遵守同样的可访问规则。如果它被声明为私有,它就只能在外围类的内部才可以被访问。
非静态成员类的每个实例都隐含着一个与外围类的外围实例相关联。非静态成员类的实例方法内部,可以调用外围实例上的方法,或者利用修饰过的this构造器获得外围实例的引用。如果嵌套类的实例可以在它外围类的实例之外存在,那么它必须是静态内部类。
在内部类(非静态成员类)种得到外围类的引用: 外围类名.this.
如果声明的成员类不要求访问外围实例时,就应该把他声明成static的。否则它的每个实例都会关联一个外围类的实例,消耗时间和空间。
匿名类的常见用法:1.动态创建函数对象。2.创建过程对象(创建Runnable、Thread实例)。3.用在静态工厂方法的内部。
局部类用得较少。
23.请不要在新代码中使用原生态类型
List:原生态类型;List<String>:参数化的类型
使用参数化的类型就会在编写代码期间发现问题所在。而且哥哥对象不用进行手工类型转换了。
目前Java还支持原生态类型,是因为已经存在许多代码是使用了原生态类型的,为了兼容性考虑。
List和List<Object>的区别:前者逃避类型检查,后者明确说了类型。
使用通配符:? 使用通配符是安全的,使用原生态的是不安全的。
24.消除非受检警告
要尽可能消除警告,避免错误。
如果无法消除警告,同时可以证明引起警告的代码是类型安全的,可以使用@SuppresWarnings("unchecked")注解来禁止这条警告。
25.列表优先于数组
数组和泛型相比:
1.数组是协变的(covariant)如果sub是super的子类,那么sub数组也是super数组的子类;但是泛型不是这样,Type<sub>和Type<super>没有什么关系。
2.数组是具体化的,数组会在运行时才知道并检查它们的元素类型约束。泛型则是通过擦除(erasure)来实现,泛型只会在编译的时候强化他们的类型信息,但是在运行时丢弃(或者擦除)它们的元素类型信息。
为什么不支持泛型数组:
//Cannot create a generic array of ArrayList<Integer>
ArrayList<Integer>[] intArr = new ArrayList<Integer>[10];
Object[] obj = intArr;
ArrayList<String> listStr = new ArrayList<String>();
obj[0] = listStr;
ArrayList<Integer> listInt = intArr[0];
Integer i = listInt.get(0);//想要Integer,但却是String
假设允许泛型数组,那么第2行是正确的,那么将不会有第1行中所示的编译错误。
那么就可以将 intArr 转型成 Object[],然后向Object[]放 ArrayList<String>,而不是我们想要的ArrayList<Integer>
因此,在运行时,类型是擦除的,运行时系统无法对数组中存储的类型做检查。它看到仅是:向intArr数组里面放 ArrayList对象。
相当于:
// ArrayList<String> listStr = new ArrayList<String>();
ArrayList listStr = new ArrayList();//运行时看到的情况
// ArrayList<Integer> listInt = intArr[0];
ArrayList listInt = intArr[0];//运行时看到的情况
在上面第9行,如果改成:
Object o = listInt.get(0);
//do something with o
我们以为Object o 它实际引用 的是Integer类型的,但它底层却是String类型的,如果调用 hashCode(),我们以为它执行的是Integer的hashCode(),但它执行的是String的hashCode(),那意味着发现不了错误。。。。。。。因为是执行的时候才会抛出错误,所以这个泛型的初衷背道而驰。
因此,JAVA不支持泛型数组。