effective java 第三天

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不支持泛型数组。

猜你喜欢

转载自blog.csdn.net/weixin_38967434/article/details/82755745