Java设计模式–单例模式
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
这是一种比较官方的说法,其实道理很简单,就是使一个类只实例化一个对象,最基本的就是将构造函数私有,然后写一个方法去返回该类的对象。
- 1.懒汉式
- 1.1 线程不安全
这是最容易想到的,最简单的实现
因为只能有一个对象,所以在方法中必须要判断对象是否已经实例化
public class LazySingletonTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1==instance2);
}
}
class LazySingleton{
private static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if(lazySingleton==null) {
lazySingleton=new LazySingleton();
}
return lazySingleton;
}
}
- 1.2 多线程
在上面的代码中,如果遇到多个线程同时执行会导致当第一个线程没有实例化完成第二个线程继续实例化导致出现两个实例(与描述不符)
适当的再方法中加入synchronized关键字对方法进行同步
public class LazySingletonTest3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(() -> {
LazySingleton3 instance = LazySingleton3.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
LazySingleton3 instance2 = LazySingleton3.getInstance();
System.out.println(instance2);
}).start();
}
}
class LazySingleton3{
private static LazySingleton3 lazySingleton;
private LazySingleton3() {
}
public static synchronized LazySingleton3 getInstance() {
if(lazySingleton==null) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lazySingleton=new LazySingleton3();
}
return lazySingleton;
}
}
这里讲实话写的并不是很妥当,当对象不管是否已经建立都会被synchronized约束,比较耗费资源,但是对于理解是很有帮助的(虽然不是很难),这里是我的一点小小的改进(如果是只了解设计模式的话前面的代码就可以了)
public class LazySingletonTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(() -> {
LazySingleton2 instance = LazySingleton2.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
LazySingleton2 instance2 = LazySingleton2.getInstance();
System.out.println(instance2);
}).start();
}
}
class LazySingleton2{
//volatile防止重排序
private volatile static LazySingleton2 lazySingleton;
private LazySingleton2() {
}
public static LazySingleton2 getInstance() {
if(lazySingleton==null) {
synchronized (LazySingleton2.class) {
if(lazySingleton==null) {
lazySingleton=new LazySingleton2();
}
}
}
return lazySingleton;
}
}
- 2.饿汉式
用类的加载机制保证单例模式,jvm会保证线程的安全
public class HungrySingleTonTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
HungrySingleTon instence = HungrySingleTon.getInstence();
HungrySingleTon instence2 = HungrySingleTon.getInstence();
System.out.println(instence==instence2);
}
}
class HungrySingleTon {
private static HungrySingleTon hungrySingleTon=new HungrySingleTon();
private HungrySingleTon() {
}
public static HungrySingleTon getInstence() {
return hungrySingleTon;
}
}
- 3 内部类
和饿汉式的原理差不多,都是利用了类的加载机制,同时也保证了线程的安全
单线程测试:
public class InnerClassSingleTonTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
InnerClassSingleTon instence = InnerClassSingleTon.getInstence();
InnerClassSingleTon instence2 = InnerClassSingleTon.getInstence();
System.out.println(instence==instence2);
}
}
class InnerClassSingleTon{
private static class InnerClassHolder{
private static InnerClassSingleTon innerClassSingleTon=new InnerClassSingleTon();
}
private InnerClassSingleTon() {
}
public static InnerClassSingleTon getInstence() {
return InnerClassHolder.innerClassSingleTon;
}
}
多线程测试
public class InnerClassSingleTonTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
new Thread(()->{
InnerClassSingleTon instence3 = InnerClassSingleTon.getInstence();
System.out.println(instence3);
}).start();
new Thread(()->{
InnerClassSingleTon instence3 = InnerClassSingleTon.getInstence();
System.out.println(instence3);
}).start();
}
}
class InnerClassSingleTon{
private static class InnerClassHolder{
private static InnerClassSingleTon innerClassSingleTon=new InnerClassSingleTon();
}
private InnerClassSingleTon() {
}
public static InnerClassSingleTon getInstence() {
return InnerClassHolder.innerClassSingleTon;
}
}
很明显,运行完上面的两组代码,你已经对这个设计模式有大概的了解了,但是我们知道我们不光可以通过类名.静态方法创建对象,还可以通过Java的反射来实现,当我们通过反射建立对象的时候,他的值会与我们类名.静态方法创建对象的值相同吗?看到这里,不要着急,不妨先思考一下
public class InnerClassSingleTonTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Constructor<InnerClassSingleTon> constructor = InnerClassSingleTon.class.getDeclaredConstructor();
constructor.setAccessible(true);
InnerClassSingleTon newInstance = constructor.newInstance();
InnerClassSingleTon instence = InnerClassSingleTon.getInstence();
System.out.println(newInstance==instence);
}
}
class InnerClassSingleTon{
private static class InnerClassHolder{
private static InnerClassSingleTon innerClassSingleTon=new InnerClassSingleTon();
}
private InnerClassSingleTon() {
}
public static InnerClassSingleTon getInstence() {
return InnerClassHolder.innerClassSingleTon;
}
}
答案是否定的,这反而提示了我们一个隐患,我们前面只想到了普通创建对象的方式,而忽略了Java的反射,实际开发中,这个错误是比较致命的,所以写代码一定要多多思考。。。
既然我们发现了错误,那我们就离成功很近了
于是我们在构造方法中加入判断条件
public class InnerClassSingleTonTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Constructor<InnerClassSingleTon> constructor = InnerClassSingleTon.class.getDeclaredConstructor();
constructor.setAccessible(true);
InnerClassSingleTon newInstance = constructor.newInstance();
InnerClassSingleTon instence = InnerClassSingleTon.getInstence();
System.out.println(newInstance==instence);
}
}
class InnerClassSingleTon{
private static class InnerClassHolder{
private static InnerClassSingleTon innerClassSingleTon=new InnerClassSingleTon();
}
private InnerClassSingleTon() {
if(InnerClassHolder.innerClassSingleTon!=null) {
throw new RuntimeException("单例不可以有多个实现");
}
}
public static InnerClassSingleTon getInstence() {
return InnerClassHolder.innerClassSingleTon;
}
}
这样就可以简单的保证单例的稳定
- 4 枚举类
当然,还有就是Java的枚举本省就是一个单例(tip:其实枚举也是一个类)
Class Enum<E extends Enum>
public enum EnumSingleTon {
INSTANCE;
public void print() {
System.out.println(this.hashCode());
}
}
class EnumTest{
public static void main(String[] args) {
EnumSingleTon instance = EnumSingleTon.INSTANCE;
EnumSingleTon instance2 = EnumSingleTon.INSTANCE;
System.out.println(instance==instance2);
}
}
当然对于反射他是安全的
import java.lang.reflect.Constructor;
public enum EnumSingleTon {
INSTANCE;
public void print() {
System.out.println(this.hashCode());
}
}
class EnumTest{
public static void main(String[] args) throws Exception{
//在Enum类中仅有 producted Enum(String name, int ordinal)构造方法
Constructor<EnumSingleTon> constructor = EnumSingleTon.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
constructor.newInstance("INSTANCE",0);
}
}
- 5.序列化
- 原理:不经过构造函数(字节流)
public class InnerClassSingleTonTest{
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
InnerClassSingleTon instence = InnerClassSingleTon.getInstence();
//写
ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("testSerializable"));
outputStream.writeObject(instence);
outputStream.close();
//读
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("testSerializable"));
InnerClassSingleTon object=(InnerClassSingleTon)ois.readObject();
System.out.println(instence==object);
}
}
class InnerClassSingleTon implements Serializable{
private static final long serialVersionUID = 42L;//划重点
private static class InnerClassHolder{
/**
* 利用类的加载机制,保证线程安全
*/
private static InnerClassSingleTon innerClassSingleTon=new InnerClassSingleTon();
}
private InnerClassSingleTon() {
if(InnerClassHolder.innerClassSingleTon!=null) {
throw new RuntimeException("单例不可以有多个实现");
}
}
public static InnerClassSingleTon getInstence() {
return InnerClassHolder.innerClassSingleTon;
}
Object readResolve() throws ObjectCollectedException{//划重点
return InnerClassHolder.innerClassSingleTon;
}
}
这样的话,即使InnerClassSingleTon变化,依然可以输出true
其实只要你细心观察,单例设计模式虽然实现起来比较简单,但是在很多场合都发挥着很大的作用,例如:Java自己的Math类也是单例设计模式,或者某些moba游戏。。。看到这里,这个设计模式基本的内容已经结束了,剩下的就是你们自己的体会和实践了。