黑马程序员——java基础——单例设计模式
2015-06-10 15:58
253 查看
一,什么是单例设计模式
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。在Java开发中,有时我们会有这样的需求就是在描述一个类的时候,要求我们必须保证这个类的对象在内存中只会存在一个,就是保存对象在内存中的唯一性,那我们在设计这个类的时候,就应该使用单例设计模式。比如:Java中的Runtime类就是采用的单例模式来设计的。
二,Java 中常用的单例模式有两种
A,懒汉式
三,懒汉式与饿汉式的区别
1,饿汉式是在类加载的时候内存中就创建了对象,而懒汉式 是在调用该类的静态工厂方法的时候才创建对象。
2,饿汉式 是线程安全的,而懒汉式线程不安全。
分析懒汉式产生线程安全问题的原因:
当多个线程同时 执行懒汉式中的getSingleInstance()方法的时,这多个线程都会访问共享数据 静态私有字段s(因为该字段是静态的 所以是共享数据),并且操作共享数据的代码是多行,所以会产生线程安全问题,这样就保证不了该类在内存中的对象的唯一性。
解决懒汉式的线程安全问题:
上面的代码虽然可以解决线程的安全问题,就是可以保证即使有多个线程过来执行getSingleInstance()方法,内存中只会存在一个Single类的对象,但是只要有一个线程过来执行getSingleInstance()方法获取Single 类的对象,都会先出判断锁标记,假如内存中现在内存中就已经有了Single的对象(s字段已经被赋值了),一个线程过来之后先判断锁,但是这种判断是无效的判断而且会消耗资源从而导致效率比较低。我们要去提高效率可以在同步代码块外面添加一个if(s==null)的条件判断,因为每一个线程获取拿去对象的时候不管怎么样都会去判断s是否等于null
,要是我们在通过同步代码块外面添加if(s==null)的话,就可以减少判断锁的次数,效率也就相对较高。
代码:
上面两种单例模式,能够保证对象的唯一性的根本原因就是 ,将构造函数私有化,不允许在该类的外部调用构造函数创建对象。要是我们利用反射技术通过Single.class.getDeclaredConstructor() 获取到Single类私有构造器,再通过setAccessible(true)方法取消默认 Java 语言访问控制检查的能力,那么我们可以直接调用构造器的newInstance()方法来创建Single类的实例,并且可以多次创建多个对象,这样的话就保证不了对象的唯一性。所以说我们要是利用反射技术的话,上面两种单例模式就会失效。
PS:如果一个枚举类的枚举值只有一个话,这个枚举类可以当做单例设计模式使用。
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。在Java开发中,有时我们会有这样的需求就是在描述一个类的时候,要求我们必须保证这个类的对象在内存中只会存在一个,就是保存对象在内存中的唯一性,那我们在设计这个类的时候,就应该使用单例设计模式。比如:Java中的Runtime类就是采用的单例模式来设计的。
二,Java 中常用的单例模式有两种
A,懒汉式
class Single { //定义一个静态的私有字段 private static Single s=null; //定义静态工厂方法 返回该类的实例 public static Single getSingleInstance() { if(s==null) { s=new Single(); } return s; } //私化构造函数 private Single() { } }B,饿汉式
class Single { //定义一个静态的私有字段, 并在类加载时候就创建了该类的实例 private static final Single s=new Single(); //定义静态工厂方法 返回该类的实例 public static Single getSingleInstance() { return s; } //私化构造函数 private Single() { } }
三,懒汉式与饿汉式的区别
1,饿汉式是在类加载的时候内存中就创建了对象,而懒汉式 是在调用该类的静态工厂方法的时候才创建对象。
2,饿汉式 是线程安全的,而懒汉式线程不安全。
分析懒汉式产生线程安全问题的原因:
当多个线程同时 执行懒汉式中的getSingleInstance()方法的时,这多个线程都会访问共享数据 静态私有字段s(因为该字段是静态的 所以是共享数据),并且操作共享数据的代码是多行,所以会产生线程安全问题,这样就保证不了该类在内存中的对象的唯一性。
解决懒汉式的线程安全问题:
class Single { //定义一个静态的私有字段 private static Single s=null; //定义静态工厂方法 返回该类的实例 public static Single getSingleInstance() { //添加同步 注意这里的锁不能是this 因为这是在静态方法中 synchronized(Single.class) { if(s==null) { s=new Single(); } } return s; } //私化构造函数 private Single() { } }
上面的代码虽然可以解决线程的安全问题,就是可以保证即使有多个线程过来执行getSingleInstance()方法,内存中只会存在一个Single类的对象,但是只要有一个线程过来执行getSingleInstance()方法获取Single 类的对象,都会先出判断锁标记,假如内存中现在内存中就已经有了Single的对象(s字段已经被赋值了),一个线程过来之后先判断锁,但是这种判断是无效的判断而且会消耗资源从而导致效率比较低。我们要去提高效率可以在同步代码块外面添加一个if(s==null)的条件判断,因为每一个线程获取拿去对象的时候不管怎么样都会去判断s是否等于null
,要是我们在通过同步代码块外面添加if(s==null)的话,就可以减少判断锁的次数,效率也就相对较高。
代码:
class Single { //定义一个静态的私有字段 private static Single s=null; //定义静态工厂方法 返回该类的实例 public static Single getSingleInstance() { if(s==null) { //添加同步 synchronized(Single.class) { if(s==null) { s=new Single(); } } } return s; } //私化构造函数 private Single() { } }
上面两种单例模式,能够保证对象的唯一性的根本原因就是 ,将构造函数私有化,不允许在该类的外部调用构造函数创建对象。要是我们利用反射技术通过Single.class.getDeclaredConstructor() 获取到Single类私有构造器,再通过setAccessible(true)方法取消默认 Java 语言访问控制检查的能力,那么我们可以直接调用构造器的newInstance()方法来创建Single类的实例,并且可以多次创建多个对象,这样的话就保证不了对象的唯一性。所以说我们要是利用反射技术的话,上面两种单例模式就会失效。
PS:如果一个枚举类的枚举值只有一个话,这个枚举类可以当做单例设计模式使用。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统