mvc mvp mvvm 总结

常用的软件架构模式大概可以分为以下三种:
MVC:Model-View-Controller
MVP:Model-View-Presenter
MVVM:Model-View-ViewModel

  • 三者的共同点,也就是 Model 和 View
  1. Model 就是领域模型,数据对象,同时,提供外部对应用程序数据的操作的接口,也可能在数据变化时发出变更通知。Model 不依赖于 View 的实现,只要外部程序调用 Model 的接口就能够实现对数据的增删改查。
  2. View 就是 UI 层,提供对最终用户的交互操作功能,包括 UI 展现代码及一些相关的界面逻辑代码。
  • 三者的差异在于如何粘合 View 和 Model,实现用户的交互操作以及变更通知

  1. Controller 接收 View 的操作事件,根据事件不同,或者调用 Model 的接口进行数据操作,或者进行 View 的跳转,从而也意味着一个 Controller 可以对应多个 View。Controller 对 View 的实现不太关心,只会被动地接收,Model 的数据变更不通过 Controller 直接通知 View,通常 View 采用观察者模式监听 Model 的变化。
  2. Presenter,与 Controller 一样,接收 View 的命令,对 Model 进行操作;与 Controller 不同的是 Presenter 会反作用于 View,Model 的变更通知首先被 Presenter 获得,然后 Presenter 再去更新 View。一个 Presenter 只对应于一个 View。根据 Presenter 和 View 对逻辑代码分担的程度不同,这种模式又有两种情况:Passive View 和 Supervisor Controller。
  3. ViewModel,注意这里的 “Model” 指的是 View 的 Model,跟上面那个 Model 不是一回事。所谓 View 的 Model 就是包含 View 的一些数据属性和操作的这么一个东东,这种模式的关键技术就是数据绑定(data binding),View 的变化会直接影响 ViewModel,ViewModel 的变化或者内容也会直接体现在 View 上。这种模式实际上是框架替应用开发者做了一些工作,开发者只需要较少的代码就能实现比较复杂的交互。

设计模式总结

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型模式

1. 工厂方法模式(Factory Method)

  • 普通工厂模式
    建立一个工厂类,对实现了同一接口的一些类进行实例的创建

  • 多个工厂方法模式
    对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象

  • 静态工厂方法模式
    将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可

总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

2. 抽象工厂模式(Abstract Factory)

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑, 有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码

其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!

3. 单例模式(Singleton)

在 Java 应用中,单例对象能保证在一个 JVM 中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了 new 操作符,降低了系统内存的使用频率,减轻 GC 压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程

  • 懒汉
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Singleton {  
    private static Singleton instance;
    private Singleton (){}
    public static synchronized Singleton getInstance() {
    if (instance == null) {
    instance = new Singleton();
    }
    return instance;
    }
    }

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的 lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步

  • 饿汉
    1
    2
    3
    4
    5
    6
    7
    public class Singleton {  
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance() {
    return instance;
    }
    }

这种方式基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

  • 静态内部类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Singleton {  
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    }

这种方式同样利用了 classloder 的机制来保证初始化 instance 时只有一个线程,它跟饿汉不同的是(很细微的差别):饿汉是只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有显示通过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,我想让他延迟加载,另外一方面,我不希望在 Singleton 类加载时就实例化,因为我不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉就显得很合理。

  • 枚举
    1
    2
    3
    4
    5
    public enum Singleton {  
    INSTANCE;
    public void whateverMethod() {
    }
    }

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊

  • 双重校验锁
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton {  
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }

4. 建造者模式(Builder)

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工厂模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。

5. 原型模式(Prototype)

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

  • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
  • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
    要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象

结构型模式

6. 适配器模式 (Adapter)

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式

  • 类的适配器模式
    核心思想就是:有一个 Source 类,拥有一个方法,待适配,目标接口是 Targetable,通过 Adapter 类,将 Source 的功能扩展到 Targetable 里,这样 Targetable 接口的实现类就具有了 Source 类的功能

  • 对象的适配器模式
    基本思路和类的适配器模式相同,只是将 Adapter 类作修改,这次不继承 Source 类,而是持有 Source 类的实例,以达到解决兼容性的问题

  • 接口的适配器模式
    接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行

