01.类加载器
为什么要研究类加载的全过程?
---有助于理解JVM的运行过程
---更深入了解Java动态性(解热部署、动态加载),提高程序的灵活性
---最重要的一点儿是:有利于分析各种Web容器,Android插件化的原理
类加载机制
JVM将class文件加载到内存,并对数据进行校验、解析和初始化,最终形成JVM能够直接使用的Java类型的过程
抄图如下:
类加载全过程
1、加载:
将字节码加载到内存中 ,并将这数据转换成方法区中运行的数据结构,在堆中生成一个表示这个类的java.lang.Class对象,作为方法区内数据的访问入口,当然这个过程需要类的加载器的参与
2、链接:
将Java类的二进制代码合并到JVM的运行状态中的过程,期间经过
1)验证:
--确保加载类的信息符合JVM规范,没有安全方面的问题
2)准备:
--正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都在方法区中进行分配
注意:在准备阶段为类变量进行赋初值,比如static int a= 10;那么它的初始值是0,只有在初始化的时候,a的值才会变成10;
3)解析:
--虚拟机中常量池内的符号引用替换为直接引用的过程
注意字符串常量值:一般类名、方法名、参数名、变量名都属于常量。
3、初始化
1)初始化阶段是执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中语句合并产生的。
类构造器是我们创建不了的。
2)当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发其父类的初始化
3)虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁和同步。
4)当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化。
在准备阶段为类变量进行赋初值,比如static int a= 10;那么它的初始值是0,只有在初始化的时候,a的值才会变成10;
字符串常量值:一般类名、方法名、参数名、变量名都属于常量。
类构造器是我们创建不了的。
此处需要一张图片
类的主动引用和被动引用
1.类的主动引用和被动引用(一定会发生类的初始化)
l new一个类的对象
l 调用类的静态成员(除了final常量)和静态方法
l 使用java.lang.reflect包的方法对类进行反射和调用
l 当虚拟机启动,java Demo则一定会初始化Demo类,就是先启动main方法所在的类
l 当初始化一个类,如果它的父类没有被初始化,则先会初始化它的父类
2.类的被动引用不会发生初始化
l 通过数组定义类引用不会触发此类的初始化:
A a[] = new A[];这样不会触发A类的初始化。
l 当访问一个静态域时,只有真正声明这个域的类才会初始化:
通过子类访问父类声明的静态域的时候,父类会初始化,但是子类不会初始化。
示例代码:(测试主动引用和被动引用)
1 package com.chunjiangchao.classloader; 2 3 4 /** 5 * 类的引用的测试 6 * @author chunjiangchao 7 * 8 */ 9 public class UseClassDemo { 10 static{ 11 System.out.println("静态初始化UseClassDemo"); 12 } 13 public static void main(String[] args) throws ClassNotFoundException { 14 System.out.println("执行UseClassDemo的main方法"); 15 16 //主动引用 17 /*静态初始化GrandFather 18 静态初始化类Father 19 静态初始化Son 20 执行构造方法GrandFather 21 执行构造方法Father 22 执行构造方法Son*/ 23 // new Son(); 24 Class.forName("com.chunjiangchao.classloader.Son"); 25 /**会执行 26 * 静态初始化GrandFather 静态初始化类Father 27 * //Son.money = 10; 28 */ 29 /** //不会执行静态代码块 30 // System.out.println(Son.size); 31 Son [] son = new Son[10]; 32 */ 33 } 34 35 } 36 class Son extends Father{ 37 static { 38 System.out.println("静态初始化Son"); 39 } 40 public Son(){ 41 System.out.println("执行构造方法Son"); 42 } 43 } 44 class Father extends GrandFather{ 45 public static int money = 1;//静态变量 46 public final static int size = 20;//静态常量 47 static{ 48 System.out.println("静态初始化类Father"); 49 money = 1000; 50 } 51 public Father(){ 52 System.out.println("执行构造方法Father"); 53 } 54 } 55 class GrandFather extends Object{ 56 static{ 57 System.out.println("静态初始化GrandFather"); 58 } 59 public GrandFather(){ 60 System.out.println("执行构造方法GrandFather"); 61 } 62 }
深入类加载器
类加载器原理
类加载器树状结构,双亲委托(代理)机制
自定义类加载器(文件、网络、加密)
线程上下文类加载器
服务器类加载原理和OSGI介绍
类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中运行时的数据结构,在堆中生成代表这个类的java.lang.Class对象,作为方法区类数据的访问入口
类缓存
标准的Javase类加载器可以按照需求加载类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM的垃圾回收器可以回收这些Class对象
java. lang.ClassLoader类介绍
作用:
--java.lang.ClassLoader类的基本职责就是根据一个制定类的名称,找到或者生成其对应的字节码,然后从字节码中定义一个Java类,即java.lang.Class类的一个实例。
--除此之外,ClassLoader还负责加载Java应用所需要的资源,如图像文件和配置文件等。
相关方法:
getParent()返回该类加载器的父类加载器
loadClass(String name)加载名称为name的类,返回的结果是java.lang.Class对象
findClass(String name)查找名称为name的类,返回的结果是java.lang.Class对象
findLoadedClass(String name)查找名称为name的已经被加载的类,返回的结果是java.lang.Class类的实例
defineClass(String name,byte[] b,int off,int len)把字节数组b中的内容转换成Java类,返回的结果是java.lang.Class类的实例。这个方法是final
resolveClass(Class<?> c)链接制定的Java类
--对于上面给出的方法,表示类名称的name参数的值是类的二进制名称,需要注意内部类的表示方式,如:com.chunjiangchao.Sample$1和com.example.Sample$Inner等的表示
类加载器的层次结构(树状结构)
引导类加载器(bootstrap class loader)
l 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或者sun.boot.class.path路径下的内容),使用C++代码来写的,并不继承自java.lang.ClassLoader
l 加载扩展类和应用程序类加载器,并指定他们的父类加载器
l 注意:BootStrap class loader是用C++写的,其余的是用Java写的
扩展类加载器(extensions class loader)
l 用来加载Java的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录下查找并加载Java类
l 由sun.misc.Launcher$ExtClassLoader实现
应用程序类加载器(application class loader)
l 它根据Java应用的类路径(classpath,java.class.path路径),一般来说,java应用的类都是由它来完成加载的。
l 由sun.misc.Launcher$AppClassLoader实现
自定义类加载器
l 开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,易满足一些特殊的需求
示例代码(测试双亲委托机制)
1 package com.chunjiangchao.classloader; 2 /** 3 * 测试类加载器的层次结构、双亲委托机制 4 * @author chunjiangchao 5 * 6 */ 7 public class ClassLoaderDemo2 { 8 9 public static void main(String[] args) { 10 System.out.println(ClassLoader.getSystemClassLoader()); 11 System.out.println(ClassLoader.getSystemClassLoader().getParent()); 12 System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent()); 13 Object o = new Object(); 14 System.out.println(o.getClass().getClassLoader()); 15 /**** 16 打印结果如下 17 sun.misc.Launcher$AppClassLoader@1a752144 18 sun.misc.Launcher$ExtClassLoader@7fdb04ed 19 null 20 null 21 java.lang包的类是BootStrapClassLoader 加载的 22 */ 23 } 24 25 }
类加载器的代理模式
代理模式
l 交给加载器来加载指定的类
双亲委托机制
l 就是某个特定的加载器在接收到加载类的请求的时候,首先将加载任务委托给父类的加载器,一次追索,知道最高的级别的,如果父类加载器可以完成加载任务,就成功返回;只有父类加载器无法完成此加载请求,才自己去加载。如果加载不到就报错
l 双亲委托机制保证了Java核心库的类型安全
这种机制保证不会出现用户自己能定义java.lang.Object 、java.lang.String等类的情况
l 类加载器除了用于加载类,也是安全最基本的屏障
双亲委托机制:这种机制的好处就是不会随随便便的加载用户写的类。
比如用户写了一个lang.String的类和系统的String的类同一个名字,这个时候,先交给用户自己定义的类加载器-》App classloader-》Ext classloader-》bootstrop classloader,在bootstrop classloader里面发现rt里面有String类,那么就会加载rt里面的String类不会加载用户自定义的String类。这样做的好处是安全,优先使用系统的类。
双亲委托机制是代理模式的一种
l 并不是所有的类加载器都采用双亲委托机制
Tomcat服务器类加载器也采用代理模式,但是所不同的是,它首先尝试自己去加载这个类,如果找不到再代理给父类加载器。这与一般类加载器是相反的。
自定义文件系统加载器
(可以使用自己的类加载器加载class文件,同样采用双亲委托机制)
1 package com.chunjiangchao.classloader; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.io.InputStream; 8 9 /** 10 * 自定义文件系统类加载器,采用双亲委托机制,先把请求交给父加载器去加载,如果父加载器加载不到,才开始在loadClass中加载数据 11 * @author chunjiangchao 12 * 13 */ 14 public class FileClassLoader extends ClassLoader { 15 private String binDir; 16 public FileClassLoader(String binDir){ 17 this.binDir = binDir; 18 } 19 @Override 20 public Class<?> loadClass(String name) throws ClassNotFoundException { 21 //查询这个class文件是否已经被加载,如果已经被加载直接拿过来使用即可 22 Class<?> clazz = findLoadedClass(name); 23 if(clazz!=null) 24 return clazz; 25 else{ 26 ClassLoader parentClassLoader = getParent(); 27 System.out.println(parentClassLoader.getClass().getName()); 28 29 try { 30 clazz = parentClassLoader.loadClass(name);//采用双亲委派机制 31 } catch (Exception e) { 32 //可能会找不到,抛出异常 33 // e.printStackTrace(); 34 } 35 //clazz = null;//如果不使用parentClassLoader,那么Object类也要在bin文件夹中,不然会报错 36 if(clazz!=null){ 37 return clazz; 38 }else{ 39 System.out.println("一般走不到这块,因为AppClassLoader已经可以将当前bin文件中的class文件加载到"); 40 //将文件读入内存,然后开始加载 41 byte[] classBytes = getClassBytes(name); 42 if(classBytes==null){ 43 throw new ClassNotFoundException("文件没找到"); 44 }else{ 45 clazz = defineClass(name,classBytes, 0, classBytes.length); 46 } 47 } 48 } 49 return clazz; 50 } 51 private byte[] getClassBytes(String className){ 52 String classFilePath = binDir+ className.replaceAll("[.]", "/")+".class"; 53 System.out.println(classFilePath); 54 InputStream inputStream = null; 55 ByteArrayOutputStream baos = null; 56 try { 57 inputStream = new FileInputStream(classFilePath); 58 baos = new ByteArrayOutputStream(); 59 int length = 0; 60 byte buffer[] = new byte[1024]; 61 while((length = inputStream.read(buffer))!=-1){ 62 baos.write(buffer, 0, length); 63 } 64 return baos.toByteArray(); 65 } catch (FileNotFoundException e) { 66 e.printStackTrace(); 67 } catch (IOException e) { 68 e.printStackTrace(); 69 }finally{ 70 if(baos!=null){ 71 try { 72 baos.close(); 73 } catch (IOException e) { 74 e.printStackTrace(); 75 } 76 } 77 if(inputStream!=null){ 78 try { 79 inputStream.close(); 80 } catch (IOException e) { 81 e.printStackTrace(); 82 } 83 } 84 } 85 return null; 86 87 88 } 89 90 }
在本程序中测试使用的model类
1 package com.chunjiangchao.classloader; 2 3 public class FileClassLoaderUse { 4 static { 5 System.out.println("FileClassLoaderUse类被加载了"); 6 } 7 public int age = 10; 8 }
测试文件加载系统FileClassLoaderDemo类
1 package com.chunjiangchao.classloader; 2 /** 3 * 测试文件加载系统 4 * @author chunjiangchao 5 */ 6 public class FileClassLoaderDemo { 7 8 public static void main(String[] args) { 9 FileClassLoader fileClassLoader = new FileClassLoader("D:/eclipse_workspace01/MyClassLoader/bin/"); 10 try { 11 Class<?> fileClassLoaderUse01 = fileClassLoader.loadClass("com.chunjiangchao.classloader.FileClassLoaderUse"); 12 Class<?> fileClassLoaderUse02 = fileClassLoader.loadClass("com.chunjiangchao.classloader.FileClassLoaderUse"); 13 14 FileClassLoaderUse fileClassLoaderUse = new FileClassLoaderUse(); 15 Class<?> class3 = fileClassLoaderUse.getClass(); 16 FileClassLoaderUse use = (FileClassLoaderUse) fileClassLoaderUse01.newInstance(); 17 //发现用同一个类加载器加载的.class文件,他们是同一个Class对象,因为FileClassLoader也是采用 18 //双亲委托,所以他们的classloader其实是同一个,导致Class对象是同一个对象 19 System.out.println(fileClassLoaderUse01.hashCode()); 20 System.out.println(fileClassLoaderUse02.hashCode()); 21 System.out.println(class3.hashCode()); 22 System.out.println(use.age); 23 24 //加载不在当前bin目录下面的class类 25 FileClassLoader fileClassLoader2 = new FileClassLoader("D:/eclipse_workspace01/Test/bin/"); 26 Class<?> loadClass = fileClassLoader2.loadClass("com.classloader.demo.HelloWorld"); 27 Class<?> loadClass2 = fileClassLoader2.loadClass("com.classloader.demo.HelloWorld"); 28 System.out.println(loadClass.hashCode()); 29 System.out.println(loadClass2.hashCode()); 30 31 } catch (ClassNotFoundException e) { 32 e.printStackTrace(); 33 } catch (InstantiationException e) { 34 e.printStackTrace(); 35 } catch (IllegalAccessException e) { 36 e.printStackTrace(); 37 } 38 } 39 40 }
加密字节码类加载器
1、 加密算法文件
1 package com.chunjiangchao.decrptclassloader; 2 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 9 /** 10 * @author chunjiangchao 11 * 12 */ 13 public class EncryptDecryptClassUtils { 14 /** 15 * 对输入的数据进行^操作 16 * @param in 17 * @param out 18 * @throws Exception 19 */ 20 public static void cypher(InputStream in,OutputStream out) throws Exception{ 21 int b=-1; 22 while((b=in.read())!=-1){ 23 out.write(b^0xff); 24 } 25 } 26 public static void encryptClass(String srcPath,String desDir) throws Exception{ 27 encrpt(srcPath, desDir+srcPath.substring(srcPath.lastIndexOf("/")+1)); 28 29 } 30 public static void encrpt(String src, String dest){ 31 FileInputStream fis = null; 32 FileOutputStream fos = null; 33 34 try { 35 fis = new FileInputStream(src); 36 fos = new FileOutputStream(dest); 37 38 int temp = -1; 39 while((temp=fis.read())!=-1){ 40 fos.write(temp^0xff); //取反操作 //采用DES也行,只要是对称加密的都可以 41 } 42 43 } catch (Exception e) { 44 e.printStackTrace(); 45 }finally{ 46 try { 47 if(fis!=null){ 48 fis.close(); 49 } 50 } catch (IOException e) { 51 e.printStackTrace(); 52 } 53 try { 54 if(fos!=null){ 55 fos.close(); 56 } 57 } catch (IOException e) { 58 e.printStackTrace(); 59 } 60 } 61 62 } 63 64 }
2、解密class文件类加载器
1 package com.chunjiangchao.decrptclassloader; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.FileInputStream; 5 6 public class EncryptClassLoader extends ClassLoader{ 7 private String srcPath; 8 public EncryptClassLoader(String srcPath){ 9 this.srcPath = srcPath; 10 11 } 12 /** 13 * 1、如果当前加载的类在本项目里面 14 * 会先判断是否已经加载名称为name的Class文件,如果没有会调用父加载器(AppClassLoader)进行加载, 15 * 如果还加载不到就会字节往内存中加载解密后的数据,由于程序在运行之前,应用程序bin目录下的.class文件已经被加载 16 * 到内存中了,所以一般情况下会从getParent().loadClass()中返回值 17 * 2、如果当前加载的类不在本项目里面 18 * 那么一个参数的findClass和两个参数的findClass效果是一样的 19 */ 20 @Override 21 protected Class<?> findClass(String name) throws ClassNotFoundException { 22 Class<?> clazz = findLoadedClass(name); 23 if(clazz!=null) 24 return clazz; 25 try { 26 System.out.println(getParent()); 27 clazz = getParent().loadClass(name); 28 } catch (Exception e1) { 29 //这块肯定会找不到,会打印出异常信息,为了防止混淆视听,我就把它给屏蔽掉了 30 //e1.printStackTrace(); 31 } 32 if(clazz!=null) 33 return clazz; 34 else{ 35 String className = null; 36 className = srcPath+name.substring(name.lastIndexOf('.')+1)+".class"; 37 FileInputStream fis ; 38 ByteArrayOutputStream baos; 39 try { 40 fis = new FileInputStream(className); 41 baos = new ByteArrayOutputStream(); 42 EncryptDecryptClassUtils.cypher(fis, baos); 43 byte[] byteArray = baos.toByteArray(); 44 return defineClass(name, byteArray, 0,byteArray.length); 45 } catch (Exception e) { 46 e.printStackTrace(); 47 } 48 } 49 50 51 return null; 52 } 53 /** 54 * 如果用这个方法进行加载本项目中已经存在的class文件,不使用双重委托机制的判断(findLoadedClass和getParent().loadClass) 55 * 那么会出现两个类加载器加载两个文件,在用字节码class文件newInstance强制类型转换的时候,会出现失败,哪怕你看见两个类的类名完全一致 56 * 对方法进行重载 57 * @param name 58 * @param ccc 59 * @return 60 * @throws ClassNotFoundException 61 */ 62 protected Class<?> findClass(String name,String ccc) throws ClassNotFoundException { 63 String className = srcPath+name.substring(name.lastIndexOf('.')+1)+".class"; 64 FileInputStream fis ; 65 ByteArrayOutputStream baos; 66 try { 67 fis = new FileInputStream(className); 68 baos = new ByteArrayOutputStream(); 69 EncryptDecryptClassUtils.cypher(fis, baos); 70 byte[] byteArray = baos.toByteArray(); 71 return defineClass(name, byteArray, 0,byteArray.length); 72 } catch (Exception e) { 73 e.printStackTrace(); 74 } 75 76 return null; 77 } 78 }
3、对字节码文件进行加密测试类
1 package com.chunjiangchao.decrptclassloader; 2 3 4 /** 5 * 对字节码文件进行加密操作 6 * @author chunjiangchao 7 */ 8 public class EncryptClassDemo { 9 10 public static void main(String[] args) throws Exception { 11 String srcPath = "D:/eclipse_workspace01/Test/bin/com/classloader/demo/HelloWorld.class"; 12 String desDir = "D:/test/"; 13 EncryptDecryptClassUtils.encryptClass(srcPath, desDir); 14 15 //加密本项目中的一个类文件 16 srcPath = "D:/eclipse_workspace01/MyClassLoader/bin/com/chunjiangchao/decrptclassloader/TestMoreClassLoader.class"; 17 EncryptDecryptClassUtils.encryptClass(srcPath, desDir); 18 } 19 20 }
4、使用解密类加载器测试类
1 package com.chunjiangchao.decrptclassloader; 2 3 4 /** 5 * 对字节码进行解密 6 */ 7 public class DecryptClassDemo { 8 9 public static void main(String[] args) throws Exception { 10 String desDir = "D:/test/"; 11 // 加载加密的字节码 12 EncryptClassLoader encryptClassLoader = new EncryptClassLoader(desDir); 13 Class<?> clazz = encryptClassLoader.findClass("com.classloader.demo.HelloWorld"); 14 System.out.println(clazz); 15 System.out.println(clazz.newInstance()); 16 17 //解密本项目中的一个class文件 18 //因为在一个参数的findClass文件中,它会先判断 19 clazz = encryptClassLoader.findClass("com.chunjiangchao.decrptclassloader.TestMoreClassLoader"); 20 System.out.println(clazz); 21 System.out.println((TestMoreClassLoader)clazz.newInstance()); 22 23 Class<?> clazz2 = encryptClassLoader.findClass("com.chunjiangchao.decrptclassloader.TestMoreClassLoader",null); 24 System.out.println(clazz2); 25 //这行代码会出现类型转换异常,说明当前class2对象是通过我们自己类加载器加载的,不同类加载器加载的相同的类,在内存中也是不同的 26 System.out.println((TestMoreClassLoader)clazz2.newInstance()); 27 /*Exception in thread "main" java.lang.ClassCastException: com.chunjiangchao.decrptclassloader.TestMoreClassLoader cannot be cast to com.chunjiangchao.decrptclassloader.TestMoreClassLoader*/ 28 } 29 30 }
5、测试两个类加载器加载同一个class文件,创建出两个不同的Class对象的Model
1 package com.chunjiangchao.decrptclassloader; 2 /** 3 * 用来测试多个类加载器加载同一个文件的情况 4 * @author zzb 5 * 6 */ 7 public class TestMoreClassLoader { 8 static{ 9 System.out.println("TestMoreClassLoader被执行了"); 10 } 11 public int number = 2; 12 }
上例代码总结:
1、不同类加载器,如果采用了双亲委托机制来加载类,那么加载的Class对象有可能是相同的
在加载.Class文件的时候,如果被加载的.Class文件在本项目里面,而且已经被加载了,此时想要用自定义的类加载器测试效果,必须用getParent().loadClass(name)双亲来加载,如果直接通过流的方式读入到内存的方式加载,那么它会判断成为一个新的Class对象,此时强制类型转换肯定会出错。
2、加载在本项目外的class文件,可以直接通过流的方式读入到内存,当然在读之前添加findLoadedClass(name)来获取Class是有必要的,防止两次加载,加载的Class对象不是同一个。
类加载器在线程上下文中
在线程中修改了类加载器,那么当前线程的类加载器就是用户设置的类加载器
1 package com.chunjiangchao.otherclassloader; 2 3 import com.chunjiangchao.classloader.FileClassLoader; 4 5 /** 6 * 测试线程上下文类加载器 7 * @author zzb 8 * 9 */ 10 public class ThreadContextClassLoaderDemo { 11 public static void main(String[] args) throws ClassNotFoundException { 12 //当前类加载器 13 ClassLoader classLoader = ThreadContextClassLoaderDemo.class.getClassLoader(); 14 System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@15b57dcb 15 //当前线程加载器 16 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 17 System.out.println(contextClassLoader);//sun.misc.Launcher$AppClassLoader@15b57dcb 18 19 //设置当前线程的类加载器 20 Thread.currentThread().setContextClassLoader(new FileClassLoader("D:/eclipse_workspace01/MyClassLoader/bin/")); 21 ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); 22 System.out.println(threadClassLoader); 23 Class<?> loadClass = ((FileClassLoader)threadClassLoader).loadClass("com.chunjiangchao.classloader.FileClassLoaderUse"); 24 System.out.println(loadClass); 25 26 } 27 }
网络类加载器
1 package com.chunjiangchao.otherclassloader; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.net.MalformedURLException; 7 import java.net.URL; 8 import java.net.URLConnection; 9 10 /** 11 * 网络ClassLoader,其实就是加载网络上面的字节码文件 12 * @author chunjiangchao 13 * 14 */ 15 public class NetClassLoader extends ClassLoader{ 16 private String neturl; 17 public NetClassLoader(String url){ 18 this.neturl = url; 19 20 } 21 @Override 22 protected Class<?> findClass(String name) { 23 Class<?> clazz = findLoadedClass(name);//建议先在内存中查找 24 if(clazz!=null) 25 return clazz; 26 try { 27 clazz = getParent().loadClass(name);//也可以采用双亲委托 28 } catch (ClassNotFoundException e) { 29 e.printStackTrace(); 30 } 31 if(clazz!=null){ 32 return clazz; 33 }else{ 34 try { 35 URL url = new URL(neturl); 36 URLConnection openConnection = url.openConnection(); 37 InputStream inputStream = openConnection.getInputStream(); 38 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 39 byte buffer[] = new byte[1024]; 40 int length = -1; 41 while((length = inputStream.read(buffer))!=-1){ 42 baos.write(buffer,0,length); 43 } 44 byte[] byteArray = baos.toByteArray(); 45 return defineClass(null, byteArray, 0, byteArray.length); 46 } catch (MalformedURLException e) { 47 e.printStackTrace(); 48 } catch (IOException e) { 49 e.printStackTrace(); 50 } 51 return null; 52 } 53 } 54 55 56 }
应用程序示例代码如下
代码地址:http://download.csdn.net/detail/fengbianyun/9490163