本文主要是介绍黑马程序员---类加载器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
------- android培训、java培训、期待与您交流! ----------
简要介绍什么是类加载器和类加载器的作用
Class类中的方法:
ClassLoader | getClassLoader() 返回该类的类加载器。 |
ClassLoader类中的方法:
ClassLoader | getParent() 返回委托的父类加载器。 |
package cn.itcast.day2;
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(
ClassLoaderTest.class.getClassLoader()
.getClass().getName());//sun.misc.Launcher$AppClassLoader
System.out.println(
System.class.getClassLoader() ); //null,不代表没有类加载器,而代表它是一个特殊的类加载器BootStrap。
//以下代码,验证 类加载器 的父子关系。
ClassLoader loader = ClassLoaderTest.class.getClassLoader();//AppClassLoader
while(loader!=null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
类加载器 的 父子关系,运行结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null (代表:BootStrap)
类加载器之间的父子关系和管辖范围图:
我们将刚才写的那个类,右键 Export (以java的 jar 包的格式)导出到 jre/lib/ext/*.jar 文件夹下。
再运行程序,该类的类加载器变成了 ExtClassLoader。
此时的环境状态是classpath目录有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,
为什么由爸爸(ExtClassLoader)加载了,而没有被儿子(AppClassLoader)加载,这时候我们就需要了解类加载的具体过程和原理了。
类加载器的委托机制
先来说说ClassLoader家族的树型结构:
在这里,除了有系统自带的那些ClassLoader以外,我们还可以写自己的ClassLoader 挂到这个ClassLoader tree下面去。
我们写自己的ClassLoader的时候,都必须继承ClassLoader这个类。
你在new自己的ClassLoader实例的时候,就可以给它指定Parent。
要想把你的ClassLoader挂在家族tree下面去,就必须给它指定一个爸爸。也可以使用默认。
我们在最后也会写一个自己的类加载器,去挂到系统自带的来加载器树型结构下面。然后用我们的类加载器去加载我们特定的目录。
我们把我们加密的类放到那个特定目录下,当人家在使用 我们加密的类 的时候 就只能用 我们特定的类加载器 去加载。
我们的类加载器在加载的过程中就对这些类进行解密。解密完出来就是完好的字节码了。你如果用普通的类加载器加载进去就是一堆看不懂的没用的东西。
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。
有一道面试题:能不能自己写个类叫java.lang.System,我们要答:通常是不可以的,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用java系统提供的System。但是我们还有一个办法是可以自己写一个类加载器,为了不让它采用类加载器的委托机制,这个类还得特殊的写。
把先前编写的类加入到jdk的rt.jar中,会有怎样的效果呢?不行!!!看来是不能随意将自己的class文件加入进rt.jar文件中的。
编写自己的类加载器
※知识讲解(写类加载器的原理):
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date{
public String toString() {
return "hello itcast";
}
}
package cn.itcast.day2;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyCLassLoaderTest {
public static void main(String[] args) throws Exception{
String src = args[0];
String destpath = args[1];
FileInputStream fis = new FileInputStream(src);
String dest = destpath + "\\" + src.substring(src.lastIndexOf("\\")+1);
FileOutputStream fos = new FileOutputStream(dest);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips, OutputStream ops) throws Exception {
int b = -1;
while((b=ips.read())!=-1) {
ops.write(b^0xff);
}
}
}
*有包名的类 不能调用 没包名的类。
package cn.itcast.day2;
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(new ClassLoaderAttachment().toString());
}
}
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date{
public String toString() {
return "hello itcast";
}
}
package cn.itcast.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws Exception{
String src = args[0];
String destpath = args[1];
FileInputStream fis = new FileInputStream(src);
String dest = destpath + "\\" + src.substring(src.lastIndexOf("\\")+1);
FileOutputStream fos = new FileOutputStream(dest);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips, OutputStream ops) throws Exception {
int b = -1;
while((b=ips.read())!=-1) {
ops.write(b^0xff);
}
}
private String classDir;
@Override
//子类不能比父类抛出更广泛的异常。
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + "\\" + name.substring(name.lastIndexOf(".")+1) + ".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();//定义一个字节数组输出流。(自带一个数组,将输出的内容就存放到这个字节数组里)
cypher(fis,bos);//解密到bos里面。
fis.close();
System.out.println("aaa");
byte[] bytes = bos.toByteArray();//创建一个新分配的 byte 数组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中。
return defineClass(bytes, 0, bytes.length);//将字节数组变成Class文件。
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader() {}
public MyClassLoader(String classDir) {
this.classDir = classDir;
}
}
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
System.out.println("xxx");
Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
Date d1 = (Date) clazz.newInstance();
System.out.println(d1);
}
}
※老师将itcast.jar删掉后,没有重启Eclipse,致使ExtClassLoader一直在找 jre/lib/ext 文件夹中的itcast.jar包,导致报错不断。
查错方法:
Window --- show view --- problems 窗口中查看 error 信息。
一个类加载器的高级问题分析
package cn.itcast.itcastweb.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();//这个out是往浏览器上面打。
ClassLoader loader = this.getClass().getClassLoader();
while(loader!=null) {
out.println(loader.getClass().getName() + "<br>");
loader = loader.getParent();
}
out.flush();
out.close();
}
}
把 MyServlet.class 文件打 jar 包,放到 ext 目录中,重启 tomcat ,发现找不到 HttpServlet 的错误。
------- android培训、java培训、期待与您交流! ----------
这篇关于黑马程序员---类加载器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!