静态内部类的方式
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return InnerClassSingleton.singleton;
}
private class InnerClassSingleton{
protected static Singleton singleton = new Singleton();
}
}
  然而,虽然静态内部类模式可以很好地避免并发创建出多个实例的问题,但这种方式仍然有其存在的隐患。
  存在的隐患
  · 一旦一个实例被持久化后重新生成的实例仍然有可能是不的。
  · 由于java提供了反射机制,通过反射机制仍然有可能生成多个实例。
  序列化和反序列化带来的问题:反序列化后两个实例不一致了。
private static void singleSerializable() {
try (FileOutputStream fileOutputStream=new FileOutputStream(new File("myObjectFilee.txt"));
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);) {
//            SingletonObject singletonObject = SingletonObject.getInstance();
//            InnerClassSingleton singletonObject = InnerClassSingleton.getInstance();
EnumSingleton singletonObject = EnumSingleton.INSTANCE;
objectOutputStream.writeObject(singletonObject);
objectOutputStream.close();
fileOutputStream.close();
System.out.println(singletonObject.hashCode());
} catch (IOException e) {
e.printStackTrace();
}
try (FileInputStream fileInputStream=new FileInputStream(new File("myObjectFilee.txt"));
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);) {
//            SingletonObject singleTest=(SingletonObject) objectInputStream.readObject();
//            InnerClassSingleton singleTest=(InnerClassSingleton) objectInputStream.readObject();
EnumSingleton singleTest=(EnumSingleton) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println(singleTest.hashCode());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
  问题点及解决办法
ObjectInputStream中的readOrdinaryObject。
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
  调用自定义的readResolve方法
  protected Object readResolve(){
  System.out.println("调用了readResolve方法!");
  return  InnerClassSingleton.getInstance();
  }
  通过反射机制获取到两个不同的实例
private static void attack() {
try {
Class<?> classType = InnerClassSingleton.class;
Constructor<?> constructor = classType.getDeclaredConstructor(null);
constructor.setAccessible(true);
InnerClassSingleton singleton = (InnerClassSingleton) constructor.newInstance();
InnerClassSingleton singleton2 = InnerClassSingleton.getInstance();
System.out.println(singleton == singleton2);  //false
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
  解决方案 : 私有构造方法中进行添加标志判断。
  private InnerClassSingleton() {
  synchronized (InnerClassSingleton.class) {
  if (false == flag) {
  flag = !flag;
  } else {
  throw new RuntimeException("单例模式正在被攻击");
  }
  }
  }
  单例优方案,枚举的方式
  枚举实现单例的优势
  · 自由序列化;
  · 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
  · 线程安全;
  public enum Singleton {
  INSTANCE;
  private Singleton(){}
  }
  Hibernate的解决方案
  通过ThreadLocal的方式
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
public class HibernateSessionFactory {
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
private static final ThreadLocal threadLocal = new ThreadLocal();
private static Configuration configuration = new Configuration();
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
static {
try {
configuration.configure(configFile);
sessionFactory = configuration.buildSessionFactory();
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? essionFactory.openSession() : null;
threadLocal.set(session);
}
return session;
}
// Other methods...
}