类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个 Wrapper 类,持有原类的一个实例,在 Wrapper 类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类 Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

7. 装饰模式(Decorator)

装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例

Source 类是被装饰类,Decorator 类是一个装饰类,可以为 Source 类动态的添加一些功能
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!

8. 代理模式(Proxy)

代理模式就是多一个代理类出来,替原对象进行一些操作

代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

9. 外观模式(Facade)

外观模式是为了解决类与类之家的依赖关系的,像 spring 一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个 Facade 类中,降低了类类之间的耦合度,该模式中没有涉及到接口

10. 桥接模式(Bridge)

桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的 JDBC 桥 DriverManager 一样,JDBC 进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是 JDBC 提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了

11. 组合模式(Composite)

组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便

使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

12. 享元模式(Flyweight)

享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用

行为型模式

13. 策略模式(strategy)

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可

14. 模板方法模式(Template Method)

模板方法模式就是指:一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用

15. 观察者模式(Observer)

当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化! 对象之间是一种一对多的关系。

MySubject类就是我们的主对象,Observer1 和 Observer2 是依赖于 MySubject 的对象,当 MySubject 变化时,Observer1 和Observer2 必然变化。AbstractSubject 类中定义着需要监控的对象列表,可以对其进 行修改:增加或删除被监控对象,且当 MySubject 变化时,负责通知在列表内存在的对象

16. 迭代器模式(Iterator)

迭代器模式就是顺序访问聚集中的对象,这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问

17. 责任链模式(Chain of Responsibility)

有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发 出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。

18. 命令模式(Command)

命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里, 士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。

Invoker 是调用者(司令员),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象。命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开。

19. 备忘录模式(Memento)

主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象

Original 类是原始类,里面有需要保存的属性 value 及创建一个备忘录类,用来保存 value 值。Memento 类是备忘录类,Storage 类是存储备忘录的类,持有 Memento 类的实例

20. 状态模式(State)

当对象的状态改变时,同时改变其行为。

状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。

21. 访问者模式(Visitor)

访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果

适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添 加?3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦,

22. 中介者模式(Mediator)

中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和 Mediator 类的关系,具体类类之间的关系及调度交给 Mediator 就行,这有点像 spring 容器的作用

User 类统一接口,User1 和 User2 分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了 Mediator 类,提供统一接口,MyMediator 为其实现类,里面持有 User1 和 User2 的实例,用来实现对 User1 和 User2 的控制。这样 User1 和 User2 两个对象相互独立,他们只需要保持好和 Mediator 之间的关系就行,剩下的全由 MyMediator 类来维护!

23. 解释器模式(Interpreter)

参考

SurfaceView, GLSurfaceView, SurfaceTexture 和 TextureView 总结

  • SurfaceView

从 Android 1.0(API level 1)时就有 。它继承自类 View,因此它本质上是一个 View。但与普通 View 不同的是,它有自己的 Surface。我们知道,一般的 Activity 包含的多个 View 会组成 View hierachy 的树形结构,只有最顶层的 DecorView,也就是根结点视图,才是对 WMS 可见的。这个 DecorView 在 WMS 中有一个对应的 WindowState。相应地,在 SF 中对应的 Layer。而 SurfaceView 自带一个 Surface,这个 Surface 在 WMS 中有自己对应的 WindowState,在 SF 中也会有自己的 Layer。如下图所示:

也就是说,虽然在 App 端它仍在 View hierachy 中,但在 Server 端(WMS和SF)中,它与宿主窗口是分离的。这样的好处是对这个 Surface 的渲染可以放到单独线程去做,渲染时可以有自己的 GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。但它也有缺点,因为这个 Surface 不在 View hierachy 中,它的显示也不受 View 的属性控制,所以不能进行平移,缩放等变换,也不能放在其它 ViewGroup 中,一些 View 中的特性也无法使用。

  • GLSurfaceView

从 Android 1.5(API level 3) 开始加入,作为 SurfaceView 的补充。它可以看作是 SurfaceView 的一种典型使用模式。在 SurfaceView 的基础上,它加入了 EGL 的管理,并自带了渲染线程。另外它定义了用户需要实现的 Render 接口,提供了用 Strategy pattern 更改具体 Render 行为的灵活性。作为 GLSurfaceView 的 Client,只需要将实现了渲染函数的 Renderer 的实现类设置给 GLSurfaceView 即可。相关类图如下。其中 SurfaceView 中的 SurfaceHolder 主要是提供了一坨操作 Surface 的接口。GLSurfaceView 中的 EglHelper 和 GLThread 分别实现了上面提到的管理 EGL 环境和渲染线程的工作。GLSurfaceView 的使用者需要实现 Renderer 接口。

  • SurfaceTexture

