建议 107 : 使用反射增加装饰模式的普适性
这里虽然不复杂,但是这个例子比较经典。所以记录下来
public interface Animal {
public void doStuff();
}
public class Rat
implements Animal{
@Override
public void doStuff() {
System. out.println( "jerry will play with tom");
}
@Override
public void doStuff() {
System. out.println( "jerry will play with tom");
}
}
下面为Rat增加能力。
public interface Feature {
public void load();
public void load();
}
public class FlyFeature
implements Feature{
@Override
public void load() {
System. out.println( "增加一只翅膀");
}
@Override
public void load() {
System. out.println( "增加一只翅膀");
}
}
public class DigFeature
implements Feature{
@Override
public void load() {
System. out.println( "增加钻地能力");
}
@Override
public void load() {
System. out.println( "增加钻地能力");
}
}
接下来是装饰类,将Feature装饰到Rat身上。
public class DecorateAnimal
implements Animal {
private Animal animal;
private Class<? extends Feature> clz;
public DecorateAnimal(Animal _animal,Class<? extends Feature> _clz){
animal = _animal;
clz = _clz;
}
@Override
public void doStuff() {
final InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
if(Modifier. isPublic(method.getModifiers())){
obj = method.invoke( clz.newInstance(),args);
}
animal.doStuff();
return obj;
}
};
ClassLoader cl = getClass().getClassLoader();
Feature proxy = (Feature) Proxy. newProxyInstance(cl, clz.getInterfaces(),handler);
proxy.load();
}
private Animal animal;
private Class<? extends Feature> clz;
public DecorateAnimal(Animal _animal,Class<? extends Feature> _clz){
animal = _animal;
clz = _clz;
}
@Override
public void doStuff() {
final InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
if(Modifier. isPublic(method.getModifiers())){
obj = method.invoke( clz.newInstance(),args);
}
animal.doStuff();
return obj;
}
};
ClassLoader cl = getClass().getClassLoader();
Feature proxy = (Feature) Proxy. newProxyInstance(cl, clz.getInterfaces(),handler);
proxy.load();
}
}
接下来是为Rat增加能力的时候了
public class AnimalTest {
public static void main(){
Animal Jerry = new Rat();
Jerry = new DecorateAnimal(Jerry,FlyFeature. class);
Jerry = new DecorateAnimal(Jerry,DigFeature. class);
Jerry.doStuff();
}
public static void main(){
Animal Jerry = new Rat();
Jerry = new DecorateAnimal(Jerry,FlyFeature. class);
Jerry = new DecorateAnimal(Jerry,DigFeature. class);
Jerry.doStuff();
}
}
这个是一个比较经典的装饰场景,一个层级调用的方式,完成了我们想要增加的功能,代码几乎没有耦合,再增加任何功能的话,也会很容易。
建议:108 反射让模版方法模式更强大。
我们都接触过JUnit单元测试,我们只要写test开头的public,非抽象的方法,在运行的时候,就会顺序执行,
这个就是实现这个的典型案例
public abstract class AbsPopulator {
public final void dataInitaling() throws Exception{
Method[] method = getClass().getMethods();
for(Method m :method){
if(isInitDataMethod(m)){
m.invoke( this);
}
}
}
private boolean isInitDataMethod(Method m) {
return m.getName().startsWith( "test")
&& Modifier. isPublic(m.getModifiers())
&& Modifier. isAbstract(m.getModifiers());
}
public final void dataInitaling() throws Exception{
Method[] method = getClass().getMethods();
for(Method m :method){
if(isInitDataMethod(m)){
m.invoke( this);
}
}
}
private boolean isInitDataMethod(Method m) {
return m.getName().startsWith( "test")
&& Modifier. isPublic(m.getModifiers())
&& Modifier. isAbstract(m.getModifiers());
}
}
这样只要类继承AbsPopulator,运行时执行dataInitaling()就可以了,所有的测试操作都会执行
建议 110:提倡异常封装
这个建议之前确实很少使用,感觉比较有用,所以记录下来,
1.提高系统的友好性(封装的Exception可以指定抛出何种异常,更清晰的反映给用户,例如文件size过大,名字过长等)
2.提高系统可维护性(可以直接定位到具体位置,不需要捋代码)
3.解决Java异常机制自身的缺陷(java只允许一个Exception抛出,这是我们可以把每个Exception放到List中,然后通过我们的封装将Exception队列抛出,常见于判断用户名和密码等数据的校验,如果用户名和密码都有问题,提示用户,就可以避免,第一次提示用户名不对,用户改完,又提示密码不对,这样用户体验很糟糕)
建议 113:不要再Finally中出现return语句,或者修改return返回值/对象,
建议 114:在构造函数中不要抛出异常
建议 115:使用Throwable获得栈信息
public class MyLog {
public static void v(){
StackTraceElement[] str = new Throwable().getStackTrace();
StackTraceElement element = str[ 1];
Log. v( "XPC", "element=" + element.getClassName()+ ":"+element.getMethodName()+ ":"+element.getLineNumber());
}
public static void v(){
StackTraceElement[] str = new Throwable().getStackTrace();
StackTraceElement element = str[ 1];
Log. v( "XPC", "element=" + element.getClassName()+ ":"+element.getMethodName()+ ":"+element.getLineNumber());
}
}
这个在我们搜集堆栈信息的时候,比较有用,
我们可以知道那个方法,那个函数那一行调用的。
com.example.activitytest.MainActivity:onResume:100
建议 116 :异常只为确实异常的事务服务
这个case只能说注意,但是作者的只为有点过于绝对,其实代码是死的,人是活的,我们完全可以在使用的时候,根据特殊的场景来特殊使用,就像之前我遇到的,android中判断一个应用是否安装,如果不存在则...,这个就可以使用这种case来处理,否则非常麻烦。
建议 120 :不使用stop方法停止线程
stop方法停止线程的危害很明显,将会导致线程执行不完,
那么如何停止呢?1.通过标志位
2.通过isInterrupted 和 interrupt方法的组合
如果是线程池的话,则通过shutdown关闭就可以了
建议 121 :线程优先级只使用三个等级
虽然线程优先级有10个等级,但是相邻登记之间,转换为各个系统的等级之后,相差很小,所以为了保证尽量接近我们想要的结果,只使用 MIN_PRIORITY(1) ,(NORM_PRIORITY)5 ,(MAX_PRIORITY)10这三个等级来设置优先级
建议 122 ;使用线程异常处理器提高系统可靠性
这个之前很早就用过,所以我在考虑是否可以全局做一个异常处理接收器,然后各个位置都发出自定义的异常,这样就可以统一处理接收到各个异常时,我们要做什么操作了
建议 123 :虽然volatile是直接从主内存中写入和读取的,但仍然不是线程安全的,只是每次获取最新数据
建议:124 异步运算考虑使用Callable接口
当采用异步运算的时候,当我们需要监听线程运算状态的时候,可以结合 Future 和 Callable的组合来实现,
例如当需要线程执行的时候,显示一个进度条,就可以采用这种模型。
建议 125 尽量使用线程池
这里主要介绍的和学习的应该是线程池的实现原理,归根结底很简单。
第一次submit的时候,创建足够多的线程池,
然后从一个BlockingQueue里面获取任务,当没有任务,则阻塞,这样即使当没有任务的时候,也是有一些线程存在并一致处于block状态的,但是当有任务到来的时候,直接执行,省去了创建和销毁的开销。
建议 126 适时选择不同的线程池来实现
corePoolSize : 最小线程数,线程池启动后保持线程的最小数量,但是需要注意的是线程数辆是逐渐达到
corePoolSize的,第一次当需要多少就会启动多少。
maximumPoolSize:最大数量线程
keepAliveTime:线程最大声明期,当超过corePoolSize的线程等待时间达到keepAliveTime的时候,销毁线程
unit:时间单位
workQueue:任务队列
threadFactory:线程工厂,定义如何启动一个线程,名字/是否是后台线程等
handler:拒绝任务处理器,当线程超过
maximumPoolSize时,由它来处理
newSingleThreadExecutor
newCacheThreadExecutor
newFixedThreadPool
根据含义查看源码,功能很简单
建议 127 显式锁 VS 内部锁
1.Lock 支持更细粒度的所控制,参考:ReentrantReadWriteLock();
2.Lock是无阻塞锁,synchronized是阻塞锁,
3.Lock可实现公平锁,synchronized只能实现非公平锁(当A线程解锁,B,C线程随机持有锁,并获取执行权,为非公平锁,Lock可以在参数中加入true,声明出公平锁)
4.Lock是代码级的,synchronized是JVM级的
建议 131 :CyclicBarrier 让多线程齐步走
使用场景,多个线程同时到达起点之后,统一开始干活:
public class Worker
implements Runnable{
private CyclicBarrier cb;
public Worker(CyclicBarrier _cb){
cb = _cb;
}
@Override
public void run() {
try {
Thread. sleep( new Random().nextInt( 1000));
Log. v( "XPC", Thread. currentThread().getName() + "-到达汇合点");
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch ( BrokenBarrierException e) {
e.printStackTrace();
}
}
private CyclicBarrier cb;
public Worker(CyclicBarrier _cb){
cb = _cb;
}
@Override
public void run() {
try {
Thread. sleep( new Random().nextInt( 1000));
Log. v( "XPC", Thread. currentThread().getName() + "-到达汇合点");
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch ( BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierTest {
public static void test(){
CyclicBarrier cb = new CyclicBarrier( 2, new Runnable() {
@Override
public void run() {
Log. v( "XPC", " 隧道已经打通!");
}
});
new Thread( new Worker(cb), "工人1").start();
new Thread( new Worker(cb), "工人2").start();
}
public static void test(){
CyclicBarrier cb = new CyclicBarrier( 2, new Runnable() {
@Override
public void run() {
Log. v( "XPC", " 隧道已经打通!");
}
});
new Thread( new Worker(cb), "工人1").start();
new Thread( new Worker(cb), "工人2").start();
}
}
LOG:
07-21 01:12:05.034 V/XPC: 工人1-到达汇合点
07-21 01:12:05.374 V/XPC: 工人2-到达汇合点
07-21 01:12:05.374 V/XPC: 隧道已经打通!
可以看到线程1执行到await的时候,不不会执行run方法,而是扽线程2也执行了await方法的时候,才开始执行,其实这个需求使用CountDownLatch也可以实现,只是这个会逻辑更加清晰和简单
建议146 注释:
四种有效的注释
1.版权注释
2.解释意图的注释
3.警告的注释
4.todo的注释
建议 147:让接口的职责单一:
合并接口
建议 149 :依赖抽象而不是实现:
依赖抽象可以将我们的代码解耦合。
到这里 151个建议粗略的看了一遍,吸收了一些东西,以后再经常回头看看吧。