类加载器ClassLoader与命名空间

1、类加载器的双亲委派机制:

类加载器ClassLoader与命名空间_第1张图片
2、类加载器的命名空间
该类加载和其所有父类加载器加载的类组成该类加载器的命名空间。
同一命名空间内不会存在全限定名相同的类,
同一命名空间内,子类加载器加载的类可以访问父类加载器加载的类;反之,父类加载器加载的类不能访问自类加载器加载的类。
例:

public class ClassLoaderTest extends ClassLoader{

	private String classLoaderName;
	private String baseUrl;
	
	//自定义findClass方法,只有在使用自定义累加器时,才会调用
	@Override
	public Class findClass(String className){
		System.out.println("自定义findClass被调用...");
		String path = baseUrl + className.replace(".", "\\") + ".class";
		System.out.println("当前加载的类的全限定名是 :" + path);
		byte data[] = findData(path);
		Class calzz = defineClass(className, data, 0, data.length);
		return calzz;
	}
	
	public ClassLoaderTest(String calssLoader) {
		super();
		this.classLoaderName = calssLoader;
	}
	
	public ClassLoaderTest(ClassLoader parent, String calssLoader) {
		super(parent);
		this.classLoaderName = calssLoader;
	}
	
	//设置一个路径,用来存放编译生成的.class文件;
	//该路径与默认的classPath不同,AppClassLoader无法加载该路径下的类,自定义类加载器可以加载该路径下的类
	private void setPath(String baseUrl) {
		this.baseUrl = baseUrl;
	}
	
	//自定义findData 将.class文件解析成byte数组
	private byte[] findData(String className) {
		InputStream in = null;
		byte[] ch = null;
		ByteArrayOutputStream out = null;
		
		try {
			in = new FileInputStream(new File(className));
			out = new ByteArrayOutputStream();
			int a = 0;
			while( -1 != (a = in.read())) {
				out.write(a);
			}
			ch = out.toByteArray();
			return ch;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return ch;
	}


	public static void main(String[] args) throws Exception {
		ClassLoaderTest loader1 = new ClassLoaderTest("loader1");
		loader1.setPath("F:\\Test\\");//设置自定义类加载器的加载路径
		//被类加载器加载后,得到Class对象
		Class c1 = loader1.loadClass("xcs.com.cn.MyTest1");
		Object o1 = c1.newInstance();//实例化MyTest1
		System.out.println();
		
		ClassLoaderTest loader2 = new ClassLoaderTest("loader1");
		loader2.setPath("F:\\Test\\");
		Class c2 = loader2.loadClass("xcs.com.cn.MyTest2");
		Object o2 = c2.newInstance();
		System.out.println();
	}
}

场景一:
将编译生成的.class文件全部copy到F:\test\路径下,将classPath下的生成的MyTest2.class删除,运行查看控制台输出
结论:Mytest1由AppClassLoader加载,MyTest2由自定义的ClassLoaderTest类加载器加载。

public class MyTest1 {
	public MyTest1() {
		System.out.println("MyTest1 is loader by :" + this.getClass().getClassLoader());
	}
}

    public class MyTest2 {
    	public MyTest2() {
    		System.out.println("MyTest2 is loader by :" + this.getClass().getClassLoader());
    	}
    }

类加载器ClassLoader与命名空间_第2张图片

场景二:
将编译生成的.class文件全部copy到F:\test\路径下,将classPath下的生成的MyTest2.class删除,在Mytest2的构造中调用new MyTest1()
结论:子类加载器加载的类可以调用父类加载器加载的类(同一命名空间)Mytest1由AppClassLoader加载,MyTest2由自定义的ClassLoaderTest类加载器加载。

public class MyTest1 {
	public MyTest1() {
		System.out.println("MyTest1 is loader by :" + this.getClass().getClassLoader());
	}
}
public class MyTest2 {
	public MyTest2() {
		System.out.println("MyTest2 is loader by :" + this.getClass().getClassLoader());
		new MyTest1(); //在Mytest2中掉用Mytest1
	}
}

类加载器ClassLoader与命名空间_第3张图片
场景三:
将编译生成的.class文件全部copy到F:\test\路径下,将classPath下的生成的MyTest2.class删除,在Mytest1的构造中调用new MyTest2()
结论:父类加载器加载的类不可以调用子类加载器加载的类(不在同一命名空间)Mytest1由AppClassLoader加载,MyTest2由自定义的ClassLoaderTest类加载器加载。

public class MyTest1 {
	public MyTest1() {
		System.out.println("MyTest1 is loader by :" + this.getClass().getClassLoader());
		new  MyTest2(); //在MyTest1中调用MyTest2
	}
}
public class MyTest2 {
	public MyTest2() {
		System.out.println("MyTest2 is loader by :" + this.getClass().getClassLoader());
	}
}

类加载器ClassLoader与命名空间_第4张图片
3、双亲委派的优点
通过双亲委派,java自带的系统类一定是被java自带的类加载器加载,保证了系统的安全性。

4、类加载器也是类,被谁加载
拓展类加载器和应用类加载器都是被启动类加载器加载;启动类加载器是JVM的一部分,由C++编写,虽JVM的创建而创建。
自定义类加载器由应用类加载器加载。

你可能感兴趣的:(JVM)