从 Android 3.0(API level 11)加入。和 SurfaceView 不同的是,它对图像流的处理并不直接显示,而是转为 GL 外部纹理,因此可用于图像流数据的二次处理(如 Camera 滤镜,桌面特效等)。比如 Camera 的预览数据,变成纹理后可以交给 GLSurfaceView 直接显示,也可以通过 SurfaceTexture 交给 TextureView 作为 View heirachy 中的一个硬件加速层来显示。首先,SurfaceTexture 从图像流(来自 Camera 预览,视频解码,GL 绘制场景等)中获得帧数据,当调用 updateTexImage() 时,根据内容流中最近的图像更新 SurfaceTexture 对应的 GL 纹理对象,接下来,就可以像操作普通 GL 纹理一样操作它了。从下面的类图中可以看出,它核心管理着一个 BufferQueue 的 Consumer 和 Producer 两端。Producer 端用于内容流的源输出数据,Consumer 端用于拿 GraphicBuffer 并生成纹理。SurfaceTexture.OnFrameAvailableListener 用于让 SurfaceTexture 的使用者知道有新数据到来。JNISurfaceTextureContext 是 OnFrameAvailableListener 从 Native 到 Java 的 JNI 跳板。其中 SurfaceTexture 中的 attachToGLContext() 和 detachToGLContext() 可以让多个 GL context 共享同一个内容源。

Android 5.0 中将 BufferQueue 的核心功能分离出来,放在 BufferQueueCore 这个类中。BufferQueueProducer 和 BufferQueueConsumer 分别是它的生产者和消费者实现基类(分别实现了 IGraphicBufferProducer 和 IGraphicBufferConsumer 接口)。它们都是由 BufferQueue 的静态函数 createBufferQueue() 来创建的。Surface 是生产者端的实现类,提供 dequeueBuffer/queueBuffer 等硬件渲染接口,和 lockCanvas/unlockCanvasAndPost 等软件渲染接口,使内容流的源可以往 BufferQueue 中填 graphic buffer。GLConsumer 继承自 ConsumerBase,是消费者端的实现类。它在基类的基础上添加了 GL 相关的操作,如将 graphic buffer 中的内容转为 GL 纹理等操作。到此,以 SurfaceTexture 为中心的一个 pipeline 大体是这样的:

  • TextureView

在 4.0(API level 14) 中引入。它可以将内容流直接投影到 View 中,可以用于实现 Live preview 等功能。和 SurfaceView 不同,它不会在 WMS 中单独创建窗口,而是作为 View hierachy 中的一个普通 View,因此可以和其它普通 View 一样进行移动,旋转,缩放,动画等变化。值得注意的是 TextureView 必须在硬件加速的窗口中。它显示的内容流数据可以来自 App 进程或是远端进程。从类图中可以看到,TextureView 继承自 View,它与其它的 View 一样在 View hierachy 中管理与绘制。TextureView 重载了 draw() 方法,其中主要把 SurfaceTexture 中收到的图像数据作为纹理更新到对应的 HardwareLayer 中。SurfaceTexture.OnFrameAvailableListener 用于通知 TextureView 内容流有新图像到来。SurfaceTextureListener 接口用于让 TextureView 的使用者知道 SurfaceTexture 已准备好,这样就可以把 SurfaceTexture 交给相应的内容源。Surface 为 BufferQueue 的 Producer 接口实现类,使生产者可以通过它的软件或硬件渲染接口为 SurfaceTexture 内部的 BufferQueue 提供 graphic buffer。

