设计模式 单例模式

单例模式的意思就是确保某个类的实例在系统中只有一个,可以简单概括为以下三点:

  1. 只能有一个类实例;
  2. 类必须自行创建这个实例;
  3. 类必须自行向整个系统提供这个实例。

设计模式 原则&分类

设计模式的主要目标在于保证程序的高可用及高扩展性,在实际开发中不应该过多地纠结使用哪种设计模式,而是应该多体会设计模式的原则,换言之,只要遵循一定的原则,这些所谓的模式完全可能在无意识的状态下自发出现在产品代码中。其实这些设计模式也都不是谁突然间硬想出来的,而是从大量实践中总结出来的比较好的组织代码结构的方式。

好的设计能够有效避免或者延缓程序架构的腐化,通常,很多程序结构一开始也许设计得还可以,但随着业务功能的不断扩展和变化,可能就不得不在结构上做出一些妥协让步,慢慢地也就失去了结构。

另外,在设计过程中也要注意避免过度设计,即为不可能发生的变动付出过多的复杂度代价。总之不要滥用设计模式,不要觉得一个地方有可能会变动,就忍不住考虑是否应该增加复杂度来换取灵活性。而要避免过度设计,关键在于能正确的预见变化,以及权衡所引入的复杂度相对于发生变化的可能性和破坏力是否值得,当然这些都需要一定的经验积累以及对业务的认识。

UML 关系图

在面向对象的设计中,类的关系涉及依赖、关联、聚合、组合和泛化这五种关系,耦合程度依次递增。

Java I/O 网络

对于网络I/O通信,其实站在应用程序的角度,就是面向Socket进行读写的过程,至于如何建立连接,数据包如何传输等问题则由TCP/IP以及更底层的协议保障,这些通常由操作系统和底层硬件负责实现,它们的目的就是希望能简化应用程序的开发。

在Linux系统中,一切皆文件,所以Socket也统一抽象为一个文件描述符,那么对应用程序来讲,收发消息就是对一个文件描述符进行读写。但是它与读写普通的本地磁盘文件也有区别,由于网络的延迟和不可靠性,在读Socket的过程中可能会有大量的时间花在等待数据包的到达上,因此如何提高应用程序在读写Socket过程中的响应性,就成了网络通信编程中一个很重要的问题。

针对网络I/O,Linux提供了五种IO模型,具体可以参考笔记

不过Java并没有全部支持,而是逐步选择支持了三种模型:
在JDK1.4之前,Java的IO模型只支持阻塞式IO(Blocking IO),简称为BIO;
在JDK1.4时,Java支持了I/O多路复用模型, 简称NIO,即新IO模型,不过现在JDK1.8早已成为主流版本,已经没什么新鲜了,所以更多的人愿意将它理解为非阻塞I/O,即None-Blocking IO;
在JDK1.7时,Java对NIO包进行了升级,支持异步I/O(Asynchronous IO),简称为AIO,因为是对nio包的升级,所有也称为NIO2.0;

Java I/O 文件

Java在操作系统的基础上对I/O操作提供了更高级的Api封装,可以对这些Api进行一些粗略的分类,比如从数据格式的角度可以分为基于字节操作的InputStreamOutputStream,以及基于字符操作的ReaderWriter,从数据传输方式的角度可以分为基于磁盘操作的File和基于网络操作的Socket,其实在Java中,I/O主要考虑的就是以怎样的方式将怎样格式的数据进行传输的问题,然后从这两个方向考虑如何提高I/O操作的效率

由于I/O类库有很多类,很难对每个都介绍到位,这里只是简单从文件操作的场景对一些常用的Api做一些梳理,尽量从中去体会Java语言对于I/O操作支持和封装的思路,另外,如果想对I/O有更好的理解,最好先有如下准备知识:

NIO Channel & Selector

Channel 与 Stream 是一个级别,只不过 Stream 是单向的,比如InputStreamOutputStream,而 Channel 可以是单向的,也可以是双向的,既可以同时支持读和写操作,具体由其实现的接口所决定

java.nio.channels
1
2
3
4
5
6
7
public interface ReadableByteChannel extends Channel {
public int read(ByteBuffer dst) throws IOException;
}

public interface WritableByteChannel extends Channel{
public int write(ByteBuffer src) throws IOException;
}

可以看到 read 和 write 方法接受的都是一个 ByteBuffer 参数,其中Channel.read是向ByteBuffer中put数据,然后应用从ByteBuffer中获取,而Channel.write是从ByteBuffer中get数据,然后发送给其他远程主机。两者均返回字节数,然后缓冲区的position位置也会前移对应的字节数,如果只进行了部分传输,缓冲区可以被重新提交给通道并从上次中断的地方继续传输,该过程可以重复进行直到缓冲区的hasRemaining()方法返回false