3、类与接口
15、使类和成员的可访问性最小化
- 尽可能地使每个类或者成员不被外界访问。
- 让类具有公有的静态final数据域,或者这种域的访问方法,通常是错误的。(将其变为私有,并提供一个公有的不可变的列表)
private static final Object[] A_A= {1,2,3};
public static final List<Object> values = Collections.unmodifiableList(Arrays.asList(A_A));
public static final Object[] values(){
return A_A.clone();
}
- Java9开始,一个模块就是一个包
- 四个访问级别(私有,包私有,受保护的,公有的)
16、要在共有类而非公有域中使用访问方法
- 如果类是包级私有的,或者私有的嵌套类,直接暴露它的数据域并没有本质的错误
- 共有类永远都不应该暴露可变的域。
17、使可变性最小化
- 使类成为不可变类的五条规则
- 不要提供任何会修改对象状态的方法(设值方法)
- 保证类不会被扩展。(恶意子类 假装对象状态改变,防止子类化,一般声明类 final的)
- 声明所有域都是final的
- 声明所有的域都为私有的
- 确保对于任何可变组件的互斥访问
- 不可变对象本质上使线程安全的,它们不要求同步。
- 不可变类真正唯一缺点是:对于每个不同的值都需要一个单独的对象。
- 构造器应该创建完全初始化的对象,并建立所有的约束关系,不要在构造器或者静态工厂方法之外再提供公有的初始化方法。
18、复合优先于继承
- 与方法调用不同的是,继承打破了封装性。(超类的实现可能随者发行版本变化,子类可能会遭受到破坏)
- 不扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例。
19、要么设计继承并提供文档说明,要么禁止继承
- 该类必须要有文档说明它可覆盖的方法的自用性。
- 构造器绝不能调用可被覆盖的方法。
20、接口优先于抽象类
- 现有的类可以很容易被更新,以实现新的接口。
- 接口是定义mixin(混合类型)的理想选择。
21、为后代设计接口
- Java8之后,接口增加了缺省实现 通过defualt关键字。
22、接口只用于定义类型
- 常量接口模式(一个接口只包含静态的final域,每个域都导出一个常量)是对接口的不良使用。
- 当类实现接口时,接口就充当可以引用这个类的实例的类型。
23、类层次优于标签类
24、静态成员类优于非静态成员类
- 嵌套类分为四种:静态成员类,非静态成员类,匿名类,局部类;除了第一种之外,其他三种都称为内部类。
- 如果声明成员类不要求访问外部实例,就要始终把修饰符static放在它的声明中。
- 匿名类在使用的同时被声明和实例化。
- 局部类,在方法内部定义类。
25、限制源文件为单个顶级类
4、泛型
26、请不要使用原生态类型
- 每一种泛型都定义一种原生态类型,例如 List 相对应的原生态类型是List;
- 如果使用原生态类型,就失掉了泛型在安全性和描述性方面的所有优势。
- 如果是像List这样的原生态类型,就会失掉类型安全性,但是如果使用像List这样的参数类型,就不会。
- ?无限制的通配符类型
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d046c7db95465cfcbdda26d498bdfeeb.png#pic_center)
27、消除非受检的警告
- 参考 29中的代码: @SuppressWarnings(“unchecked”)
28、列表(List)优先于数组
- 数组和泛型相比,有两个很重要的不同点,
- 数组是协变的,泛型是可变的;
- 数组是具体化的,泛型是擦除来实现的;
- 在某些情况下,即使某个对象不是数组的基类型,我们也可以把它赋值给数组元素。这种属性叫做协变 。例如:Object[] =new Long [l ] ;
29、优先考虑泛型
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c53d3c3f12c5d10cf6306d85be2ca75a.png)
- 考虑泛型则 new E()会有错误;此错误有两种解决方法
- elements = (E[]) new Object[数组长度]; (优先使用,但会导致堆污染,因为数组运行时数据类型和编译时数据类型不一致)
- 把E[] 改为Object[]类型,用的时候**@SuppressWarnings(’'unchecked”) = (E) e l ements[–size];**
@SuppressWarnings("unchecked")
public synchronized <T> T[] toArray(T[] a) {
if (a.length < elementCount)
return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());
System.arraycopy(elementData, 0, a, 0, elementCount);
if (a.length > elementCount)
a[elementCount] = null;
return a;
}
@HotSpotIntrinsicCandidate
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
30、优先考虑泛型方法
- 静态工具方法尤其适合泛型化
- 声明类型参数的类型参数列表,处于方法的修饰符及其返回值之间
- public static < E> Set< E> union(Set< E> sl, Set< E> s2) {方法体…}, 类型参数列表< E> ,返回类型Set< E>
- public static <E extends Comparable E max(Collection c]; 其中<E extends Comparable 可以读作 针对每个可以与自身相比较的类型E;
31、利用有限制通配符来提升API的灵活性
- 为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型
- 如果参数化类型表示一个 T 生产者,就使用 <? extends T>;
- 如果它表示一个 T 消费者, 就使用 <? super T> ;
- 不要使用通配符作为返回类型
- PESC原则:PESC = producer-extens , consumer -super.
- comparable和comparator都是消费者;
32、谨慎并用泛型和可变参数
- 将值保存在泛型可变参数数组中是不安全的;
- 显式创建泛型数组是非法的 ,泛型可变参数声明方法是合法的 ,由于泛型可变参数声明方法实践用处比较大,允许了这一矛盾现象的存在。
- SafeVarargs注解是通过方法的设计者做出承诺,说明这是类型安全的;不要随意用**@SafeVarargs**对方法注解,除非它是真正安全的;
- 对于每一个带有泛型可变参数或者参数化类型的方法,都要用@SafeVarargs 进行注解;
- 允许另一个方法访问一个泛型可变参数数组是不安全的;
- 泛型可变参数在下列条件是安全的:
- 1、它没有在可变参数数组中保存任何值;
- 2、它没有对不被信任的 ;
- 以上两个条件只要有任何一条被破坏,就要立即修正它;
static <T> T[] toArray(T...args){
return args;
}
static <T> T[] pickTwo(T a, T b,T c){
switch (ThreadLocalRandom.current().nextInt(2)){
case 0: return toArray(a,b);
case 1: return toArray(a,c);
}
throw new AssertionError();
}
public static void main(String[] args) {
String[] strings = pickTwo("goog", "fast","aaaaa");
System.out.println(strings[0]);
}
33、优先考虑类型安全的异构容器
- 将键(key)进行参数化而不是将容器(container)参数化。然后将参数化的键提交给容器来插入或者获取值,用泛型系统来确保值的类型与它的键相符。
public class demo_33 {
private Map<Class<?>, Object> fa = new HashMap<>();
public <T> void putFa(Class<T> type,T instance){
fa.put(Objects.requireNonNull(type),instance);
}
public <T> T getFa(Class<T> type){
return type.cast(fa.get(type));
}
public static void main(String[] args) {
final demo_33 demo_33 = new demo_33();
demo_33.putFa(String.class,"java");
demo_33.putFa(Integer.class,1);
final String fa = demo_33.getFa(String.class);
System.out.println(fa);
}
}