最后,总结下这几者的区别和联系。简单地说,SurfaceView 是一个有自己 Surface 的 View。它的渲染可以放在单独线程而不是主线程中。其缺点是不能做变形和动画。SurfaceTexture 可以用作非直接输出的内容流,这样就提供二次处理的机会。与 SurfaceView 直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理 BufferQueue,因此内存消耗也会稍微大一些。TextureView 是一个可以把内容流作为外部纹理输出在上面的 View。它本身需要是一个硬件加速层。事实上 TextureView 本身也包含了 SurfaceTexture。它与 SurfaceView+SurfaceTexture 组合相比可以完成类似的功能(即把内容流上的图像转成纹理,然后输出)。区别在于 TextureView 是在 View hierachy 中做绘制,因此一般它是在主线程上做的(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。而 SurfaceView+SurfaceTexture 在单独的 Surface 上做绘制,可以是用户提供的线程,而不是系统的主线程或是渲染线程。另外,与 TextureView 相比,它还有个好处是可以用 Hardware overlay 进行显示。

参考

Java Concurrency Utilities

  • 阻塞队列 BlockingQueue

BlockingQueue 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景

BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:

操作 抛异常 特定值 阻塞 超时
插入 add(o) offer(o) put(o) offer(o, timeout, timeunit)
移除 remove(o) poll(o) take(o) poll(timeout, timeunit)
检查 element(o) peek(o)

四组不同的行为方式解释:
1)抛异常:如果试图的操作无法立即执行,抛一个异常。
2)特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
3)阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
4)超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。

BlockingQueue 的实现
BlockingQueue 是个接口,你需要使用它的实现之一来使用 BlockingQueue。java.util.concurrent 具有以下 BlockingQueue 接口的实现(Java 6):
1)ArrayBlockingQueue
2)DelayQueue
3)LinkedBlockingQueue
4)PriorityBlockingQueue
5)SynchronousQueue

  • 阻塞双端队列 BlockingDeque

在线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到 BlockingDeque。如果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除数据,这个时候也可以使用 BlockingDeque

操作 抛异常 特定值 阻塞 超时
插入 addLast(o) offerLast(o) putLast(o) offerLast(o, timeout, timeunit)
移除 removeLast(o) pollLast(o) takeLast(o) pollLast(timeout, timeunit)
检查 getLast(o) peekLast(o)

java.util.concurrent 包提供了以下 BlockingDeque 接口的实现类:
1) LinkedBlockingDeque

参考链接English
参考链接Chinese

Android 多媒体开发总结

1. Android MediaCodec stuff

  • create and configure the MediaCodec object
  • loop until done:
    if an input buffer is ready:
    read a chunk of input, copy it into the buffer
    if an output buffer is ready:
    copy the output from the buffer
  • release MediaCodec object

2. Supported Media Formats

parameters SD (Low quality) SD (High quality) HD 720p (N/A on all
Video resolution 176 x 144 px 480 x 360 px 1280 x 720 px
Video frame rate 12 fps 30 fps 30 fps
Video bitrate 56 Kbps 500 Kbps 2 Mbps
Audio codec AAC-LC AAC-LC AAC-LC
Audio channels 1 (mono) 2 (stereo) 2 (stereo)
Audio bitrate 24 Kbps 128 Kbps 192 Kbps

Java 开发总结

1. Java Thread 类总结

  • Java 的线程有4种状态: 新建(New)、运行(Runnable)、阻塞(Blocked)、结束(Dead)
  • Java 线程阻塞(Blocked)的类型:
    1)调用 sleep 函数进入睡眠状态,Thread.sleep(1000) 或者 TimeUnit.SECONDS.sleep(1),sleep 不会释放锁
    2)等待(wait)某个事件,分为两种,(wait,notify,notifyAll),(await, signal,signalAll), 必须在获取到锁的环境才能调用
    3)等待锁,synchronized 和 lock 环境中,锁已经被别的线程拿走,等待获取锁
    4)IO阻塞(Blocked),比如网络等待,文件打开,控制台读取。System.in.read()
  • 线程驱动任务的处理,涉及到两个概念,线程(Thread)、以及任务(Task, Java里面叫Runnable),推荐的做法是写一个类(class)实现 Runnable 接口
  • Java有两种锁可供选择:
    1)对象或者类(class)的锁, 每一个对象或者类都有一个锁。使用 synchronized 关键字获取。 synchronized 加到 static 方法上面就使用类锁,加到普通方法上面就用对象锁。
    2)显示构建的锁(java.util.concurrent.locks.Lock),调用 lock 的 lock 方法锁定关键代码。
  • 可见性(visibility)是指多个线程之间看到完全相同的资源,任何线程对资源的修改,其它线程都能够实时看到。synchronized 能够保证资源的可见性 , volatile 关键字也能够保证资源的可见性
  • Thread.yield() 释放当前线程的 cpu 时间,让同优先级以上的线程可以获取 cpu
  • thread.join() 让调用该方法的 thread 完成 run 方法里面的东西后, 再执行join()方法后面的代码
  • wait notify notifyAll,这三个都是 Object 的方法,wait 用来阻塞(Block)当前线程,但是会释放对象的锁,notify 告诉线程分派器唤醒等待的某一个线程,notifyAll 会唤醒所有等待的线程,需要特别注意的是,这三个方法都需要在锁定的环境(synchronized)里面使用,否则编译通过,但是运行报错。wait notify notifyAll 与 synchronized 配对使用
  • await signal signalAll 是java.util.concurrent.locks.Condition的三个方法,Condition 可以通过 Lock 获得 Condition condition = lock.newCondition()。从功能上来说,await signal signalAll与wait notify notifyAll应该是相同的,只是await signal signalAll 与 Condition 配对使用

