public class Service{ private Service(){};//Prevents instantiation private static final Map<String,Provider> providers = new ConcurrentHashMap<String,Provider>(); //Provider registraion API public static void registerDefaultProvider(Provider p){ registerProvider(name,p); } public static void registerProvider(String name,Provider p){ providers.put(name,p); } //Provider unregistration API public static void unreginsterProviders(){ providers.remove();//不穿参数全部清空 } public static void unreginsterSelectProvider(String name){ providers.remove(name);//移除某个 } //Service asscess API public static Service newInstance(){ return newInstance(DEFAULT_PROVIDER_NAME); } public static Service new Instance(String name){ Provider p = providers.get(name); if(p==null) throw Exception; return p.newService(); } } 复制代码
Map<String,List<String>> m = new HashMap<String,List<String>(); ==>结合泛型 Map<String,List<String>> m = HashMap.newInstance(); public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V>; } 复制代码
ValueOf ---返回实例和其参数具有相同值,类型转换方法 of --- valueOf 简写 getInstance --- 唯一的实例 newInstance --- 返回不同的实例 getType --- 返回对象类型 newType --- 返回不同的对象类型 复制代码
创建带参数实例选择 | 优点 | 缺点 |
---|---|---|
重叠构造器 | 适合<=3参数确定,如自定义view,开销低 | 参数过多时,可读性差,混乱和出错 |
JavaBean模式 | 可读性好,多参不容易出错 | 出现不一致状态,违法类的不可变原则,线程不安全 |
Builder模式 | 适合>3,可读性强,灵活,参数约束,可扩展 | 额外开销,重复代码 |
//使用重叠构造器 mNativeAdLoader = new NativeAdLoader(mContext,unitId,"ab:123",超时时间); //定义重叠构造器 public class NativeAdLoader{ public NativeAdLoader(Context context,String unitId){ this(context,unitId,""); } public NativeAdLoader(Context context,String UnitId,String stragegy){ this(context,unitId,stragegy,0) } public NativeAdLoader(Context context,String unitId,String stragegy,long timeout){ this.context = context; this.unitId = unitId; this.stragegy = stragegy; this.timeout = timeout; } } 复制代码
//使用JavaBean mNativeAdLoader = new NativeAdLoader(); mNativeAdLoader.setContext(mContext); mNativeAdLoader.setUnitId(unitId); mNativeAdLoader.setStrategy("ab123"); mNatvieAdLoader.setTimeout(超时时间); //定义JavaBean public class NativeAdLoader{ public NativeAdLoader(){} public void setContext(Context context){ this.mContext = context; } public void setUnitId(String unitId){ this.unitId = unitId; } public void setStrategy(String strategy){ this.strategy = strategy; } public void setTimeout(long timeout){ this.timeout = timeout; } } 复制代码
//使用构键器 mNativeAdLoader = new NativeAdLoader.Builder(mContext,"unitId") .forNativeAdSourcesByStrategy("ab:123",超时时间) .build(); //定义构建器 public class NativeAdLoader{ public static class Builder{ //必要参数 protected Context mContext; private String mUnitId; //可选参数 private String mStrategy; private long mTimeout public Builder(Context context,String unitId){ this.mContext = context; this.mUnitId = unitId; } public Builder forNativeAdSourcesByStrategy(String strategy,long timeout){ this.mStrategy = strategy; this.mTimeout = timeout; return this; } public NativeAdLoader build(){ return new NativeAdLoader(this) } } public NativeAdLoader(Builder builder){ this.context = builder.context; this.unitID = builder.unitID; this.strategy = builder.strategy; this.timout = builder.timeout; } } 复制代码
//Singleton with static factory public class AdsManager{ //private(私有不可直接引用) + static(方便静态方法) + final(最终对象)+ 类加载时实例化 private static final AdsManager INSTANCE = new AdsManager(); private AdsManager(){};//私有化 public static AdsManager getInstance(){//getInstance 表明该对象的单例性 return INSTANCE; } //Remainder ommitted } 复制代码
解决多线程不安全问题(懒加载)
public class AdsManager{ private static transient AdsManager mInstance;//瞬时 private AdsManager(){}; public static AdsManager getInstance(){ if(mInstance == null){ synchronize(AdsManager.class){ if(mInstance ==null){ mInstance = new AdsManager(); } } } return mInstance; } } 复制代码
完美枚举写法(java 1.5上)
public enum AdsManager{ INSTANCE; } //简洁可读,防止多次实例化,提供序列化机制,无法反射攻击 复制代码
//工具类或者不想要被实例化的类 public class UtilsClass{ //将默认的构造器私有化并直接抛异常 private void UtilsClass(){ throw new AssertionError(); } //Remainder omitted } 复制代码
String s = new String("123"); ==> String s = "123"; //Date,Calendar,TimeZone 只创建一次 Class Person{ private final Date birthDate; private static final Date BOOM_START; private static final Date BOOM_END; //静态代码块写法,类加载仅执行一次 static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(...); BOOM_START = gmtCal.getTime(); getCal.set(...); BOOM_END = gmtCal.geTime(); } //坏处如果该方法不用,就会依然创建以上对象 public boolean isBabyBoomer(){ return birthDate.compareTo(BOOM_START)>=0&& birthDate.compareTo(BOOM_END)<0; } } 复制代码
public static void main(String[] args){ Long sum = 0L;//申明时特别注意,避免使用装箱 for(long i=0;i<Integer.MAX_VALUE;i++){ sum += i; } System.out.println(sum); } 复制代码
//栈实现类 public class Stack { private Object[] elements; private int size=0; private static final int DEFAULT_INITIAL_CAPACITY=16; public Stack(){ elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e){ ensureCapacity(); elements[size++] = e; } public Object pop(){ if(size==0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null;//eliminate obsolete reference return result; } private void ensureCapacity(){ if(elements.length == size){ elements = Arrays.copyOf(elements,2*size+1); } } } //什么是过期引用,活动部分,非活动部分? //垃圾回收机制会自动回收非活动部分内存,而程序员要做的就是将该过期引用对象告诉垃圾,这是非活动部分。 复制代码
1. WeakHashMap
代表弱引用缓存(缓存项过期自动删除,缓存项生命周期由该键的外部对象决定时)
2.不容易确定缓存的生命周期,缓存随着时间推移变得越来越没价值,定时清除 Timer
或 ScheduledThreadPoolExecutor
3.缓存添加新条目时清理, LinkedHashMap.removeEldestEntry
4.更复杂直接使用 java.lang.ref???
1.没有显式的取消注册
2.不确定什么时候取消,只保存回调的弱引用,常见保持成WeakHashMap中的键
//io流,connection,DB等 try{ //do something }catch(Exception){ }finally{//must do .close .cancel .terminate(); } 复制代码
自反性(reflexive) x!=null&&x.equals(x) ==>true 对称性(symmetric) x!=null&&y!=null && x.equals(y) ==>y.equals(x) 传递性(transitive)x,y,z!=null && x.equals(y)&&y.equals(z) ==>x.equals(z) 一致性(consistent)x,y!=null &&x,y not change ==>x.equals(y) always true/false 非null性 (nevernull)x!=null && x.equals(null) ==>false 复制代码
== 代替 equals ,是时候降低成本了
使用instanceof操作检查类型
参数转换类型(默认进行了instanceof测试)
这样写(先比较最有可能不一样的)
Double.compare Arrays.equals field == null? o.field == null : field.equals(o.field); 如果field 和 o.field 是相同对象的引用,更快 field==o.field || (field!=null&&field.equals(o.field)) 复制代码
写完equals方法自问自己,是否对称,传递,一致
覆盖equals总要覆盖hashCode
不要将equals申明的Object替换成其他类型
//没有覆盖Object.equals public boolean equals(MyCleass o){ ... } Override 注解好处就会告诉你,编你不过 复制代码
1.不这样违反Object.hashCode的通用约定(如果两个对象的equals方法是比较相等的,那么调用这两个对象任意一个hashCode方法必须产生相同的整数结果)
2.不这样导致该类无法结合基于散列的集合正常工作,比如HashMap、HashSet、HashTable
class PhoneNumber{ private final short areaCode; private final short prefix; private final short lineNumber; public PhoneNumber(...){ //igore construct } @Override public boolean equals(Object O){ //igore equals } //Broken - NO HashCode Method } Map<PhoneNumber,String> m = new HashMap<PhoneNumber,String>(); m.put(new PhoneNumber(123,456,789),"lizhaoxiong"); ==>m.get(new PhoneNumber(110,456,789)) 会返回null *** ==>m.get(new PhoneNumber(123,456,789)) 会返回"lizhaoixong"吗?待test 如果一个对象的属性值发生变化,那么这个对象的hashCode值还相同吗?test 复制代码
@Override public int hashCode(){ int result = 17; result = 31*result + areaCode; result = 31*result + prefix; result = 31*result + lineNumber; resutl result; } 思路: 随便找个int 数; 不同域转换int: boolean(f?1:0)| byte,short,int (int)f |(int)(f^(f>>>32)) |Float.floatToIntBits(f)|Double.doubleToLongBits(f)+Float.floatToIntBits(f)| |对象,递归调用equals和hashCode。|数组,Arrays.hashCode,递归每个元素hashCode再组合排列 公式:result = 31*result+c; 1.为什么是31? 移位和减法代替乘法,JVM优化性能(31*i = (i<<5)-i) 2.直接result int的常亮值的坏处? 线性时间、平方级时间?降级了,散列表 ==> 链表 3.怎么使用延迟初始化? if(result=0)才进行计算,上面代码 4.什么是散列函数? 5.String、Integer、Date的hashCode方法返回的是什么确切值?这样的坏处? 复制代码
1.虽然这不是强制的,但这是规范。
2.如果不覆盖试着比较下 类名@16进制散列 VS 自定义(简洁、信息丰富、易于阅读)
3.toString应该返回值得 关注的信息 或 诊断信息
4.toString 要么返回统一规范化格式信息 要么你注释说明好
提供一种无需调用构造器就可以创建对象思路
拷贝的精确含义取决于该对象的类
要把这个搞懂,必须形成一个专题,就目前而言没有多大意义
1.compareTo没有在Object声明
2.compareTo 是Comparable接口唯一方法,实现该接口比较对象数组Array.sort(a)
比较返回的是int类型值
3.当比较对象类型不同时直接抛出ClassCastException不进行比较,equals返回false
4.Comparable接口是参数化的,comparable方法是静态类型,不必进行类型转化和类型检查,有问题直接无法编译,如果参数null,直接NullPointException
5.CompareTo方法中域的比较是顺序比较而不是等同性比较。
由于Java平台的所有值类都实现该接口,当你在编写一个值类需要明显的内在排序关系,比如字母、数值、时间等,坚决考虑实现该接口
public interface Comparable<T>{ int compareTo(T t); } 复制代码
自定义Comparator专门用于你自定的排序
int 比较><= ,float和double用Double.compare/Float.compare
多个关键域比较,从最关键的开始,逐步进行到所有重要域,出现非0结果结束返回该结果
public int compareTo(People p){ if(inner < p.inner) return -1; if(inner > p.inner) return 1; if(out < p.out) return -1; if(out > p.out) return 1; ... return 0; } //确定差值不大于INTERGER.MAX_VALUE 2^31 -1 public int compareTo(People p){ int diffInner = inner -p.inner; if(diffInner!=0) return diffInner; int diffOut = out - p.out; if(diffOut!=0) return diffOut; return 0; } 复制代码
尽可能地降低可访问性
Android 和 java 的公有类的成员变量编写到底是追求简洁还是安全
class Point { public int x; public int y; } VS class Point { private int x; private int y; public Point (int x,int y){ this.x = x; this.y = y; } public void setX(int x){ this.x = x; } public void setY(int y){ this.y = y; } public int getX(){return this.x} public int getY(){return this.y} } //构建时约束条件 public Point (int x, int y){ if(x<0&&y<0){ throw new IllegalArgumentException("x,y" + x + y) } this.x = x; this.y = y; } 复制代码
如何把类变成不可变类?(参照String、BigInteger源码)
不对外提供修改对象状态的任何方法
保证类不会被扩展,比如声明final类和private构造器(valueof)
声明域都是final
声明域都是private
使用函数对操作数运算并不修改它
public Complex add(Complex c){ return new Complex(x+c*x,y+c*y) } 复制代码
为什么要使用不可变类?
相比可变类的复杂空间和不确定性,不可变稳定可靠
不可变对象本质是线程安全的,不要求同步
//为了鼓励多使用,应该这样写 public static final Complex ZERO = new Complex(0,0); public static final Complex ONE = new Complex(1,0); ==>近一步单例化,提高内存占用 复制代码
复制代码
对象可以被自由共享(剔除拷贝构造器)
内部信息共享
Building blocks
就算是可变类,也要让其可变性最小(比如TimerTask状态只有执行和取消)
唯一缺点:创建这种对象代价可能很高,为提高性能使用 可变配套类
可变配套类 String 的可变配套类是StringBuilder和淘汰的StringBuffer BigInteger 是BitSet 复制代码
Why?
复合/转发?
想要给现有类增加一个功能/特点,在新类中增加一个私有域,它引用现有类的一个实例,使现有类成为新类的一个组件,这样的设计叫做复合(composition) 新类的每个方法都可以调用被包含的现有类的实例对应的方法,并返回它的结果,称为转发(forwarding) 在转发的基础上,返回结果时做定制化处理,这就是装饰/包装(wrapper) 复制代码
```java Class A { public void methodA(){}; public void methodB(){}; }
Class BWrapper { private A a; public BWrapper(A a){this.a=a};
public void wrapA(){ a.methodA(); } public void wrapB(){ a.methodB(); } public void wrapC(){ a.methodA(); a.methodB(); } 复制代码
}
包装类不适合回调框架(SELF问题) 慎用继承! 用继承前问问自己,两者 is-a的关系吗?每个B确实也是A吗?不能确定就不该扩展A。那么就让B应该包含A的一个私有实例,并且暴露较小、简单的API,A本质不是B的一部分,只是它的实现细节。 (Java 违反这一原则的,栈Stack不是向量vector,属性列表Properties不是散列表Hashtable) #### 要么为继承而设计提供文档说明,要么禁止继承 为什么写基类的要比写实现的值钱? 好的API文档应该描述给定方法<u>做了什么工作</u>,而不是描述<u>如何做到的</u> 使用此类的方法要注意什么,应该干什么,不能干什么 类必须通过某种形式提供适当的钩子(hook) 编写基于继承的基类后一定要编写子类进行测试 构造器决不能调用可被覆盖的方法 对于并非为了安全子类化而设计的类要禁止子类化,有几种方法 1. 声明类为final 2. 构造器私有化或者包级私有并增加公有静态工厂代替构造器(单例) 3. 利用包装类模式代替继承实现更多功能 #### 接口优于抽象类 **区别** - 抽象类可包含实现,接口不允许 - 抽象类依赖继承进行子类实现,由于Java单继承原则丧失灵活性 - 接口灵活,扩展性强,并且非层次。高手用继承,多用接口好 **包装类设计模式体现着点** **多接口模拟多重继承** **骨架实现**(先设计公有接口,可能抽象的基类简单的公有实现,具体子类实现) = 简单实现+抽象类 **公有接口设计切记**:多多测试接口,不然一旦上线发行,就不好修改了 #### 接口只用于定义类型 **常量接口模式** 实现细节泄漏API、可能后期无价值、污染代码(反面教程ObjectStreamConstants) **有固定常量的需求** 1. 枚举类型 2. 不可实例化的工具类(大量引用类名) 3. 静态导入 #### 类层次优于标签类 #### 函数对象表示策略 函数指针 策略模式 元素排序策略:通过传递不同比较器函数,就可以获得各种不同的排列顺序 函数对象:Java没有提供函数指针,使用对象引用实现同样功能,这种对象的方法是对其他对象的操作, 且一个类导出一个方法,它的实例实际等同于指向该方法的指针,这样的实例叫做函数对象。 #### 优先考虑静态成员类 **嵌套类有四种:** **静态成员类(非内部类)**: 特点— 理解为恰好声明到类里面的普通类,可访问外围类的成员,声明私有也可供外围类内部使用 场景— 公有辅助类,像枚举,像Calculator.Operation.MINUS **非静态成员类(内部类)**: 特点— 每个实例都包含一个额外指向外围对象的引用,非静态成员类强依赖外围类,并在发行版本中不可由非静转静 场景— Iterator、HashMap的Entry **匿名类(内部类)**: 特点— 没有名字,不是外围类成员,声明时实例化,不可Instanceof,不可扩展,必须简短 场景— 动态创建函数对象,匿名的Sort方法的Comparator、创建过程对象Runnable、Thread、TimeTask 、静态工程内部 **局部类(内部类)**: 特点— 使用少见 场景— 任何可以声明局部变量的地方 经典问题:静态内部类和非静态内部类的区别? ## 泛型 #### 请不要在新代码中使用原生态类型 泛型的作用 — 在编译时期告知是否插入类型错误的对象,而非再运行时期在报错,泛型检查。 泛型定义 — 声明一个多个类型参数的类或者接口,java1.5版本开始支撑,例如List<String>。 原生态类型 — 不带任何类型参数的泛型类型,List<E> 原生态时List,只是为了和引入泛型之前的遗留代码兼容 ```java //未使用泛型 private final Collection students = ...; students.add(new Teather()); for(Iterator i = students.iterator;i.hasNext();){ Student s = (Student) i.next();//Throws ClassCastException } //使用泛型 private final Collection<Student> students = ...; students.add(new Teather()); // 提前Error //for-each 和for for(Student s: students){} for(Iterator<Student> i = students.iterator();i.hasNext();){ Student s = i.next();//No cast necessary } 复制代码
泛型和原生区别 — 泛型检查
泛型子类化 — List 是原生态类型List的子类型,而不是参数化类型List 的子类型
List<String> strings = new ArrayList<String>(); unsafeAdd(strings, new Integer(44)); String s = strings.get(0); //编译通过,但是收到警告 private static void unsafeAdd(List list, Object o){ list.add(o); } //无法编译通过,List<String> 不是List<Object>的子类 private static void unsafeAdd(List<Object> list,Object o){ list.add(o); } 复制代码
无限制通配符类型 — Set<?> 不确定和不关心实际参数类型
无限制通配符类型Set<?>和原生态类型Set之间有什么区别?
安全的!不是什么都能插入Set<?>,需要声明 Extends ?
什么时候使用原生态(泛型信息可以在运行时被擦除) — 1.类文字 2.和instanceof有关
static void newElements(Set s1,Set s2){ for(Object o1 : s1){ if(s2.contains(o1)){...} } } //会报错 static void newElements(Set<?> s1,Set<?> s2){ for(Object o1 : s1){ if(s2.contains(o1)){...} } } //利用泛型来使用instanceof if(0 instanceof Set){ Set<?> m = (Set<?>)o; } 复制代码
非受检警告 — 强制转化、方法调用、普通数组、转化警告
简单 ==>多用泛型,即可消除
难的 ==>@SuppressWarnings("unchecked") //一定要使用确认注释原因
因为类型信息已经被擦除了。
错误发现原则— 能在编译时发现就不要放到运行时发现,泛型列表(擦除—编译时检查类型信息,运行时擦除类型信息)优先于数组(运行时检查元素类型约束),这个就像地铁安检一样,你进入地铁前对你进行检查(泛型列表),而当你已经进入地铁后,再来人对你进行检查(数组),危险极大。
《具体查看Stack源码》 elements = new E[DEFAULT_INITIAL_CAPACITY]; ==> @suppressWarnings("unChecked") elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY]; E result = elements[--size]; ==> E result = (E)elements[--size] ==> @SuppressWarnings("unCkecked") E result = (E)elements[--size]; 总之需要你确认没有安全问题 class DelayQueue<E extends Delayed> implements BlockingQueue<E>; E被称之为有限制的类型参数 每个类型都是它的子类型 复制代码
public static <E> Set<E> union(Set<E> s1,Set<E> s2){ Set<E> result = new HashSet<E>(s1); result.addAll(s2); return result; } //消除代码chenyu Map<String,List<String>> ana = new HashMap<String,List<String>>(); ==> Map<String,List<String>> ana = newHashMap(); public static <K,V> HashMap<K,V> newHashMap(){ return new HashMap<K,V>(); } //递归类型限制Comparable接口 public interface Comarable<T>{ int compareTo(T o); } //根据元素的自然顺序计算列表的最大值 public static <T extends Comparable<T>> T max(List<T> list){ Iterator<T> i = list.iterator(); T result = i.next(); while(i.hasNext()){ T t = i.next(); if(t.compareTo(result)>0){ result = t; } } return result; } 复制代码
泛型参数类型特点?
List 不是List的子类,但自己是自己的子类
提高灵活性的两种方式?
合理的选择严格的参数类型、生产者参数、消费者参数,包装类型安全和灵活性
public class Stack<E> { public void push(E e); public E pop(); } + pushAll + popAll //子类无法push进去,由于Integer是Numble的子类,但是Integer插入不了声明为Numble的 public void pushAll(Iterable<E> src){ for(E e: src){ push(e); } } 有限制的通配符类型==> public void pushAll(Iterable<? extends E> src){ for(E e: src){ push(e); } } //如果声明E是Object,由于Object是Numble的父类,但是Numble却无法调用Object的 public void popAll(Collection<E> dst){ while(!isEmpty()){ dst.add(pop()); } } 有限制的通配符类型==> public void popAll(Connection<? super E> dst){ while(!isEmpty()){ dst.add(pop()) } } //升级eg1: static <E> E reduce(List<? extends E> list ,Function<E> f); //升级eg2:返回类型仍然为Set<E> ,不要用通配符类型作为返回类型,除非非常强调灵活性 public static <E> Set<E> union(Set<? extends E> s1,Set<? extends E> s2) //升级eg3: public static <T extends Comparable<? super T>> T max(List<? extends T> list) //注意类型匹配: max(List<? extends T> list){ Iterator<? extends T> i = list.iterator(); } //无限制的类型参数,不能将元素放回刚刚从中取出的列表 public static void swap(List<?> list,int i,int j){ list.set(i,list.set(j,list.get(i))); } ==> 调用swapHelper(List<E> list,int i,int j) 复制代码
集合(Set、Map、List)、单元素容器(ThreadLocal、AtomicReference)
Map<Class,Object> favorites = new HashMap,Object>()
为什么?
1.多数组最好转化为Map枚举集合+泛型设计!
2.EnumMap内部使用了通过序数索引的数组,并隐藏了实现细节
3.数组最大的问题是无法保证序数和数组索引之间的关系
4.嵌套的EnumMap关系(142页)EnumMap<..., EnumMap<...>>。
5.使用Enum.ordinal是傻叉
//将所有花园的花根据种类打印出来 //1. 打印的map、key是type、vlaue是花对象集合(定义) //2. 遍历原有集合(type),重写put //3. == 遍历花园得到花对象 Map<Flower.Type, Set<Flower>> flowersByType = new EnumMap<Flower.Type, Set<Flower>>(Flower.Type.class); for(Flower.Type t: Flower.Type.values()) { --values方法可以这样吗?返回的是key对应的对象吗? flowersByType.put(t,new HashSet<Flower>()); } for(Flower f:garden){ flowersByType.get(f.type).add(f); } System.out.println(flowerByType); 复制代码
ordinal方法返回每个常量类型的数字位置(序数)
该方法被设计给EnumSet和EnumMap使用,请完全避免自行使用ordinal方法
// 错误事例 public enum Ensemble{ ONE,TWE,THTREE...; public int numberOfDay(){ return ordinal()+1; } } // 正确事例 public enum Ensemble{ ONE(1),TWE(2),THREE(3)...; private int num; Ensemble(int day){num = day} public int numOfDay(){return num;} } 复制代码
为什么?
// bad code public class Text{ public static final int STYLE_BOLD = 1 << 0; //1 public static final int STYLE_ITALIC = 1 << 1; //2 public void applyStyles(int styles){...} ==> text.applyStyles(STYLE_BOLD | STYLE_ITALIC); } //nice code public class Text{ public enum Style{BOLD,ITALIC... } public void applyStyles(Set<Style> styles){...} ==> text.applyStyles(EnumSet.of(Style.BOLD,Style.ITALIC)); } 复制代码
1.基本数据类型的枚举如何通过扩展枚举实现呢
2.通过Collection<? Extends Operation> 这个有限制通配符类型
3.虽然无法编写可扩展枚举类型,但可以通过编写接口即实现该接口实现进行模拟
命名模式的3个缺陷:
注解类型:
@Retention(RetentionPolicy.RUNTIME) -- 运行时保留 @Target(ElementType.METHOD) -- 作用域,在方法中才合理 public @interface Test {} //Retention(生命范围,源代码,class,runtime) //Documented,Inherited,Target(作用范围,方法,属性,构造方法等) 复制代码
防止由于重载或者或者无意识地覆盖产生的覆盖(有覆盖需求就加上吧)
到底是使用标记接口还是标记注解?
参考:Serializable标记接口(被序列化接口)
如何处理参数和返回值?
如何设计方法签名?
如何为方法编写文档?
--专注可用性、健壮性、灵活性
1.错误发生前检查参数有效性,降低调试工作难度
2.使用@throws标签(tag)标明违反参数值限制会抛出的异常,如ArithmeticException
IllegalArgumentException、IndexOutOfBoundsException、NullPointerException
3.使用断言做有效性检查
4.只要有效性检查有一次失败,那么你为此做的努力便可以连本带利的偿还
/** * @throws NullPointerException if object is null */ public static <T> T requireNonNull(final T object, final String message) { if (object == null) { throw new NullPointerException(message); } return object; } 复制代码
不要使用Clone进行保护性拷贝
保护性拷贝是在检查参数的有效性前进行的
Date类事可变的,把它交给如Date.getTime()返回long进行时间表示
重载是在编译时期决定的 -- 重载方法当方法名称相同,在调用时容易造成调此非此。
如何避免胡乱使用重载 -- 永远不要使用两个具有相同参数数目的重载方法。
在创建多个功能相似的,仅仅传入参数类型不同的方法时,尽可能通过方法命名区分而非参数类型区分。
编写API时,请不要让调用程序员感到混淆的,调用不明确,避免重载的导致难以发现的错误。
覆盖是运行时期的,覆盖更加安全。-- so 避免编译时的类型影响
public static String classify(Collection<?> c){ return c instanceof Set ? "Set" : c instanceof List ? "List" : "Unknow Collection" } 复制代码
当我们编写方法,明确了参数的类型但是不确定参数的数量的时候,就用可变参数
可变参数的实现本质sum(int... args) 就是sum(int [] args)
==>先创建一个数组,数组的大小为在调用方法传递的参数数量
//可变参数解决无参数问题 public static int min(int firstArg,int... args){ int min = firstArg; for(int arg: args){ min = arg < min?arg:min; } return min; } 复制代码
可变参数是为printf而设计,最主要的作用体现在printf和反射机制
Arrays.asList ==> Arrays.toString() 复制代码
从性能角度,可变参数每次的调用都会导致一次数组的分配和初始化,so 灵活性 VS 性能
每次都得为空指针考虑,所以请尽量不要返回null,除非你是有意识的
永远不会分配零长度数组的代码写法
//返回零长度的数组 private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0]; public Cheese[] getCheeses[]{ return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY); } //返回空集合 return Collections.emptyList(); //具体参考Collections的EmptyList系列的几个内部类 复制代码
How to Write Doc Comments
为每个被导出的类、接口、构造器、方法、域声明增加文档注释
方法的文档注释:
,并且由标签开头的文字不用句点结束
{@code index>=0}
概要描述(Summary Description)-- 文档注释的第一句话,对你写的API一句话描述体现你的总结能力了
更多说明:
讨论局部变量的处理、控制结构、类库的用法、各种数据类型的用法以及reflection 和 native method的用法
及优化和命名惯例
遍历集合和遍历数组for循环是比while循环更好,但是由于 迭代器和索引变量 的存在很容易出错
使用for-each循环完全隐藏迭代器和索引变量,适合集合和数组
性能上稍有优势,它对数组的索引边界值只是计算一次
嵌套遍历时,内外数量不一致会抛出NoSuchElementException,嵌套循环可能需要在第一层循环内把遍历的值记录下来,而for-each完美的处理了嵌套的问题
如果要编写类型表示一组元素,即使不让其实现Collection,也要让其实现Iterable,这样就可以使用for-each
过滤 -- 要删除指定元素调用remove方法(修改)
替换 — 遍历列表或数组时,要取代替换元素时 (修改)
平行迭代
Random三个缺点 ==> 伪随机数生成器 Random.nextInt(int) ,So
advise!
一种办法是复杂的使用BigDecimal,不方便较慢,要自己使用十进制小数点(货币)
还有一种办法,是使用int(简单)或者long(复杂)
Java分为基本数据类型和引用类型,每个基本数据类型都有一个引用类型
区别?
对于引用数据类型使用==操作符总是错误的
请把能声明成基本数据类型变量,避免反复的装箱和拆箱,导致性能下降
什么时候使用引用类型呢?
不适合使用字符串的场景:
当是简单输出或者显示使用字符串连接 +
但当拼接的量级别变大,或者使用for循环进行拼接请使用 StringBuilder代替String
多使用StringBuilder的append方法
更一般的讲,如果有合适的接口类型存在,那么参数、返回值、变量和域都应该使用接口类型声明
养成接口作为类型的好习惯吧,使你的程序更灵活吧
Vector<Subscriber> subscribers = new Vector<Subscriber>(); ==> good code : List<Subscriber> subscribers = new Vector<Subscriber>(); 比如ThreadLocal类的之前的HashMap 要换成 IdentityHashMap那不是一句话的事,而且不用担心影响 复制代码
如果对象的类是 基于类的框架 (abstract class),就应当用相关的基类来引用这个对象,比如TimerTask
反射提供了通过程序访问类的成员名称、域类型、方法等信息的能力
反射的设计初衷:
为了基于组件的应用创建工具设计,这类工具需要装载类。普通应用程序在运行时不应该以反射方式访问对象。目前反射机制使用场景:浏览器、对象监视器、代码分析工具、解释型的内嵌式系统、RPC系统。现实app中当编译时无法获取到的类或者过时方法等
反射的缺点:
使用注意:当你编写程序与编译时的未知类一起工作时
本地方法的三种用途:访问特定平台的机制,注册表和文件锁,访问遗留代码和数据能力,提供系统性能
使用本地方法提高性能时不值得提倡的,原来是可行的现在随着JVM的块和Java平台完善,比如BigInteger
本地语言不是安全的,可能需要编写胶合代码,并且单调乏味和难以阅读
本地代码中的一个Bug可能让你痛不欲生
名人名言:
很多计算机上的过失都被归咎于效率(没有必要达到的效率)
计较效率上的小小的得失,不成熟的优化才是一切问题的根源
在优化方面我们应该遵守两条规则:第一,不要进行优化 第二,没有绝对清晰的优化方案之前,请不要进行优化
API的设计对性能的影响是非常实际的,使用哪些性能优化过的API特别重要,保证接口的灵活性也特别重要
不要费力的编写快速的程序,尽力编写好的程序,速度自然而然就随之而来
借助性能方面的工具是不错的手段
标示符类型 | 例子 |
---|---|
包 | com.xiaoyu.util, org.xiaoyu.dao.impl |
类或者接口 | HttpServlet, AsyncTask(单词首字母大写) |
方法或者域 | toString, equal, isUpperCase(首单词首字母小写,之后单词首字母大写) |
常量域 | IP_ADDR(全部大写,单词之间加下划线) |
局部变量 | stuNumber,mString(与方法命名类似) |
类型参数 | T,E,V,K等等 |
标示符类型 | 例子 |
---|---|
类 | 名称或名称短语,Timer,BufferedWriter,ChessPiece |
接口 | Collection,Comparator,Runn able ,Iter able ,Access ible |
注解 | BindingAnnotation,Inject,ImplementedBy,Singleton |
方法或者域 |
动词或动词短语,append或drawImage,返回boolean is开头 isEmpty,isEnabled 方法返回非boolean对象的函数或属性:speed(),color(),getTime(),getA+setA 转换对象类型:toType,toString,toArray 返回view:asList 返回和调用对象同值基本类型方法:typeValue、intValue 静态工厂的常用名称:valueOf、getInstance、newInstance、getType、newType |
发挥异常优点,提高程序的可读性、可靠性和可维护性,介绍下异常的指导原则
异常的错误作用:
so,异常应该只用于异常的情况,永远不应该用于正常的控制流
“状态测试方法” “可识别的返回值”
Java 程序设计提供三种可抛出结构(throwable)
永远不要定义Exception的子类,只会困扰API的用户
catch 块总是具有断言失败的特征
标准意味着可被重用
可被重用异常排行榜
www.importnew.com/16725.html
最让人困惑的异常莫过于抛出的异常和执行任务没有明显的联系,往往由底层抽象抛出的异常?!
如何避免?— 异常转译
public E get(int index) { try { return listIterator(index).next(); } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); //异常链 try{ ... //Use lower-level bidding }catch(LowerLevelException cause){ throw new HigherLevelException(cause); } 复制代码
当然也不要滥用,可以给低层传递参数前检查高层方法的参数有效性,从而避免底层方法抛出异常
而如果无法避免底层异常,让高层绕开这些异常,隔离!并记录下来
如果失败的情形不容易重现,想要通过系统日志分析会非常困难
so,编写代码时把细节打印出来
当然也不要刻意追求,错就是错了,另外API文档应当清楚的指名对象将会处于什么状态
不考虑后果直接try catch是极其不负责任的行为,起码你解决不了,输出些日志出来也行
Synchronized 同一时刻,只有一个线程可以执行某一个方法或者代码块
互斥的理解:当一个对象呗一个线程修改时,可以阻止另一个线程观察到对象内部不一致的状态(状态一致性)
boolean域的读写操作是原子的,让第一个线程轮询(开始false),通过第二个线程改变(false)表明第一个线程终止自己了
i++不是原子操作
不共享可变数据或者共享不可变数据以及非要共享可变数据就必须执行同步
public class StopThread{ private static boolean stopRequested; private static synchronized void requestStop(){ stopRequested = true; } private static synchronized boolean stopRequested(){ return stopRequested; } main(){ Thread backgroudThread = new Thread( new Runnable(){ public void run(){ int i = 0; while(!stopRequested()) i++; } } ); backgroundThread.start(); TimeUnit.SECOND.sleep(1); requestStop(); } } or ==> private static volatile boolean stopRequested;//就不需要写同步方法了 复制代码
过度同步可能会导致性能降低、死锁、不确定行为
死锁原因:它企图锁定某某,但它无法获得该锁
分析StringBuffer内部同步问题
分拆锁、分离锁、非阻塞并发控制
//代替new thread 的Executor Framework 三步曲 ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(runnable); executor.shutdown(); //轻载的服务器,够用不需要设置啥 Executors.newCachedThreadPool //大负载产品,固定线程数目的线程池 Executors.newFixedThreadPool //最大限度的使用 ThreadPoolExecutor 复制代码
Executor Famework 所做的工作就是执行
Collections Framework 所做的工作就是聚集
使用ScheduledThreadPoolExecutor代替Timer,更灵活。Timer只用一个线程,面对长期任务影响准确性,一旦线程抛出未被捕获的异常,timer就会停止。而executor支持多个线程,并能够优雅的从抛出的未受检异常任务中恢复。
应该用更高级的并发工具来代替 wait和notify(手动太困难)
并发java.util.concurrent:Executor Framework、Concurrent Collection、Synchronizer
并发集合:ConcurrentMap、BlockingQueue(生产者和消费者队列)
同步器:CountDownLatch/CyclicBarrier—倒计时锁存器、Semaphore
System.nanoTime更加准确更加精确,它不受系统的时钟的调整影响(System.currentTimeMills)
线程安全的级别:
私有锁,把锁对象封装在它所同步的对象中(private + final)适用于无条件线程安全
总之,有条件的线程安全必须写文档指名了“哪个方法调用序列需要外部同步,并且获得哪把锁”
双层检查 :减少延迟初始化的性能开销,再加上volatile
不健壮、不可移植性
把堆栈轨迹定向一个特定域应用程序的日志中Thread.setUncaughtExceptionHandler
将一个对象编码成一个字节流,即序列化;相反,即为反序列化。
序列化代理模式
一个类被序列化的直接开销非常低,但是后期的长期开销确是实实在在的,一旦一个类被发布,大大降低这个类实现的灵活性。
旧版本来序列化一个类,在用新版本反序列化,往往会导致程序的失败。
UID — 序列化版本serialVersionUID,如果你没有声明显式的序列化UID,那么一旦类发生改变,那么自动生成的UID也会发生变化,兼容性遭到破坏,运行时导致InvalidClassException异常
反序列化作为隐藏的构造器,具备和其他构造器相同特点,很容易出现Bug和安全漏洞
随着类的新版本发布,相关测试负担加重(确保序列化和反序列化过程成功)
哪些类需要序列化(Date BigInteger、Throwable—异常从服务队传客户端、Component—GUI的发送保持恢复、HttpServlet—回话状态可缓存、存在目的为了融于这种类型的框架)
哪些类不应该实现序列化(thread pool、为继承设计的类+用户接口—为扩展存在的,不然会背负沉重负担)
ReadObjectNoData
内部类不因该是实现序列化
transient表示的变量默认不序列化(瞬时),writeObject和readObject表示具体的序列化形式
@serial标签
StringList
readObject方法相当于公有构造器,如同其他构造器一样需要检查参数有效性,必要时对参数进行保护性拷贝
一个Singleton如果实现Serializable的话,就不再是单例了,除非在readResolve做文章
当不能在客户端扩展类编写readObject或者writeObject时候,考虑使用序列化代理模式
了解规则,看到背后,打破规则,创造新规则