结论:在 《java 类的加载结论四》 的第16个结论,这里面在复述一下:
命名空间: 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。==> 子类加载器能看见父类加载器加载的类,而父类加载器不能看见子类加载器所加载的类。jvm 这样子设计有效提高了安全性.
特征:
1. 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
2. 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类
总之:子类加载器加载的类能访问父类加载器加载的类,而父类加载器加载的类不能访问子类加载所加载的类,因为子类的类的命名空间范围包含了父加载器的命名空间。
代码示例:
package com.tanruyu.jvm.classloading;
public class MyClassLoaderTest2 {
static {
System.out.println("MyClassLoaderTest2 class init!");
System.out.println("MyClassLoaderTest2 classLoader is "+MyClassLoaderTest2.class.getClassLoader());
}
public static void main(String[] args) throws Exception {
MyClassLoader2 classLoader2 = new MyClassLoader2("MyClassLoader2");
classLoader2.setPath("F:/temp/");
Class<?> loadClass = classLoader2.loadClass("com.tanruyu.jvm.classloading.model.Water");
loadClass.newInstance();
//场景操作一:将target 目录下classpath 下面的目录copy 到 F 盘的temp 目录下,然后启动main 方法输出如下:
// MyClassLoaderTest2 class init!
// MyClassLoaderTest2 classLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
// Water class init!
// Water ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
// Fish class init
// Fish ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
//场景操作二:将target 目录下classpath 下面的目录copy 到 F 盘的temp 目录下,然后删除classpath 下的Water.class 和 Fish.class 文件,然后启动main 方法输出如下:
// MyClassLoaderTest2 class init!
// MyClassLoaderTest2 classLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
// Water class init!
// Water ClassLoader is com.tanruyu.jvm.classloading.MyClassLoader2@74a14482
// Fish class init
// Fish ClassLoader is com.tanruyu.jvm.classloading.MyClassLoader2@74a14482
//场景操作三:将target 目录下classpath 下面的目录copy 到 F 盘的temp 目录下,然后删除classpath 下的Water.class 文件,然后启动main 方法输出如下:
/* MyClassLoaderTest2 class init!
MyClassLoaderTest2 classLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
Water class init!
Water ClassLoader is com.tanruyu.jvm.classloading.MyClassLoader2@74a14482
Fish class init
Fish ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: com/tanruyu/jvm/classloading/model/Water */
//场景操作四:将target 目录下classpath 下面的目录copy 到 F 盘的temp 目录下,然后删除classpath 下的Fish.class 文件,然后启动main 方法输出如下:
/* MyClassLoaderTest2 class init!
MyClassLoaderTest2 classLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: com/tanruyu/jvm/classloading/model/Fish
at com.tanruyu.jvm.classloading.model.Water.<clinit>(Water.java:8)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.tanruyu.jvm.classloading.MyClassLoaderTest2.main(MyClassLoaderTest2.java:15)
Caused by: java.lang.ClassNotFoundException: com.tanruyu.jvm.classloading.model.Fish
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
Water class init!
Water ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2*/
}
}
//以下为 Water 和 Fish 类的源码
//package com.tanruyu.jvm.classloading.model;
//
//public class Water {
//
// static {
// System.out.println("Water class init!");
// System.out.println("Water ClassLoader is " + Water.class.getClassLoader());
// new Fish();
// }
//
//}
//
//package com.tanruyu.jvm.classloading.model;
//
//public class Fish {
//
// static {
// System.out.println("Fish class init");
// System.out.println("Fish ClassLoader is " + Fish.class.getClassLoader());
// new Water();
// }
//
//}
自定义类加载器源码:
package com.tanruyu.jvm.classloading;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader2 extends ClassLoader {
private String path;
private String classLoaderName;
private final String fileExtensionName = ".class";
public MyClassLoader2(String classLoaderName) {
super();
this.classLoaderName = classLoaderName;
}
public MyClassLoader2(String classLoaderName,ClassLoader parentClassLoader) {
super(parentClassLoader);
this.classLoaderName = classLoaderName;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte [] byteArr = loadClassDate(name);
return defineClass(name, byteArr, 0, byteArr.length);
}
private byte[] loadClassDate(String binaryName) {
binaryName = binaryName.replace(".","/");
try(
InputStream is = new FileInputStream(path + binaryName + fileExtensionName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
){
int ch = 0;
while((ch = is.read()) != -1) {
bos.write(ch);
}
return bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void setPath(String path) {
this.path = path;
}
}