JVM预定义的三种类型类加载器:
除了以上列举的三种类加载器,还有一种比较特殊的类型 — 线程上下文类加载器。
Java虚拟机的第一个类加载器是Bootstrap,这个加载器很特殊,它不是Java类,因此它不需要被别人加载,它嵌套在Java虚拟机内核里面,也就是JVM启动的时候Bootstrap就已经启动,它是用C++写的二进制代码(不是字节码),它可以去加载别的类。这也是我们在测试时为什么发现System.class.getClassLoader()结果为null的原因,这并不表示System这个类没有类加载器,而是它的加载器比较特殊,是BootstrapClassLoader,由于它不是Java类,因此获得它的引用肯定返回null。
委托机制具体含义 :
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
委托机制的意义: 防止内存中出现多份同样的字节码。
比如两个类A和类B都要加载System类:1)调用findLoadedClass(String) 来检查是否已经加载类
2)在父类加载器上调用loadClass方法。如果父亲不能加载,一次一级一级传给子类
3)调用子类findClass(String) 方法查找类
package
testClass;
public
class
Animal {
private
static
int
num =
0
;
public
void
say(String name){
num++;
System.out.println(name+
":"
+num);
}
}
package
classLoader;
import
java.io.ByteArrayOutputStream;
import
java.io.File;
import
java.io.FileInputStream;
public
class
MyClassLoader
extends
ClassLoader {
// 被加载的类存放的路径
private
String path = MyClassLoader.getSystemClassLoader().getResource(
""
).getPath();
@Override
public
Class<?> findClass(String name) {
System.out.println(
"重写的findClass..."
);
byte
[] data = loadClassData(name);
return
defineClass(name, data,
0
, data.length);
}
public
byte
[] loadClassData(String name) {
try
{
name = name.replace(
"."
,
"/"
);
FileInputStream is =
new
FileInputStream(
new
File(path + name +
".myclass"
));
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
int
b =
0
;
while
((b = is.read()) != -
1
) {
baos.write(b);
}
return
baos.toByteArray();
}
catch
(Exception e) {}
return
null
;
}
}
package
classLoader;
import
java.lang.reflect.Method;
public
class
ClassLoaderTest {
public
static
void
main(String[] args)
throws
Exception {
MyClassLoader c1 =
new
MyClassLoader();
Class<?> clazz = c1.loadClass(
"testClass.Animal"
);
Object animal = clazz.newInstance();
Method sayMethod = clazz.getMethod(
"say"
,
new
Class[]{String.
class
});
sayMethod.invoke(animal,
new
Object[]{
"wudiyong"
});
Class<?> clazz2 = c1.loadClass(
"testClass.Animal"
);
Object animal2 = clazz2.newInstance();
Method sayMethod2 = clazz2.getMethod(
"say"
,
new
Class[]{String.
class
});
sayMethod2.invoke(animal2,
new
Object[]{
"wudiyong"
});
}
}
Class<?> clazz2 = c1.loadClass(
"testClass.Animal"
);改成
MyClassLoader c2 =
new
MyClassLoader();
Class<?> clazz2 = c2.loadClass(
"testClass.Animal"
);
package
classLoader;
import
java.io.ByteArrayOutputStream;
import
java.io.File;
import
java.io.FileInputStream;
public
class
MyClassLoader
extends
ClassLoader {
// 被加载的类存放的路径
private
String path = MyClassLoader.getSystemClassLoader().getResource(
""
).getPath();
@Override
public
Class<?> findClass(String name) {
System.out.println(
"重写的findClass..."
);
byte
[] data = loadClassData(name);
return
defineClass(name, data,
0
, data.length);
}
@Override
public
Class<?> loadClass(String name) {
/*
* 这里不可省,因为loadClass会被执行两次,两次的参数不一样
*/
if
(name.startsWith(
"java."
)) {
try
{
return
super
.loadClass(name,
false
);
}
catch
(ClassNotFoundException e) {}
}
/*
* 用于查找是否已加载过了,一个类加载器如果重复加载同一个类会在调用defineClass方法时报错:
* attempted duplicate class definition
*/
Class<?> c = findLoadedClass(name);
if
(c ==
null
) {
c = findClass(name);
}
return
c;
}
public
byte
[] loadClassData(String name) {
try
{
name = name.replace(
"."
,
"/"
);
FileInputStream is =
new
FileInputStream(
new
File(path + name +
".class"
));
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
int
b =
0
;
while
((b = is.read()) != -
1
) {
baos.write(b);
}
return
baos.toByteArray();
}
catch
(Exception e) {
return
null
;
}
return
null
;
}
}
答案:通常不可以,但可以采取另类方法达到这个需求。
解释:为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。 但是,我们可以 自己定义一个类加载器来达到这个目的 ,为了避免双亲委托机制,这个类加载器也必须是特殊的,需要重写loadClass方法。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。版权声明:本文为博主原创文章,未经博主允许不得转载。