单例模式就是只能在类外实例化一个对象
为了保证在类外实例化一个对象,需要做到
1.构造方法私有化,
2.提供一个方法的接口,为了在类外调用这个方法,必须把该方法设置成静态的(如果不设置为静态的,则需要创建对象去调用这个方法,显然私有化构造函数不能创建对象)
单例的优点和缺点:
优点:减少内存创建开销[减少频繁创建和销毁实例的开销]
缺点:没有接口不能实现
单例的实现方式五种:懒汉式,(线程不安全+同步代码块+双重锁机制),饿汉式(普通+创建内部类),枚举
懒汉式代码实现(不线程安全):
最大的问题就是不支持多线程,因为没有加锁synchronized,所以严格意义上它不是单例模式;
分为懒汉单例模式和饿汉单例模式
懒汉单例模式
(-)普通懒汉模式实现
public class Singleton1 {
private static Singleton1 instance;
private Singleton1(){}
public static Singleton1 getInstance()
{
if(instance==null)
{
instance=new Singleton1();
}
return instance;
}
}
测试类
public class Demo01 {
public static void main(String[] args) {
Singleton1 s1=Singleton1.getInstance();
Singleton1 s2=Singleton1.getInstance();
if(s1==s2)
{
System.out.println("两个对象是相同的实例");
}
}
(二)懒汉模式有可能出现多线程并发访问安全性问题(使用同步锁解决)
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
/* public static synchronized Singleton4 getInstance()
{
if (instance == null) {
instance = new Singleton4();
}
return instance;
}*/
public static Singleton4 getInstance()
{
synchronized (Thread.class) {
if (instance == null) {
instance = new Singleton4();
}
}
return instance;
}
}
(三)懒汉模式解决多线程并发访问方法二:双重锁机制
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance(){
if(instance==null)
{
synchronized (Singleton5.class)
{
if (instance==null)
{
instance=new Singleton5();
}
}
}
return null;
}
}
饿汉单例模式
(一)普通饿汉单例模式实现
public class Singleton2 {
private static Singleton2 instance=new Singleton2();
private Singleton2() {
}
public static Singleton2 getInstance(){
return instance;
}
}
(二)饿汉单例模式第二种实现
饿汉模式的缺点是加载的速度慢,因为在加载类的时候就要实例化对象为了解决这个问题,引进了内部类,让其在执行getinstance方法的时候再去加载
public class Singleton6 {
private Singleton6() {
}
private static class Singleton{
private static Singleton6 instance=new Singleton6();
}
public static Singleton6 getInstance()
{
return Singleton.instance;
}
}
反射破解单例模式
/**
* 使用反射破坏单例模式
*/
public class Demo07 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Singleton6 s1=Singleton6.getInstance();
//得到构造方法,无论公有还是私有
Constructor<Singleton6>constructor=Singleton6.class.getDeclaredConstructor();
//对于getDeclaredConstructor方法获得的构造器需要先设置可访问,再实例化对象
constructor.setAccessible(true);
Singleton6 s2=constructor.newInstance();
if(s1!=s2)
{
System.out.println("这两个对象不相等");
}
else
{
System.out.println("这两个对象相等");
}
}
}
枚举破解反射实现单例模式
枚举破解反射的原理 反射在使用newInstance创建对象时,会检查该类是否是枚举修饰,如果是则反射创建失败,报出异常
https://www.cnblogs.com/chiclee/p/9097772.html
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
比较常用的是双重锁机制和枚举
枚举是最佳的实现方法
可以避免反射创建对象,支持序列化(普通的实现单例模式虽然创建的对象相同,但是序列化得到的结果不同)