2. Java Executors 类总结

  • 为什么要用线程池
    1)减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务(时间效率)
    2)可以根据系统的承受能力,调整线程池中工作者线程的数目,防止消耗过多的内存 (空间效率)
  • Executors 提供了创建以下几类线程池的方法
    1)Single Thread Executor:
    创建的线程池只包含一个线程,所有提交到线程池的线程会按照提交的顺序一个接一个的执行。通过 Executors.newSingleThreadExecutor() 方法创建。这种线程池适用于我们只希望每次使用一个线程的情况。
    2)Cached Thread Pool:
    线程池里会创建尽可能多的必须线程来并行执行。一旦前面的线程执行结束后可以被重复使用。当然,使用这种线程池的时候我们必须要小心。我们使用多线程的目的是希望能够提高并行度和效率,但是并不是线程越多就越好。如果我们设定的线程数目过多的时候,使用 Cached Thread Pool 并不是一个很理想的选择。因为一方面它占用了大量的线程资源,同时线程之间互相切换很频繁的时候也会带来执行效率的下降。它主要适用于使用的线程数目不多,但是对线程有比较灵活动态的要求。一般通过 Executors.newCachedThreadPool() 来创建。
    3)Fix Thread Pool:
    线程池里会创建固定数量的线程。在线程都被使用之后,后续申请使用的线程都会被阻塞在那里。使用 Executors.newScheduledThreadPool() 创建。
    4)Scheduled Thread Pool:
    线程池可以对线程执行的时间和顺序做预先指定。比如说要某些线程在某个时候才启动或者每隔多长时间就启动一次。有点像我们的 Timer Job。使用 Executors.newScheduledThreadPool()创建。
    5)Single Thread Scheduled Pool:
    线程池按照指定的时间或顺序来启动线程池。同时线程池里只有一个线程。创建方法:Executors.newSingleThreadScheduledExecutor()
  • Java 线程池和线程数的选择
    1)CPU 密集
    对于 CPU 密集类型的问题来说,更多只是 CPU 要做大量的运算处理。这个时候如果要保证系统最充分的利用率,我们最好定义的线程数量和系统的 CPU 数量一致。这样每个 CPU 都可以充分的运用起来,而且很少或者不会有线程之间的切换。在这种情况下,比较理想的线程数目是 CPU 的个数或者比 CPU个 数大1
    2)IO 密集
    对于 IO 密集型的问题来说,我们会发现由于 IO 经常会带来各种中断,并且 IO 的速度和 CPU 处理速度差别很大。因此,我们需要考虑到 IO 在整个运算时间中所占的比例。比如说我们有50%比例的时间是阻塞的,那么我们可以创建相当于当前 CPU 数目的两倍数的线程。假设我们知道阻塞的比例数的话,我们可以通过如下的一个简单关系来估算线程数的大小:
    线程数 = 可用CPU个数 / (1 - 阻塞比例数)

