当使用不同的类加载器时,也会使单例失效,如下:
单例为:
自定义的类加载器为:
测试案例如下:
输出结果为:
咱们慢慢来看这些信息。
1 Singleton.class与singletonClass
前者是系统类加载器加载器的,后者是我们自定义的类加载器加载的,虽然他们的字节码相同,但由不同的类加载器加载后就是不同的类了,所以两者的==和eaquals都为false。
2 constructor1、constructor2、constructor3
constructor1、constructor2都是通过调用Singleton.class.getDeclaredConstructor()得来的,但是两者并不是同一个对象,他们的==为false,equals为true。看getDeclaredConstructor源码就可以理解:
再看构造器的eequals方法
先通过比较是否是同一个类的构造器,然后再比较他们的参数是否一致,所以constructor1和constructor2的equals方法为true。对于constructor3和constructor1、constructor2,他们所属的类就是不一样的,即getDeclaringClass() == other.getDeclaringClass()为false。
3 singleton1和singleton3
singleton1是由constructor1构造器通过反射生成的对象,constructor3是通过constructor3构造器通过反射生成的对象,这些对象肯定都不是同一个对象。我有个疑问就是:通过constructor1.newInstance()会去执行Singleton的无参构造函数,打印出
然而执行constructor3.newInstance()却并没有打印出无参构造函数中的信息,这背后的原理希望你们能帮我解答。
有关类加载器的内容,请见后续文章
单例为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
final
class
Singleton{
private
static
final
Singleton instance=
new
Singleton();
private
Singleton(){
System.out.println(
"执行构造函数"
);
System.out.println(
"类加载器="
+
this
.getClass().getClassLoader());
}
public
static
Singleton getInstance(){
return
instance;
}
}
|
自定义的类加载器为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public
class
MyClassLoader
extends
ClassLoader{
private
String name;
private
String classPath;
public
MyClassLoader(String name){
super
(
null
);
this
.name = name;
}
@Override
protected
Class<?> findClass(String name)
throws
ClassNotFoundException {
byte
[] b=getClassBytes(name);
return
this
.defineClass(name, b,
0
,b.length);
}
private
byte
[] getClassBytes(String name) {
String classFullPath=classPath+
"/"
+name.replace(
"."
,
"/"
)+
".class"
;
byte
[] data=
null
;
try
{
FileInputStream fileInputStream=
new
FileInputStream(classFullPath);
ByteArrayOutputStream out=
new
ByteArrayOutputStream();
IOUtils.copy(fileInputStream,out);
data=out.toByteArray();
}
catch
(Exception e) {
e.printStackTrace();
}
return
data;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
String getClassPath() {
return
classPath;
}
public
void
setClassPath(String classPath) {
this
.classPath = classPath;
}
}
|
测试案例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
static
void
testClassLoader()
throws
Exception{
Singleton singleton=Singleton.getInstance();
MyClassLoader myClassLoader=
new
MyClassLoader(
"myClassLoader"
);
myClassLoader.setClassPath(
"D:/important"
);
Class singletonClass=myClassLoader.findClass(
"com.lg.design.singleton.hungry.Singleton"
);
System.out.println(
"singletonClass.getClassLoader() : "
+singletonClass.getClassLoader());
System.out.println(
"Singleton.class==singletonClass : "
+(Singleton.
class
==singletonClass));
System.out.println(
"Singleton.class.equals(singletonClass) : "
+(Singleton.
class
.equals(singletonClass)));
Constructor constructor1=Singleton.
class
.getDeclaredConstructor();
Constructor constructor2=Singleton.
class
.getDeclaredConstructor();
Constructor constructor3=singletonClass.getDeclaredConstructor();
System.out.println(
"constructor1==constructor2 : "
+(constructor1==constructor2));
System.out.println(
"constructor1.equals(constructor2) : "
+constructor1.equals(constructor2));
System.out.println(
"constructor1==constructor : "
+(constructor1==constructor3));
System.out.println(
"constructor1.equals(constructor3) : "
+constructor1.equals(constructor3));
constructor1.setAccessible(
true
);
Object singleton1=constructor1.newInstance();
constructor3.setAccessible(
true
);
Object singleton3=constructor3.newInstance();
System.out.println(
"singleton : "
+singleton);
System.out.println(
"singleton1 : "
+singleton1);
System.out.println(
"singleton3 : "
+singleton3);
System.out.println(
"singleton1==singleton3 : "
+(singleton1==singleton3));
}
|
输出结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader
@417470d0
singletonClass.getClassLoader() : com.lg.design.singleton.hungry.MyClassLoader
@470d1f30
Singleton.
class
==singletonClass :
false
Singleton.
class
.equals(singletonClass) :
false
constructor1==constructor2 :
false
constructor1.equals(constructor2) :
true
constructor1==constructor :
false
constructor1.equals(constructor3) :
false
执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader
@417470d0
singleton : com.lg.design.singleton.hungry.Singleton
@77e3cabd
singleton1 : com.lg.design.singleton.hungry.Singleton
@c137bc9
singleton3 : com.lg.design.singleton.hungry.Singleton
@5323cf50
singleton1==singleton3 :
false
|
咱们慢慢来看这些信息。
1 Singleton.class与singletonClass
前者是系统类加载器加载器的,后者是我们自定义的类加载器加载的,虽然他们的字节码相同,但由不同的类加载器加载后就是不同的类了,所以两者的==和eaquals都为false。
2 constructor1、constructor2、constructor3
constructor1、constructor2都是通过调用Singleton.class.getDeclaredConstructor()得来的,但是两者并不是同一个对象,他们的==为false,equals为true。看getDeclaredConstructor源码就可以理解:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private
Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int
which)
throws
NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for
(Constructor<T> constructor : constructors) {
if
(arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
//这里在获取构造器的时候就是用的复制
return
getReflectionFactory().copyConstructor(constructor);
}
}
throw
new
NoSuchMethodException(getName() +
".<init>"
+ argumentTypesToString(parameterTypes));
}
|
再看构造器的eequals方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
boolean
equals(Object obj) {
if
(obj !=
null
&& obj
instanceof
Constructor) {
Constructor<?> other = (Constructor<?>)obj;
if
(getDeclaringClass() == other.getDeclaringClass()) {
/* Avoid unnecessary cloning */
Class<?>[] params1 = parameterTypes;
Class<?>[] params2 = other.parameterTypes;
if
(params1.length == params2.length) {
for
(
int
i =
0
; i < params1.length; i++) {
if
(params1[i] != params2[i])
return
false
;
}
return
true
;
}
}
}
return
false
;
}
|
先通过比较是否是同一个类的构造器,然后再比较他们的参数是否一致,所以constructor1和constructor2的equals方法为true。对于constructor3和constructor1、constructor2,他们所属的类就是不一样的,即getDeclaringClass() == other.getDeclaringClass()为false。
3 singleton1和singleton3
singleton1是由constructor1构造器通过反射生成的对象,constructor3是通过constructor3构造器通过反射生成的对象,这些对象肯定都不是同一个对象。我有个疑问就是:通过constructor1.newInstance()会去执行Singleton的无参构造函数,打印出
1
2
|
执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader
@417470d0
|
然而执行constructor3.newInstance()却并没有打印出无参构造函数中的信息,这背后的原理希望你们能帮我解答。
有关类加载器的内容,请见后续文章