package com.learn.design.pattern.creational.singleton;
/**
* 这个类是Enum类型
* 这个枚举非常简单
* 枚举类是Object
* 他在多线程的时候呢
* 肯定是没有问题的
* 我们主要是关注枚举类型的序列号机制
* 还有反射攻击
* 那枚举类天然的可序列化机制呢
* 能够强有力的保证不会出现多次实例化的情况
* 那即使在复杂的序列化
* 或者反射的攻击下
* 枚举类型的单例模式
* 都没有问题
* 只不过我们现在这种写法
* 看起来不太自然
* 但是枚举类型的单例
* 可能是实现单例模式中的最佳实现
* 也是强烈推荐这种写法
* 那我们先测试一下序列化
* 我们把序列化相关的代码先打开
*
*
*
* @author Leon.Sun
*
*/
public enum EnumInstance {
/**
* 我们声明一个instance
*
*/
INSTANCE{
protected void printTest(){
System.out.println("Geely Print Test");
}
};
protected abstract void printTest();
/**
* 为了测试我们声明一个Object
* 这里用Object
* 用别的类型也可以
* 只不过我们做测试使用
*
*/
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 获取这个枚举类型
*
* @return
*/
public static EnumInstance getInstance(){
return INSTANCE;
}
}
package com.learn.design.pattern.creational.singleton;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");
// HungrySingleton instance = HungrySingleton.getInstance();
/**
* 我们直接用EnumInstance获取一个instance
* 等于一个什么呢
* EnumInstance.getInstance()
*
*
*/
EnumInstance instance = EnumInstance.getInstance();
/**
* 我们直接setData
* new一个Object
* 也就是说枚举类持有的对象在这一行已经new好了
* 然后把它序列化
* 再反序列化回来
* 看看这个data是不是同一个
*
*
*/
instance.setData(new Object());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
/**
* 这里替换成枚举类型EnumInstance
*
*
*/
EnumInstance newInstance = (EnumInstance) ois.readObject();
/**
* 输出的时候是两个instance
* 并且他们是相等的
* 那这些我们一会通过反编译来看
* 我们其实用的是枚举类持有的对象
* 那我们现在就用data来做实验
*
* 所以我们这里边直接打印data
*
* 第一个输出原始的instance的data
* java.lang.Object@3b07d329
*
*
*/
System.out.println(instance.getData());
/**
* 反序列化回来的对象
* 也是java.lang.Object@3b07d329
* 他们想不想等呢
* 答案是true
* 那枚举类就是这么强大
* 那我们看一下序列号和反序列号对枚举是怎么处理的呢
* 我们打开ObjectInputStream这个类
*
*/
System.out.println(newInstance.getData());
/**
* 这个也是进行一个比较
* 注意它是object类型
* 我们直接run一下
* 结果出来了
* 我们一起来看一下
*
*
*/
System.out.println(instance.getData() == newInstance.getData());
// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;
// Constructor constructor = objectClass.getDeclaredConstructor();
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);
//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();
// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);
// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();
}
}
我们打开ObjectOutputStream这个类,这里面有一个方法叫readEnum
/**
* Reads in and returns enum constant, or null if enum type is
* unresolvable. Sets passHandle to enum constant's assigned handle.
*/
private Enum> readEnum(boolean unshared) throws IOException {
if (bin.readByte() != TC_ENUM) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
}
int enumHandle = handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(enumHandle, resolveEx);
}
String name = readString(false);
Enum> result = null;
Class> cl = desc.forClass();
if (cl != null) {
try {
@SuppressWarnings("unchecked")
Enum> en = Enum.valueOf((Class)cl, name);
result = en;
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name + " does not exist in " +
cl).initCause(ex);
}
if (!unshared) {
handles.setObject(enumHandle, result);
}
}
handles.finish(enumHandle);
passHandle = enumHandle;
return result;
}
看一下这个方法,我们看一下readEnum的依赖,那前面我们讲序列号和反序列号的时候呢,
讲的是checkResolve(readOrdinaryObject(unshared))
/**
* Underlying readObject implementation.
*/
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
totalObjectRefs++;
try {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
现在我们来看一下readEnum,进来private Enum> readEnum(boolean unshared),这个方法上面都是各种校验,
观察重点的东西,看一下String name = readString(false);行,通过readString这个方法呢,获取枚举对象的名称name,
然后看一下行,Enum> result = null;Enum声明成null,Enum> en = Enum.valueOf((Class)cl, name);这一行进行赋值,
再通过这个类型和name来获取枚举常量,那因为枚举中的name唯一的,并且对应一个枚举常量,所以这里拿到的肯定是唯一的枚举
常量,这样就没有创建新的对象,维持了这个对象的单例属性,那ObjectInputStream里边,关于枚举处理的一个方法,还是比较简单的,
而且非常容易理解,所以枚举类对于序列号这种破坏,是不受影响的,那我们接着回来,Test里面,那枚举类不受影响,直接看一下反射呢
package com.learn.design.pattern.creational.singleton;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");
// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);
// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();
// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());
// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
// Class objectClass = LazySingleton.class;
Class objectClass = EnumInstance.class;
/**
* 现在已经报异常了
* 报的是NoSuchException
* 那这个异常就比较有意思了
* 那这个异常就出现在objectClass.getDeclaredConstructor();
* 就是获取构造器的时候呢
* 并没有获得无参构造器
* 那为什么没有获得无参构造器呢
* 那这个困惑我们现在就来解决
* 看一下java.lang.Enum
*
*
*/
Constructor constructor = objectClass.getDeclaredConstructor();
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
constructor.setAccessible(true);
EnumInstance instance = (EnumInstance) constructor.newInstance();
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);
//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();
// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);
// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();
}
}
枚举类的源码最大化一下
public abstract class Enum>
implements Comparable, Serializable
我们看一下这个类是抽象类,往下看,这些都是声明,注意看这里
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
这个方法就是Enum的构造方法,我们再看一下还有别的方法吗,已经没有了,也就是说Enum这个类呢,
只有一个构造器,并且这个不是无参的,而是需要传两个参数,才能构造出来,那我们就在Test里面不调用他的无参构造器,
我们直接按照构造器这个类型直接传进去,一个String,一个Int
Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
package com.learn.design.pattern.creational.singleton;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");
// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);
// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();
// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());
// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
// Class objectClass = LazySingleton.class;
Class objectClass = EnumInstance.class;
// Constructor constructor = objectClass.getDeclaredConstructor();
/**
* 那这一行肯定OK了
* 我们再run一下
* 先不进行对比
* 先run起来
* 看一下行不行
* 果然这一行就过了
*
*
*/
Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance();
/**
* 这个时候我们就可以调用构造器的newInstance方法
* 然后第一个传String"Geely"
* 第二个数字666
* 然后前面用EnumInstance接收一下
* 强转
*
* 在这一行的时候出现了异常
* 而这个异常看一下
* IllegalArgumentException
* 非法的参数异常
* 后面还有说明
* Cannot reflectively create enum objects
* 不能反射去创建对象
* 这个说明说的非常明显
* 鼠标放到这
* at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
* 构造器的417行
* newInstance这个方法里边
* 抛出来的
* 那我们就进newInstance里边看一下
*
*
*
*/
EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);
//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();
// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);
// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();
}
}
刚刚有提示417行
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
找到417行,throw new IllegalArgumentException("Cannot reflectively create enum objects");
if ((clazz.getModifiers() & Modifier.ENUM) != 0)这里就是判断他是不是枚举类型,看一下我们要使用newInstance这个
目标类,是不是枚举类型,所以对于枚举类型,通过反射,失败了,无法进行反射攻击,接着回来,目前来看枚举还是非常强大的,
那刚才说的序列号相关的,反射相关的,都是其他类来处理的,也就是说ObjectInptuStream,还有构造器这个类,他们两处理枚举类的逻辑,
那枚举类本身又什么优势呢,通过EnumInstance,我们看不出来什么,那没有关系,现在分享一个非常有用的反编译工具,JAD,网址就是
这个网址: https://varaneckas.com/jad/
这个网址: https://varaneckas.com/jad/,可以登陆这个网站,来下载JAD,这里面有不同系统的版本,
linux,windows,mac都可以下载到,那下载完成之后呢,下载完之后呢是一个解压包,只要解压就可以使用了,
那来到命令行,一起来操作一下,我已经下载好了,一起来看一下,为了方便我配置了环境变量,把jad放到了path里边,
我们看一下编译出来的目录会在哪里,这里有一个target,我们找到对应的目录,
C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton
这里有一个EnumInstance.class,用JAD来反编译这个class,直接右键copy path,
jad C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton\EnumInstance.class
非常简单,这个命令还有一些参数,可以上网查一下,或者看官方的帮助文档,我们就用最简单的命令来反编译他,我们可以看到反编译
已经完成了
生成了EnumInstance.jad,我们直接进来看一下,因为字体比较大,
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumInstance.java
package com.learn.design.pattern.creational.singleton;
public final class EnumInstance extends Enum
{
private EnumInstance(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumInstance getInstance()
{
return INSTANCE;
}
public static EnumInstance[] values()
{
EnumInstance aenuminstance[];
int i;
EnumInstance aenuminstance1[];
System.arraycopy(aenuminstance = ENUM$VALUES, 0, aenuminstance1 = new EnumInstance[i = aenuminstance.length], 0, i);
return aenuminstance1;
}
public static EnumInstance valueOf(String s)
{
return (EnumInstance)Enum.valueOf(com/learn/design/pattern/creational/singleton/EnumInstance, s);
}
public static final EnumInstance INSTANCE;
private Object data;
private static final EnumInstance ENUM$VALUES[];
static
{
INSTANCE = new EnumInstance("INSTANCE", 0);
ENUM$VALUES = (new EnumInstance[] {
INSTANCE
});
}
}
我们一点点看吧,首先我们看一下这个类是final的,这个比较有意思,我们在代码里面是根本就看不出来的,
那继续往下看,
private EnumInstance(String s, int i)
{
super(s, i);
}
这个是比较符合单例模式的要求的,私有构造器,接着往下看,
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
这个是data的get/set方法,继续,
public static EnumInstance getInstance()
{
return INSTANCE;
}
这个就是我们写的getInstance方法,继续往下看,
public static final EnumInstance INSTANCE;我们可以看到这个INSTANCE对象,是一个static,并且呢,final的,
那么他在什么时候实例化呢,往下看,
static
{
INSTANCE = new EnumInstance("INSTANCE", 0);
ENUM$VALUES = (new EnumInstance[] {
INSTANCE
});
}
它是通过静态块的方式,来实例化他,所以本身Enum这个类啊,他在加载的时候,就把INSTANCE这个对象声明好了,
这里声明这个单例对象呢,和饿汉模式非常像,所以呢通过这些点,本身ENUM这个类,是非常符合单例模式的要求的,
那我们从头再看一遍,final代表这个,构造器私有这里
private EnumInstance(String s, int i)
{
super(s, i);
}
不允许外部进行实例化,同时类变量是静态的,INSTANCE是静态的,没有延迟初始化,通过这个静态块在类加载的时候,
把这个对象初始化完成,所以呢他也是线程安全的,那除了这些之外,还有IO相关的类,反射相关的类,来为枚举类保驾护航,
适当的抛出异常,所以枚举类实现单例模式还是比较优雅的,那关于JAD这个工具,有兴趣的可以多使用他,通过它可以发现
很多有意思的地方,在这里也推荐大家多使用JAD反编译工具,可能能帮你发现一些新大陆,对于某些代码反编译出来,
很容易豁然开朗,那接着回到代码里边,可能枚举类平时使用的不多,项目中使用枚举类也是使用的比较简单的方式,
那这个类里面本身还可以声明方法,那我们现在来领着大家来实战一下
package com.learn.design.pattern.creational.singleton;
public enum EnumInstance {
/**
* 例如我们在这里加一个大括号
* 我们就声明一个protected方法
*
*
*/
INSTANCE{
protected void printTest(){
/**
* 直接输出
* 那还可以继续声明
*
*
*/
System.out.println("Geely Print Test");
}
};
/**
* 为了外部能够访问到printTest方法呢
* 我们还要声明一个方法
* 抽象方法
* 方法名和这个保持一致
* 就可以了
* 但是如果没有这个声明
* 外部是访问不了这个方法的
* 你们也可以试一下
* 讲到这里
* 希望你们通过JAD反编译工具
* 把现在写的类再反编译一次
* 尝试一下自己去看printTest方法到底是怎么回事
* 那也算留一个小作业了
* 相信通过这个过程
* 也能提高很多
* 至少JAD工具会用
* 并且也能看懂
* 为什么现在枚举类写方法的时候
* 我们要这么写
* 我们来到Test里边
*
*
*
*/
protected abstract void printTest();
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 获取这个枚举类型
*
* @return
*/
public static EnumInstance getInstance(){
return INSTANCE;
}
}
package com.learn.design.pattern.creational.singleton;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");
// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);
// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();
// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());
// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;
// Constructor constructor = objectClass.getDeclaredConstructor();
/**
* 那这一行肯定OK了
* 我们再run一下
* 先不进行对比
* 先run起来
* 看一下行不行
* 果然这一行就过了
*
*
*/
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance();
/**
* 这个时候我们就可以调用构造器的newInstance方法
* 然后第一个传String"Geely"
* 第二个数字666
* 然后前面用EnumInstance接收一下
* 强转
*
* 在这一行的时候出现了异常
* 而这个异常看一下
* IllegalArgumentException
* 非法的参数异常
* 后面还有说明
* Cannot reflectively create enum objects
* 不能反射去创建对象
* 这个说明说的非常明显
* 鼠标放到这
* at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
* 构造器的417行
* newInstance这个方法里边
* 抛出来的
* 那我们就进newInstance里边看一下
*
*
*
*/
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);
//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();
// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);
EnumInstance instance = EnumInstance.getInstance();
/**
* 结果已经出来了
* 这个方法已经执行了
* 那我们通过学习枚举类呢
* 你一定能够提高很多
* 那关于我们单例模式
* 这里重点说一下
* 单例模式实现的方案很多
* 每一种都有自己的优缺点
* 所以我们要根据不同的业务场景来选择自己的单例模式
* 还有这种模式是如何演进的
* 这几种单例方式都解决了什么问题
* 他又存在什么问题
* 如果问设计模式的话
* 百分之九十九都会问单例这个模式
* 单例模式看起来非常的简单
* 对于初学者来说他也是非常简单的
* 但是中间就是隔着一层窗户纸
* 只要一捅破
* 单例模式是这些模式中最复杂的模式
* 如果要深入研究的话
* 这里面的知识点还是非常有意思的
* 相信通过学习单例模式呢
* 对于提高个人的思维能力
* 编码能力
* 实操能力
* 这些肯定会有所提高的
* 另外希望有一点
* 如果学了这门课程
* 然后单例模式也都听完了
*
*
*
*/
instance.printTest();
}
}