类的访问权限
Tables | 同类 | 同包 | 不同包的子类 | 不同包的非子类 |
Public | √ | √ | √ | √ |
Protected | √ | √ | √ | |
Default | √ | √ | ||
Private | √ |
可变性最小化
- 不要提供任何会修改对象状态的方法
- 我可以工地不会被扩展
- 使所有域都是final
- 使所有的域都成为怎么私有
- 确保对于任何可变组件的互斥访问
接口优于抽象类
抽象类允许某些方法的实现,但是接口不允许.Java单继承,抽象类作为类型定义受到了很大的限制 .
- 现有的类可以很容易被更新 , 以实现新的接口.
- 接口是定义混合类型的理想选择.
- 接口允许我们构造非层次结构的类型框架
虽然接口不允许包含方法的实现,但是,使用接口来定义类型不妨碍为程序提供实现上的帮助,通过对你导出的每个重要接口都提供一个抽象的骨架的实现 skeletal implementation 类 ,把接口和抽象类的优点结合起来.
骨架实现被称为 AbstractInterface , 这里的Interface是指所实现的接口的名字. 如 Collections Framework 为每个重要集合接口都提供了一个骨架实现, 包括 AbstractCollection , AbstractSet AbstractList AbstractMap . 将它们称作SkeletalCollection , SkeletalSet , SkeletaList , SkeletalMap 也是有道理的. 但是现在Abstract 的用法已经根深蒂固 .
骨架实现的好处在于, 它们为抽象类提供了实现的上帮助,但能不强加抽象类被用作类型定义时 所特有的严格限制.对于接口的大多数实现来讲,扩展骨架实现类是个很显然的选择,但并不是必要.
接口通常是定义允许多个实现类型的最佳途径.这条规则有个例外,即当演变的容易性比v哦it你听过和功能更为重要的时候. 在这种情况下,应该使用抽象类来定义类型,但是前提必须理解并且可以接受这些局限性. 如果你你方便吗吧一个重要的接口, 就应该坚决考虑同时提供骨架实现类.最后,应该尽可能谨慎地设计所有的公有接口,并通过编写多个实现来对它们进行全面的测试.
public abstract class AbstractMapEntry<K,V> implements Map.Entry<K,V>{
public abstract K getKey();
public abstract V getValue();
public V setValue(V value){
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object o){
if(o == this)
return true ;
if(! (o instanceof Map.Entry))
return false ;
Map.Entry<?,?> arg = (Map.Entry) o ;
return equals(getKey(), arg.getKey()) && equals(getValue(),arg.getValue());
}
private static boolean equals(Object o1, Object o2){
return o1==null ? o2 == null : o1.equals(o2);
}
@Override
public int hashCode(){
return hashCode(getKey() ^ hashCode(getValue()));
}
private static int hashCode(Object o){
return o == null ? 0 : o.hashCode();
}
}
嵌套类 nested class
四种嵌套类
- 静态成员类 static member class
- 非静态成员类 nonstatic member class
- 匿名类 anonymous class
- 局部类 local class
静态成员类 static member class
静态成员类是最简单的一种嵌套类. 最好看作是普通类, 声明在另一个类的内部,可以访问外部类所有成员,包括私有成员.静态成员类是外部类的一个静态成员, 与其他的静态成员一样,也遵守同样的可访问性规则 . 如果它被声明为私有, 它就只能在外部类的内部才能访问 .
静态成员类 static member class 一种常见的用法是作为辅助类, 与它的外部类一起使用才有意义 . 如 枚举 描述了计算器的各种操作 . Operation 枚举应该是Calculator类的共有静态成员类, 然后, Calculator 类的客户端就可以用Calculator.Operation.PLUS , Calculator.Operation.MINUS 这样的命名来引用这些操作 .
package com.cn.mark.effctive;
/**
* Created by mark on 9/3/16.
*/
public enum Operation {
PLUS("+") {
@Override
double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
@Override
double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
@Override
double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
@Override
double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
abstract double apply(double x, double y);
public static void main(String[] args) {
for (Operation op : Operation.values())
System.out.println(op.apply(0.2, 0.5));
}
}
从语法上来讲,静态成员类和非静态成员类之间唯一区别是,静态成员类的声明中包含修饰符static , 尽管他们呢的语法相似,当有着很大的不同 , 非静态成员类的每个实例都隐藏这与外围类的一个外围实例 enclosing instance 的关联 . 在非静态成员类的实例方法内部,可以调用外围实例上的方法, 或用this构造外围引用 . 如果嵌套类的实例可以在它外围类的实例之外独立存在, 那么这个嵌套类就必须是静态成员类 , 在没有外围实例的情况下 , 建立非静态成员类的实例是不可能的 .
## 非静态成员类 nonstatic member class
被创建的时候,它和外围实例之间的关联也建立起来,这种关联不可修改. 一般当在外围类的莫个实例方法的内部调用非静态成员类的构造器时,这种关联关系被自动建立起来. 使用表达式 enclosingInstance .new MemberClass(args) 来手动建立这种关联关系也是可能的 . 这种关联关系需要消耗非静态成员类的实例空间, 并增加了构造时间开销 .
Map接口往往使用非静态成员类来实现集合视图 collection view
public class MySet<E> extends AbstractSet<E>{
public Iterator<E> iterator(){
return new MyIterator();
}
private class MyIterator implements Iterator<E>{
...
}
}
如果声明成员类不要求访问外围实例,就要始终把static修饰符放在它的声明中, 使他成为静态成员类,非静态成员类在保存这份引用要消耗时间与空间,并且导致外围实例在符合垃圾回收却仍然被保留着 . 如果没有外围实例情况下,也需要分配实例,就不能使用非静态成员类,因为非静态成员类的实例必须要一个外围实例.
私有静态成员类的一种常见用法是用来代表外围类代表的对象的组件,如Map实例,key , value 是关联在一起. 许多map内部由一个Entry对象 , 对应于Map中的每个 key value. 虽然每个entry都与一个Map关联但是 entry上的 getKey , getValue … 方法不需要访问map对象 , 这时使用非静态成员类表示 entry 是很浪费的 , 私有静态成员类是最佳的选择 .
static class Entry<K,V> implements Map.Entry<K,V> {
...
}
匿名类 anonymous class
没有名字的类,不能instanceof 测试 .
Arrays.sort(stringArray, new Comparator<String>(){
public int compare(String s1 , String s2){
return s1.length() - s2.length();
}
})
或接口中的匿名类
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
局部类 local class
在使用方法中局部时,有一点注意:这个局部类的对象是可以在方法内返回的,即 即使方法完成这个类对象也不销毁,它的销毁是由垃圾回收器来完成的.但由于我们无法获得这个局部类,我们无法将调用这个返回的类对象.
public static void main(String[] args) {
//局部类
class T extends Thread{
String name;
T(String name){
this.name=name;
}
public void run(){
while(true){
int seconds=2;
long f=System.currentTimeMillis()+seconds*1000;
while(System.currentTimeMillis()<f);
System.out.println("...---->娃哈哈"+name);
}
}
}
for(int i=0;i<10;i++){
//创建局部类,并调用其方法
new T(""+i).start();
}
}