Know Why Never do.html
先知道基本编程基本规则,才知道何时能够打破java
[TOC]程序员
静态工厂提交代码的可读性,而且让调用者没必要为选择什么参数构造器烦恼编程
静态工厂可以避免建立多的重复对象设计模式
静态工厂可以返回那些你自定义该对象的子对象(好比private的,更灵活)数组
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诀窍
== 代替 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 要么返回统一规范化格式信息 要么你注释说明好
提供一种无需调用构造器就能够建立对象思路
拷贝的精确含义取决于该对象的类
要把这个搞懂,必须造成一个专题,就目前而言没有多大意义
compareTo 和 equals 区别?
1.compareTo没有在Object声明
2.compareTo 是Comparable接口惟一方法,实现该接口比较对象数组Array.sort(a)
比较返回的是int类型值
3.当比较对象类型不一样时直接抛出ClassCastException不进行比较,equals返回false
4.Comparable接口是参数化的,comparable方法是静态类型,没必要进行类型转化和类型检查,有问题直接没法编译,若是参数null,直接NullPointException
5.CompareTo方法中域的比较是顺序比较而不是等同性比较。
啥时候用该接口?(collection imp)
因为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") //必定要使用确认注释缘由
数组和泛型的区别?
为何List不是List的超类也不是子类?
由于类型信息已经被擦除了。
为何第一是列表第二是数组?
错误发现原则— 能在编译时发现就不要放到运行时发现,泛型列表(擦除—编译时检查类型信息,运行时擦除类型信息)优先于数组(运行时检查元素类型约束),这个就像地铁安检同样,你进入地铁前对你进行检查(泛型列表),而当你已经进入地铁后,再来人对你进行检查(数组),危险极大。
如何学习编写泛型呢?
《具体查看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;
}
复制代码
泛型参数类型特色?
提升灵活性的两种方式?
PECS(producer-extends,consummer-super)
合理的选择严格的参数类型、生产者参数、消费者参数,包装类型安全和灵活性
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的用法
及优化和命名惯例
Why?
遍历集合和遍历数组for循环是比while循环更好,可是因为迭代器和索引变量的存在很容易出错
使用for-each循环彻底隐藏迭代器和索引变量,适合集合和数组
性能上稍有优点,它对数组的索引边界值只是计算一次
嵌套遍历时,内外数量不一致会抛出NoSuchElementException,嵌套循环可能须要在第一层循环内把遍历的值记录下来,而for-each完美的处理了嵌套的问题
advise!
若是要编写类型表示一组元素,即便不让其实现Collection,也要让其实现Iterable,这样就可使用for-each
没法使用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可能让你痛不欲生
名人名言:
不少计算机上的过失都被归咎于效率(没有必要达到的效率)
计较效率上的小小的得失,不成熟的优化才是一切问题的根源
在优化方面咱们应该遵照两条规则:第一,不要进行优化 第二,没有绝对清晰的优化方案以前,请不要进行优化
advise!
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,Runnable,Iterable,Accessible |
注解 | 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 块老是具备断言失败的特征
标准意味着可被重用
可被重用异常排行榜
最让人困惑的异常莫过于抛出的异常和执行任务没有明显的联系,每每由底层抽象抛出的异常?!
如何避免?— 异常转译
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时候,考虑使用序列化代理模式
了解规则,看到背后,打破规则,创造新规则