3. Java 常用集合总结

  • 线程安全就是说多线程访问同一代码,不会产生不确定的结果
  • List 类和 Set 类
  • HashMap 和 HashTable
  • 线程安全集合类与非线程安全集合类
    1)LinkedList、ArrayList、HashSet 是非线程安全的,Vector 是线程安全的;
    2)HashMap 是非线程安全的,HashTable 是线程安全的;
    3)StringBuilder 是非线程安全的,StringBuffer 是线程安全的
  • 集合适用场景
    1)对于查找和删除较为频繁,且元素数量较多的应用,Set 或 Map 是更好的选择;
    2)ArrayList 适用于通过为位置来读取元素的场景;
    3)LinkedList 适用于要头尾操作或插入指定位置的场景;
    4)Vector 适用于要线程安全的 ArrayList 的场景;
    5)Stack 适用于线程安全的LIFO场景;
    6)HashSet 适用于对排序没有要求的非重复元素的存放;
    7)TreeSet 适用于要排序的非重复元素的存放;
    8)HashMap 适用于大部分key-value的存取场景;
    9)TreeMap 适用于需排序存放的key-value场景。

4. Java 异常总结

  • Java 异常的基类为 java.lang.Throwable,java.lang.Error 和 java.lang.Exception 继承 Throwable,RuntimeException 和其它的 Exception 等继承 Exception,具体的 RuntimeException 继承 RuntimeException
  • 错误和异常的区别(Error vs Exception)
    1)java.lang.Error: Throwable 的子类,用于标记严重错误。合理的应用程序不应该去 try/catch 这种错误。绝大多数的错误都是非正常的,就根本不该出现的。
    2)java.lang.Exception: Throwable 的子类,用于指示一种合理的程序想去 catch 的条件。即它仅仅是一种程序运行条件,而非严重错误,并且鼓励用户程序去 catch 它。
  • Error 和 RuntimeException 及其子类都是未检查的异常(unchecked exceptions),而所有其他的 Exception 类都是检查了的异常(checked exceptions)
    1)checked exceptions: 通常是从一个可以恢复的程序中抛出来的,并且最好能够从这种异常中使用程序恢复。比如 FileNotFoundException, ParseException 等。检查了的异常发生在编译阶段,必须要使用 try…catch(或者 throws)否则编译不通过。
    2)unchecked exceptions: 通常是如果一切正常的话本不该发生的异常,但是的确发生了。发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的。比如 ArrayIndexOutOfBoundException, ClassCastException 等。从语言本身的角度讲,程序不该去 catch 这类异常,虽然能够从诸如 RuntimeException 这样的异常中 catch 并恢复,但是并不鼓励终端程序员这么做,因为完全没要必要。因为这类错误本身就是 bug,应该被修复,出现此类错误时程序就应该立即停止执行。 因此,面对 Errors 和 unchecked exceptions 应该让程序自动终止执行,程序员不该做诸如 try/catch 这样的事情,而是应该查明原因,修改代码逻辑。
  • RuntimeException:RuntimeException 体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理 RuntimeException 的原则是:如果出现 RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。其他(IOException等等)checked异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。

5. Java 的 IO 操作总结

  • Java 的 IO 操作中有面向字节( Byte )和面向字符( Character )两种方式
    面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是 InputStream 和 OutputStream 的子类。
    面向字符的操作为以字符为单位对数据进行操作,在读的时候将二进制数据转为字符,在写的时候将字符转为二进制数据,这些类都是 Reader 和 Writer 的子类。

  • IO 流主要可以分为节点流和处理流两大类

节点流类型

类型 字符流 字节流
File(文件) FileReader FileInputStream
FileWriter FileOutputSream
Memory Array CharArrayReader ByteArrayInputStream
CharArrayWriter ByteArrayOutputSream
Memory String StringReader
StringWriter
Pipe(管道) PipedReader PipedInputSream
PipedWriter PipedOutputSream

处理流类型

1)缓冲流(BufferedInPutStream/BufferedOutPutStream和BufferedWriter/BufferedReader)他可以提高对流的操作效率。
2)转换流(InputStreamReader/OutputStreamWriter)
3)数据流(DataInputStream/DataOutputStream)
4)打印流(PrintStream/PrintWriter)
5)对象流(ObjectInputStream/ObjectOutputStream)

6. 抽象类和接口联系与区别

