系列文章目录
文章目录
前言
学习单例模式
一、单例模式是什么?
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
二、饿汉式
package FOG23;
//饿汉式单例
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
问题:该类静态创建了对象,浪费了内存
三、懒汉式
package FOG23;
//懒汉式单例
public class Lazy {
private Lazy(){
}
private static Lazy LAZY;
//单线程ok,多线程不行,要加锁
//双重检测,DCL懒汉式
private static Lazy getInstance(){
if (LAZY==null){
synchronized (Lazy.class){
if (LAZY==null){
LAZY=new Lazy();
}
}
}
return LAZY;
}
}
问题:该类单线程ok,多线程不行,要加锁。LAZY=new Lazy()在cpu中不是一个原子操作。(1.分配内存2,执行构造方法,初始化3,把对象指向这个空间)可能会出现132这个顺序。
四、静态内部类
package FOG23;
//静态内部类
public class Holder {
private Holder(){
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
}
五、安全性
以上的三种都不安全,都可以通过java反射机制获得构造器,获得新的实例对象。
已懒汉式为例:
package FOG23;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//懒汉式单例
public class Lazy {
private Lazy(){
}
private static Lazy LAZY;
//单线程ok,多线程不行,要加锁
//双重检测,DCL懒汉式
private static Lazy getInstance(){
if (LAZY==null){
synchronized (Lazy.class){
if (LAZY==null){
LAZY=new Lazy();
}
}
}
return LAZY;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Lazy instance = Lazy.getInstance();
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
Lazy instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
//instance!=instance1; 不是单例了
}
}
处理这个反射机制:在无参构造中继续加入一个检测
private Lazy(){
synchronized (Lazy.class){
if (LAZY!=null){
throw new RuntimeException("无法通过反射实例!");
}
}
}
问题:无需通过
Lazy instance = Lazy.getInstance();创建对象。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//Lazy instance = Lazy.getInstance();
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
Lazy instance1 = declaredConstructor.newInstance();
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance2);
System.out.println(instance1);
//instance!=instance1; 不是单例了
}
进一步解决这个问题:在类中加一个静态的标志:来进行判断是否通过反射来进行实例化
private static boolean Tip=false;
private Lazy(){
synchronized (Lazy.class){
if (Tip==false){
Tip=true;
}else {
throw new RuntimeException("无法通过反射实例!");
}
}
}
问题:当知道这个标志符后依旧能将类的实例:
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//Lazy instance = Lazy.getInstance();
Field tip = Lazy.class.getDeclaredField("Tip");
tip.setAccessible(true);
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
Lazy instance1 = declaredConstructor.newInstance();
tip.set(instance1,false);
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance2);
System.out.println(instance1);
//instance!=instance1; 不是单例了
}
五、如何解决(枚举)
创建一个枚举类:
//enum 本身是个类
public enum enumStrings {
INSTANCE;
public enumStrings getInstance(){
return INSTANCE;
}
}
根据java反射来实现一下实例化
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
enumStrings instance = enumStrings.INSTANCE; //获得enumString实例
//通过反射获得默认的无参构造,
Constructor<enumStrings> declaredConstructor = enumStrings.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
enumStrings newInstance = declaredConstructor.newInstance();
//正常结果应该是:IllegalArgumentException("Cannot reflectively create enum objects"); 不能通过反射创建
//而结果是:Exception in thread "main" java.lang.NoSuchMethodException: FOG23.enumStrings.<init>()
//没有这个空参的构造方法
System.out.println(instance);
System.out.println(newInstance);
}
}
通过反编译:发现在enumStrings.class中出现了一个有参构造:
public enumStrings(String s,int i){
super(s,i);
}
然后在mian方法中修改:
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
enumStrings instance = enumStrings.INSTANCE; //获得enumString实例
//通过反射获得默认的无参构造,
Constructor<enumStrings> declaredConstructor = enumStrings.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
enumStrings newInstance = declaredConstructor.newInstance();
//结果为:IllegalArgumentException("Cannot reflectively create enum objects"); 不能通过反射创建
System.out.println(instance);
System.out.println(newInstance);
}
}
总结
反射无法破解enum的单例