Java 设计模式一
通过设计模式思想的深入了解与运用,我们可以提高代码的可重用性,增强系统的可维护性,以及解决很多的复杂的问题。现实中我们不能准确的预测需求的变换,但是我们可以通过代码的设计来减少需求变换给我们带来的影响。在开发中我们无处不在的单一原则,迪米特原则,开闭原则等原则的适度使用是保证我们最终开发出来的代码健壮性的关键。
今天主要来说以下单例模式。
简介##
单例模式就是单例对象的类只允许一个对象的存在,比如古代只允许一个君王的存在,所有的大臣都可以接触到君王,而对大臣提起君王就知道是谁。单例模式的定义中说,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。Singleton类称为实例类,并通过私有方法确保在一个应用中只产生一个实例。单例模式一般的设计为:
public class Singleton {
private static final Singleton singleton =new Singleton();
//单例限制
private Singleton(){
}
//通过该方法获取实例对象
public static Singleton getSingleton(){
return singleton;
}
//类中的其他方法
public static void doSomething(){
}
}
单例模式的优点:单例模式的设计减少了内存的开支,减少了系统性能上的消耗,避免频繁的销毁对象并避免了共享资源的多重占用,单例模式可以在系统设置全局的访问点,优化和共享资源访问。
单例模式的缺点:单例模式没有接口所以后续的扩展困难,单例模式和单一原则是有冲突的。
应用场景:
- 要求生成唯一的环境
- 整个项目需要一个共享访问的节点或者共享的数据
- 需要访问IO和数据库等资源
- 需要定义大量的静态方法和静态常量
通过代码寻找一个适合我们的单例模式##
1.饿汉模式
package bean;
public class Singleton {
private static final Singleton singleton =new Singleton();
//单例限制
private Singleton(){
}
//通过该方法获取实例对象
public static Singleton getSingleton(){
return singleton;
}
//类中的其他方法
public static void doSomething(){
}
}
public static void main(String[] args) {
Singleton_one s1=Singleton_one.getInstance();
Singleton_one s2=Singleton_one.getInstance();
System.out.println(s1==s2);
System.out.println("s1: "+s1.hashCode()+" s2: "+s2.hashCode());
}
运行结果:
这里你可能疑问为什么使用比较,这里可以简单的说一下equals与的区别,对应eauqls是用于不同对象之间的内容判断,用于比较引用类型和比较基本数据类型时具有不同的功能,而在对同一对象的内容进行判断时equals等同于,在不同对象内容比对时==起的作用就是对类型的判断。
上面的这种饿汉模式的写法虽然线性安全但是如果没有被使用就会对内存造成浪费。
2.懒汉模式
public class Singletonj_two {
private Singletonj_two(){}
private static Singletonj_two instance;
public static Singletonj_two getInstancej(){
if(instance == null){
instance=new Singletonj_two();
}
return instance;
}
}
此种写法在单线程中能够起到懒加载的效果,但是如果多个线程调用,一个线程已经到达if判断语句但是没有执行实例化创建,那么就会造成其它的线程创建实例的情况,所以这种写法线程不安全。
3.懒汉模式 (加入 synchronized)
package bean;
public class Singleton_two {
private Singleton_two(){}
private static Singleton_two instance;
public static synchronized Singleton_two getInstance(){
if(instance == null){
instance=new Singleton_two();
}
return instance;
}
}
Singleton_two s1=Singleton_two.getInstance();
Singleton_two s2=Singleton_two.getInstance();
System.out.println(s1==s2);
System.out.println("s1: "+s1.hashCode()+" s2: "+s2.hashCode());
这种写法就避免了上诉懒汉模式中出现的问题,但是虽然线程安全了但是每次执行都需要进行同步,就会造成效率的下降。所以这种方法也不推荐使用。
4.懒汉模式 (使用Volatile)
public class Singleton_three {
private Singleton_three(){}
//使线程实时获取 instance的值
private static volatile Singleton_three instance;
public static Singleton_three getInstance(){
if (instance==null){//保证只一个线程进入if块
synchronized (Singleton_three.class){//保证只会创建一个实例
if(instance==null)
instance=new Singleton_three();
}
}
return instance;
}
}
这种写法解决了上面写法的效率低的问题,使得线程安全的前提下,懒加载,效率提高。可以使用此种写法应用到实际开发中。
5.静态内部类实现单例
package bean;
/**
*
* @author Administrator
* 使用静态内部类实现单例模式
*
*/
public class Singleton_four {
private Singleton_four(){}
//使用一个静态内部类 并添加静态的属性
private static class SingletonInstance{
//不用写private 外部类可以直接访问内部类中的静态、私有变量
static Singleton_four single= new Singleton_four();
}
public static synchronized Singleton_four getInstance(){
return SingletonInstance.single;
}
}
Singleton_four s1=Singleton_four.getInstance();
Singleton_four s2=Singleton_four.getInstance();
System.out.println(s1==s2);
System.out.println("s1: "+s1.hashCode()+" s2: "+s2.hashCode());
运行结果:
使用静态内部类的优点是 外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化,所以并不占用内存。这种方法不仅能够保证线程安全,也能够保证单例的唯一性,实现了懒加载。JAVA虚拟机在以下几种方法中才会对类初始化:
- 遇到new、getstatic、setstatic或者invokestatic指令时
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有初始化,需要先调用其初始化方法进行初始化。
- 初始化类时,如果父类未初始化,会先初始化父类。
- 虚拟机启动时,需要指定一个主类,并先对其初始化。
- 如果使用JDK语言支持时,java.lang.invoke.MethodHandle实例最后的解析中REF_getStatic\REF_putStatic\REF_invokeStatic的方法语句,并且这个方法语句对应的类如果没有初始化则会触发初始化
------from 《虚拟机规范》
6.枚举
public class Singleton_enumerate {
//私有化构造函数
private Singleton_enumerate(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private Singleton_enumerate Singleton;
//私有化枚举的构造函数
private SingletonEnum(){
Singleton=new Singleton_enumerate();
}
public Singleton_enumerate getInstnce(){
return Singleton;
}
}
//对外暴露一个获取对象的静态方法
public static Singleton_enumerate getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
枚举方法简单高效。充分利用枚举类的特性,有效的避免多线程同步的问题,还能防止反序列化重新创建新的对象,所以推荐此方法。