abstract class 和 interface 是 Java 语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系. abstract class 表示的是 “is a” 关系,interface 表示的是 “like a” 关系

  • 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  • 抽象类要被子类继承,接口要被类实现。
  • 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
  • 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  • 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
  • 抽象方法只能申明,不能实现。abstract void abc(); 不能写成abstract void abc(){}。
  • 抽象类里可以没有抽象方法
  • 如果一个类里有抽象方法,那么这个类只能是抽象类
  • 抽象方法要被实现,所以不能是静态的,也不能是私有的。
  • 接口可继承接口,并可多继承接口,但类只能单根继承。
    特别是对于公用的实现代码,抽象类有它的优点。抽象类能够保证实现的层次关系,避免代码重复。然而,即使在使用抽象类的场合,也不要忽视通过接口定义行为模型的原则。从实践的角度来看,如果依赖于抽象类来定义行为,往往导致过于复杂的继承关系,而通过接口定义行为能够更有效地分离行为与实现,为代码的维护和修改带来方便。

7. Java 线程锁机制:synchronized、Lock、Condition

  • synchronized
    1) 原子性
    原子性意味着个时刻,只有一个线程能够执行一段代码
    2) 可见性
    确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的
    原理:当对象获取锁时,它首先使自己的高速缓存无效,这样就可以保证直接从主内存中装入变量。同样,在对象释放锁之前,它会刷新其高速缓存,强制使已做的任何更改都出现在主内存中。 这样,会保证在同一个锁上同步的两个线程看到在 synchronized 块内修改的变量的相同值。类似的规则也存在于 volatile 变量上。volatile 只保证可见性,不保证原子性。
    3)何时要同步
    一致性同步:当修改多个相关值时,您想要其它线程原子地看到这组更改,要么看到全部更改,要么什么也看不到。
    4)synchronize 的限制
    它无法中断一个正在等候获得锁的线程;
    也无法通过投票得到锁,如果不想等下去,也就没法得到锁;
    同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况
  • ReentrantLock
    java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
  • 读写锁 ReadWriteLock
    与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)
  • 线程间通信 Condition
    Condition 可以替代传统的线程间通信,用 await() 替换 wait(),用 signal() 替换 notify(),用 signalAll() 替换 notifyAll()。

8. 精选30道 Java 笔试题解答

ReactNative 开发工具 Atom 配置

ReactNative 是 Facebook 推出的基于 JavaScript 的开源框架。React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用。在 JavaScript 中用 React 抽象操作系统原生的 UI 组件,代替 DOM 元素来渲染等。

Atom 是 Github 推出的一款号称“属于21世纪”的代码编辑器, 其最大的特点是使用 node.js 来作为其插件语言,以下介绍一下用 Atom 开发 ReactNative 用到的比较好的插件。

Nuclide

Nuclide 是 Facebook 推出的 Atom 插件,作为基于文档编辑器 Atom 的软件包库,Nuclide提供了类似IDE的功能,主要用于简化原生移动应用的开发。

安装完 Atom 后,打开 Settings 面板,并点击 Install 选项卡,然后在搜索框中键入 nuclide ,如图所示:

点击该插件旁边的蓝色 Install 按钮进行安装,安装完 Nuclide 后安装推荐的设置如图:

下面介绍的插件的安装方式都是跟这一样的

save-session

让 Atom 记住上一次打开的会话

hyperclickjs-hyperclick

跳转对于调试代码和阅读代码非常重要, 安装 hyperclick 和 js-hyperclick , 就可以通过引用跳转到需要类和方法

docblockr

代码注释插件

atom-typescript

类型显示

android 自定义签名

生成开发版和发布版的签名,两个签名文件MD5和SHA1值相同,方便第三方插件(微信登录,新浪微博分享,高德地图等等)标识开发者身份
最终效果图

1.生成正式版的keystore

在app目录下新建keystore文件夹,点击Build->Generate Signed APK… 生成一个正式版的keystore(my.jks)

2.生成开发版keystore

复制粘贴my.jks,改名为my-debug.jks

3.修改my-debug.jks的keystore密码

打开终端,cd到keystore文件夹下执行以下命令:keytool -storepasswd -keystore my-debug.jks
执行后会提示输入证书的当前密码,和新密码以及重复新密码确认。这一步需要将密码改为android

4.修改keystore的alias

