JAVA语言特性与设计模式
java语言特性与设计模式
设计模式
单例模式
- 静态初始化(饿汉式)
public class Singleton1 {
private static Singleton1 singleton1 = new Singleton1();
/**
* 构造函数私有化很关键
*/
private Singleton1(){}
public static Singleton1 getInstance(){
return singleton1;
}
}
- 双重检查(懒汉式)
public class Singleton2 {
/**
* volatile很关键,确保实例在多个线程能被正确处理
*/
private volatile static Singleton2 singleton2;
/**
* 构造函数私有化很关键
*/
private Singleton2(){}
public static Singleton2 getInstance(){
if(null == singleton2){
synchronized (Singleton2.class){
if(null == singleton2){
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
}
注意:内存可见性引起的并发问题,必须使用volatile修饰单例变量
- 单例注册表
spring的bean的单例模式就是单例注册表
工厂模式
通过让子类决定创建的对象是什么,来达到将对象创建的过程封装的目的。
构成元素
- 创建者类(Creater):抽象类,定义一个抽象的工厂方法,让子类实现此方法制造产品。(如设计模式一书中的PizzaStore以及子类NYPizzaStore,chicagoPizzaStore)
- 产品类:具体要调用的方法(超类以及子类,如设计模式一书中的Pizza以及它的子类NYStyplePizza,chicagoStylePizza)
- spring如何创建bean
代理模式
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子
- motan的RPC使用的是动态代理
构造者模式
建造者模式将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端压根不用知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。它关注如何一步一步创建一个的复杂对象,不同的具体建造者定义了不同的创建过程,且具体建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性。
-
Builder(抽象建造者)
它为创建一个产品对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildXXX(),它们用于创建复杂对象的各个部件;另一类方法是getXXX(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。 -
ConcreteBuilder(具体建造者)
-
它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
-
Product(产品角色)
它是被构建的复杂对象,包含多个组成部件,具体建造者ConcreteBuilder创建该产品的内部表示并定义它的装配过程。 -
Director(指挥者)
指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
责任链模式
责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
实例清晰易懂,职责链的模式重在这个链的组成,如何组成链呢?
- 第一步需要在处理者对象类中定义后继处理者对象,将这部分代码抽象到抽象类中实现,降低了代码重复性。
- 第二步就是在处理者对象类中的处理方法中如果当前处理者对象无法处理,就将其传递到后继对象去处理。
- 第三步就是在测试类中将这些处理者类的实例串联成链。
其实这个模式有个缺陷,那就是虽然可以实现请求额度传递,但是也不保证在这里链中一定存在能处理请求的处理这类,一旦不存在,那么这个请求将一直存在与链中,如果将链设置成环形,那么这个请求将会永远在环形链中传递不休;还有一点就是由于请求的传递,请求无法立即精确的找到处理者,处理效率会降低。
- Netty消息处理的方式
- 应用场景(审批流程)
适配器模式
适配器就是一种适配中间件,它存在于不匹配的二者之间,用于连接二者,将不匹配变得匹配,简单点理解就是平常所见的转接头,转换器之类的存在。
- 适配器模式有两种:类适配器、对象适配器、接口适配器
- 前二者在实现上有些许区别,作用一样,第三个接口适配器差别较大。
- 原理:通过继承来实现适配器功能。
当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类BB,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用BB中的合适方法,这样就完成了一个简单的类适配器。
- SLF4J使用log4j
观察者模式(发布订阅模式)
- GRPC对流式请求的处理
其它模式
语言特性
JUC
- concurrentXXX
- AtomicXXX
- Executor
- Caller&&Future
- Queue
- Locks
版本差异新特性
动态代理与反射(使用场景,大量使用反射影响性能)
数据类型
- 空间占用
- 基本数据结构
- 自动转型与强制转型
- 封装与拆箱
常用集合(实现)
- HashMap
数组加链表的实现方式,数组中的每一项是个链表,通过计算存入对象的hashCode,来确定对象在数组中的位置,链表处理散列冲突,链表中的节点存放键值对
容量大小都是2的幂次方,是因为可以通过按位与操作来计算余数,比求模更快
非线程安全,在多线程put的情况下,有可能在容量超过填充因子时,进行rehash,因为hashmap为了避免尾部遍历,插入时使用的是头插法,可能会产生死循环
- ConcurrentHashMap
采用分段锁的思想,来降低并发场景下的锁定发生频率,在jdk1.7和1.8差异比较大,1.7中使用Segment,进行分段加锁,1.8使用CAS乐观锁,1.8中引入红黑树,用来解决hash冲突时,链表的顺序查找问题,红黑手的启用条件与链表的长度和map的容量有关,链表长度大于8且容量大于64时转为红黑树方式
- ArrayList&&LienkedList
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
- HashSet
基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成 - TreeMap
实现是红黑树算法的实现
红黑二叉树特点:
- 每个节点都只能是红色或者黑色
- 根节点是黑色
3.每个叶节点(NIL节点,空节点)是黑色的。- 如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
对象引用
- 强引用(不会被GC回收)
- 弱引用(每次GC都会被回收)
- 软引用(内存不足时会被回收)
- 虚引用(必须和引用队列联合使用,主要用于跟着对象被垃圾回收的过程)
异常机制(try…catch…finally)
- 处理流程
- error和Exception的区别
Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
ERROR和EXCEPTION都是Throwable的子类 - final、fianlly的区别
- final:可以作为修饰符修饰变量、方法和类,被final修饰的变量只能一次赋值;被final修饰的方法不能够在子类中被重写;被final修饰的类不能够被继承。
- finally用在异常处理中定义总是执行代码,无论try块中的代码是否引发异常,catch是否匹配成功,finally块中的代码总是被执行,除非JVM被关闭(System.exit(1)),通常用作释放外部资源。
- finalize()方法是Object类中定义的方法,当垃圾回收器将无用对象从内存中清除时,该对象的finalize()方法被调用。由于该方法是protected方法,子类可以通过重写该方法以整理资源或者执行其他的清理工作。
扩展知识点
- SPI机制
- 注解处理机制
版本特性
1.8(长期) | 1.9-1.10 | 1.11(长期) |
---|---|---|
Lambda表达式 | 模块系统 | ZGC(强悍) |
Stream API | 默认G1回收器 | 字符串API增强 |
方法引用 | 接口私有方法 | 内建HTTP Client |
接口默认方法 | 局部变量推断 | |
Metaspace(本地内存)替换PermGen | Graal编译器 |