黑马程序员:Java基础总结----类加载器

时间:2022-06-21 22:09:40
黑马程序员:Java基础总结



类加载器

 
ASP.Net+Android+IO开发

.Net培训
、期待与您交流!





类加载器

Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。

每个 Class 对象都包含一个对定义它的 ClassLoader 的引用

数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。

应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。

类加载器通常由安全管理器使用,用于指示安全域。

ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。

然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。defineClass 方法将一个 byte 数组转换为 Class 类的实例。这种新定义的类的实例可以使用 Class.newInstance 来创建。

类加载器所创建对象的方法和构造方法可以引用其他类。为了确定引用的类,Java 虚拟机将调用最初创建该类的类加载器的 loadClass 方法。

例如,应用程序可以创建一个网络类加载器,从服务器中下载类文件。示例代码如下所示:

   ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .

网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。示例实现如下:

     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
. . .
}
}

类加载器的委托机制
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

黑马程序员:Java基础总结----类加载器


            ClassLoader loader = Ts. 
class
.getClassLoader();
             
// 打印出当前的类装载器,及该类装载器的各级父类装载器
             
while 
(loader != 
null
) {
                  System. 
out
.println(loader.getClass().getName());
                  loader = loader.getParent();
            }
             
// 结果
             
// sun.misc.Launcher$AppClassLoader
             
// sun.misc.Launcher$ExtClassLoader
             
// null不是类,打印显示null




自定义的类加载器

自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法



ClassLoader 构造方法摘要
protected ClassLoader(ClassLoader parent)
          使用指定的、用于委托操作的父类加载器创建新的类加载器。
 Class<?> loadClass(String name)
          使用指定的二进制名称来加载类。
protected  Class<?> findLoadedClass(String name)
          如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。
protected  Class<?> findClass(String name)
          使用指定的二进制名称查找类
protected  Class<?> defineClass(String name, byte[] b, int off, int len)
          将一个 byte 数组转换为 Class 类的实例。



import 
java.io.ByteArrayOutputStream;
import 
java.io.File;
import 
java.io.FileInputStream;

/**
 *
 * 一、ClassLoader加载类的顺序
 *  1.调用 findLoadedClass(String) 来检查是否已经加载类。
 *  2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
 *  3.调用 findClass(String) 方法查找类。
 * 二、实现自己的类加载器
 *    1.用
FileInputStream
 
获取类的class文件的字节数组
 *    2.用
ByteArrayOutputStream
 ,
toByteArray()
将字节数组转换为Class类的实例
 *
 */
public 
class 
Ts {

       
public 
static 
void 
main(String[] args) 
throws 
InstantiationException, IllegalAccessException, ClassNotFoundException {
             
//新建一个类加载器
            MyClassLoader cl = 
new 
MyClassLoader(
"myClassLoader" 
);
             
//加载类,得到Class对象
            Class<?> clazz = cl.loadClass( 
"classloader.Animal"
);
             
//得到类的实例
            Animal animal=(Animal) clazz.newInstance();
            animal.say();
      }

}
class 
Animal{
       
public 
void 
say(){
            System. 
out
.println(
"hello world!" 
);
      }
}
class 
MyClassLoader 
extends 
ClassLoader {
       
//类加载器的名称
       
private 
String 
name
;
       
//类存放的路径
       
private 
String 
path 

"E:\\workspace\\Algorithm\\src"
;

      MyClassLoader(String name) {
             
this
.
name 
= name;
      }
      MyClassLoader(ClassLoader parent, String name) {
             
super
(parent);
             
this
.
name 
= name;
      }
       
/**
       * 重写findClass方法
       */
       
@Override
       
public 
Class<?> findClass(String name) {
             
byte
[] data = loadClassData(name);
             
return 
this 
.defineClass(name, data, 0, data.
length
);
      }
       
public 
byte
[] loadClassData(String name) {
             
try 
{
                  name = name.replace( 
"."

"//" 
);
                  FileInputStream is = 
new 
FileInputStream( 
new 
File(
path 
+ name + 
".class" 
));
                  ByteArrayOutputStream baos = 
new 
ByteArrayOutputStream();
                   
int 
b = 0;
                   
while 
((b = is.read()) != -1) {
                        baos.write(b);
                  }
                   
return 
baos.toByteArray();
            } 
catch 
(Exception e) {
                  e.printStackTrace();
            }
             
return 
null
;
      }

}




 
ASP.Net+Android+IO开发

.Net培训
、期待与您交流!