输入命令:keytool -changealias -keystore my-debug.jks -alias orangecoder -destalias androiddebugkey
这个命令会先后提示输入keystore的密码和当前alias的密码。这一步需要将别名修改为androiddebugkey。

5.修改alias的密码

输入命令:keytool -keypasswd -keystore my-debug.jks -alias androiddebugkey
执行后会提示输入keystore密码,alias密码,然后提示输入新的alias密码,将新密码修改为android

6.项目签名文件配置

打开app目录下的build.gradle文件,在android任务下加上签名文件设置

7.看看最终效果

打开gradle任务面板,双击android目录下的signingReport任务,可以看到debug版和release版的MD5和SHA1值都是一样的

mac下iterm2和zsh配置

首先看张最终的效果图

1.下载安装iterm2

  • 下载安装iterm2 下载链接
  • 将bash切换到zsh
    1
    chsh -s /bin/zsh

2.下载安装oh-my-zsh

1
curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh

3.下载安装Powerline

powerline是一款非常好用的代码提示状态栏,官网地址
如果你的终端能够正常执行pip指令,那么直接执行下面的指令可以完成安装

1
pip install powerline-status

如果没有,则先执行安装pip指令

1
sudo easy_install pip

4.设置iterm2的Regular Font 和 Non-ASCII Font

  • 下载powerline字体库

    1
    git clone https://github.com/powerline/fonts.git
  • cd到install.sh文件所在目录,执行./install.sh指令安装所有Powerline字体

  • 把iTerm 2的设置里的Profile中的Text 选项卡中里的Regular Font和Non-ASCII Font的字体都设置成 Powerline的字体,我这里设置的字体是12pt Meslo LG S DZ Regular for Powerline

5.设置iterm2的配色方案

  • 下载solarized

    1
    git clone https://github.com/altercation/solarized.git
  • 进入刚刚下载的工程的solarized/iterm2-colors-solarized 下双击 Solarized Dark.itermcolors 和 Solarized Light.itermcolors 两个文件就可以把配置文件导入到 iTerm2 里

  • iterm2配置刚安装的配色主题

6.设置iterm2的主题为agnoster

  • 下载agnoster

    1
    git clone https://github.com/fcamblor/oh-my-zsh-agnoster-fcamblor.git
  • 进入刚刚下载的工程里面运行install文件,主题将安装到~/.oh-my-zsh/themes目录下

  • 打开~/.zshrc文件,然后将ZSH_THEME后面的字段改为agnoster。ZSH_THEME=”agnoster”(agnoster即为要设置的主题)

7.增加指令高亮效果

指令高亮效果作用是当用户输入正确命令时指令会绿色高亮,错误时命令红色高亮

  • 切换到.zshrc所在目录

    1
    cd ~
  • 下载zsh-syntax-highlighting

    1
    git clone git://github.com/zsh-users/zsh-syntax-highlighting.git
  • 打开.zshrc文件,在最后添加下面内容

    1
    source ~/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
    plugins=(zsh-syntax-highlighting)

brew install mysql on mac os

1. 安装

$ brew install mysql

2. 设置MySQL数据存放地址

$ vi /etc/my.cnf

输入以下内容:
[mysqld]
datadir=/usr/local/var/mysql
plugin_dir=/usr/local/Cellar/mysql/5.7.9/lib
basedir=/usr/local/Cellar/mysql/5.7.9
pid-file=/usr/local/var/mysql/xpMac.local.pid
log-error=/usr/local/var/mysql/xpMac.local.err

3. 修改root密码

$ ps -ef|grep mysqld    (查看所有mysqld进程)
$ kill -9 123    (关闭所有mysqld进程,123为进程id)
$ mysqld_safe --skip-grant-tables
$ mysql -uroot
$ update user set authentication_string=password('1111') where user='root’;

4. 让MySQL开机自启动

$ mkdir -p ~/Library/LaunchAgents
$ ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents
$ find /usr/local/Cellar/mysql/ -name "homebrew.mxcl.mysql.plist" -exec cp {} ~/Library/LaunchAgents/ \;
$ launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

5. 登陆MySQL客户端

$ mysql -uroot -p 
输入刚才设置的密码就可以了

6. 安装MySQL Workbench,一款专为MySQL设计的ER/数据库建模工具

下载地址:http://dev.mysql.com/downloads/workbench/

参考