反射中,Class.forName和classloader的区别
java中class.forName()和classLoader都可用来对类进行加载。 class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static 块。 而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在 newInstance才会去执行static块。 Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了 newInstance()方法采用调用构造函数,创建类的对对象。
根据运行结果,可以看到,classloader并没有执行静态代码块,如开头的理论所说。
而下面的Class.forName则是夹在完之后,就里面执行了静态代码块,可以看到,2个类,line和point的 静态代码块执行结果是一起的,然后才是各自的打印结果。
。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用 来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能 更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等
类加载器的树状组织结构
引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现 的,并不继承自
java.lang.ClassLoader。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提 供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader/App classloader):它根据 Java 应用的类路径 (CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
自定义类加载器
首先介绍自定义类的应用场景:
(1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将 编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自 定义ClassLoader在加载类的时候先解密类,然后再加载。
(2)从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载 器,从指定的来源加载类。
(3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安 全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器来从某个网络地址上读取加密后 的字节代码,接着进行解密和验证,后定义出在Java虚拟机中运行的类。
1、节约系统资源。只要,这个类已经被加载过了,就不会在次加载。
2、保证 Java 核心库的类型安全。
双亲委派模型的工作过程如下:
(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加 载的类。
(2)如果没有找到,就去委托父类加载器去加载(如代码c = parent.loadClass(name, false)所示)。 父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托 父类的父类去加载,一直到启动类加载器。因为如果父加载器为空了,就代表使用启动类加载器作为父 加载器去加载。
(3)如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),则会抛出一个异 常ClassNotFoundException,然后再调用当前加载器的findClass()方法进行加载。
双亲委派模型的好处:
(1)主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String。
(2)同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不 同的 ClassLoader加载就是不同的两个类。
2. 自定义类加载器 (1)从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调 用当前加载器的findClass来完成加载。 (2)因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法,下面是一个实际例 子,在该例中我们用自定义的类加载器去加载我们事先准备好的class文件。
public class MyClassLoader extends ClassLoader { MyClassLoader(){ } MyClassLoader(ClassLoader classLoader){ super(classLoader); } @Override protected Class> findClass(String name) throws ClassNotFoundException { File file=new File("D:/People.class"); try { byte[] bytes = getFile(file); //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class Class> c = this.defineClass(name, bytes, 0, bytes.length); return c; } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } public byte[] getFile(File file) throws Exception { // 这里要读入.class的字节,因此要使用字节流 FileInputStream fis = new FileInputStream(file); FileChannel fc = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel wbc = Channels.newChannel(baos); ByteBuffer by = ByteBuffer.allocate(1024); while (true){ int i = fc.read(by); if (i == 0 || i == -1) break; by.flip(); wbc.write(by); by.clear(); } fis.close(); return baos.toByteArray(); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { MyClassLoader myClassLoader = new MyClassLoader(); Class> aClass = Class.forName("People", true, myClassLoader); Object o = aClass.newInstance(); System.out.println(o); System.out.println(o.getClass().getClassLoader()); }
线程上下文类加载器
Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的 代码可以通过此类加载器来加载类和资源。
如果类库提供了 SPI 接口,并且利用线程上下文类加载器来加载 SPI 实现的 Java 类,有可能会找不 到 Java 类。如果出现了 NoClassDefFoundError 异常,首先检查当前线程的上下文类加载器是否 正确。通过 Thread.currentThread().getContextClassLoader() 就可以得到该类加载器。该类加载 器应该是该模块对应的类加载器。如果不是的话,可以首先通过 class.getClassLoader() 来得到模块对应的类加载器,再通过 Thread.currentThread().setContextClassLoader() 来设置当 前线程的上下文类加载器。
对于运行在 Java EE? 容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。 不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个 类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 *Java Servlet 规范中 的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个 例外是:Java* *核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。* 绝大 多数情况下,Web 应用的开发人员不需要考虑与类加载器相关的细节。下面给出几条简单的原则
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)