与接口相反,例如子类没有主动使用到父类,但是类加载器会加载父类。但是对接口并不是
类加载器流程图:
它们是包含关系,只要三个加载器中的一个加载成功就成功,不然就抛异常
流程:loader 加载器加载Sample类,自己并不会加载而是交给它的父类系统类加载器,发现此时加载器还有父类,继续网上传递给父类,一直到根类加载器;但是根类加载器并不能成功加载Sample类,继续往回交给扩展类加载器,发现不能解决,继续传给系统类加载器,最终由系统类加载器完成加载Sample类,返回结果给loader1加载器;
(只要这个过程中有一个类加载器加载成功,返回正确结果了)
一句话概括就是:
某个类想要加载特定的类,自己并不会去加载,而是一直交给父类加载,如果一直传动根类加载器都不能解决,就往回交给子类加载,这个过程中只要有一个类加载器成功就宣告加载动作成功,返回正确结果。
分三种
getClassLoader
作用:返回针对于这个类的类加载器(真正加载这个类的类加载器),某些实现可能会使用null表示启动类加载器,如果这个类是由启动类(根加载器)加载器加载的就会返回null
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
//自定义类加载器
public class MyClassLoader extends ClassLoader {
private String classLoaderName;
private String path;
private final String fileExtension = ".class";
public void setPath(String path) {
this.path = path;
}
public MyClassLoader(){
super();
}
public MyClassLoader(String classLoaderName) {
super();//将系统类加载器当作该类加载器的父加载器
this.classLoaderName = classLoaderName;
}
public MyClassLoader(ClassLoader parent, String classLoaderName) {
super(parent); //显示指定该类加载器的父加载器 (可以自己自定义父加载器是谁)
this.classLoaderName = classLoaderName;
}
@Override
public String toString() {
return "[" + this.classLoaderName + "]";
}
//根据传入的name 寻找对应的Class
@Override
protected Class> findClass(String classname) throws ClassNotFoundException {
byte[] data=this.loadClassData(classname);
//调用该方法可以返回Class对象,很重要的方法
return this.defineClass(classname,data,0,data.length);
}
// 传入class文件的名字 返回类的字节数组
private byte[] loadClassData(String name) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
name=name.replace(".",File.separator);
try {
// this.classLoaderName = this.classLoaderName.replace(".", "/");
is = new FileInputStream(new File(this.path+name + this.fileExtension));
baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
public static void test(ClassLoader classLoader) throws Exception {
Class> clazz=classLoader.loadClass("com.xuge.MyClassLoader");
Object object=clazz.newInstance(); //返回实例化对象
System.out.println(clazz.getClassLoader());
}
public static void main(String[]args) throws Exception {
MyClassLoader loader1=new MyClassLoader("loader1");
test(loader1);
}
}
分析:
通过反射才是对类的主动使用
//获得当前类的ClassLoder
clazz.getClassLoader();
//获取当前线程上下文的ClassLoader
Thread.currentThread().getContextClassLoader();
//获得系统的ClassLoader
ClassLoader.getSystemClassLoader();
//获得调用者的ClassLoader
DriverManager.getCallerClassLoader();
ClassLoader是一个抽象类。
Java的class类全是由类加载器加载的,但是针对数组类的class对象并不是由类加载器加载的,而是在运行时根据情况jvm动态生成的类型。但是它的类加载器是根据数组中的类决定的。如果数组中的类是原生类,那么它是没有类加载器的
源码:
ClassLoader方法中的:
findClass可以通过传入类文件名,得到一个类Class,
loadClassData方法,根据类的名字,获取到类的二进制字节数组
defineClass方法,可以把类的二进制字节数组构建成一个类返回
* 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
* . . .
* }
* }
代码如下:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
//自定义类加载器
public class MyClassLoader extends ClassLoader {
private String classLoaderName;
private final String fileExtension = ".class";
public MyClassLoader(){
super();
}
public MyClassLoader(String classLoaderName) {
super();//将系统类加载器当作该类加载器的父加载器
this.classLoaderName = classLoaderName;
}
public MyClassLoader(ClassLoader parent, String classLoaderName) {
super(parent); //显示指定该类加载器的父加载器 (可以自己自定义父加载器是谁)
this.classLoaderName = classLoaderName;
}
@Override
public String toString() {
return "[" + this.classLoaderName + "]";
}
//根据传入的name 寻找对应的Class
@Override
protected Class> findClass(String classname) throws ClassNotFoundException {
byte[] data=this.loadClassData(classname);
//调用该方法可以返回Class对象,很重要的方法
return this.defineClass(classname,data,0,data.length);
}
// 传入class文件的名字 返回类的字节数组
private byte[] loadClassData(String name) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
try {
this.classLoaderName = this.classLoaderName.replace(".", "/");
is = new FileInputStream(new File(name + this.fileExtension));
baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
public static void test(ClassLoader classLoader) throws Exception {
Class> clazz=classLoader.loadClass("com.xuge.MyClassLoader");
Object object=clazz.newInstance(); //返回实例化对象
System.out.println(clazz.getClassLoader());
}
public static void main(String[]args) throws Exception {
MyClassLoader loader1=new MyClassLoader("loader1");
test(loader1);
}
}
public MyClassLoader(String classLoaderName)方法中调用的super(),可以使用系统默认的类加载作为父加载器,其类加载器一定是系统类(应用)加载器AppClassLoader。
public MyClassLoader(ClassLoader parent, String classLoaderName)方法中调用了super(parent);可以自己选择指定哪一个类加载器作为该类的加载器,可以不是系统类(应用)加载器AppClassLoader。
Super(parent);调用的源码是ClassLoader中的 protected ClassLoader(ClassLoader parent)
源码如下:
/**
* Creates a new class loader using the specified parent class loader for
* delegation.
*
* If there is a security manager, its {@link
* SecurityManager#checkCreateClassLoader()
* checkCreateClassLoader} method is invoked. This may result in
* a security exception.
*
* @param parent
* The parent class loader
*
* @throws SecurityException
* If a security manager exists and its
* checkCreateClassLoader method doesn't allow creation
* of a new class loader.
*
* @since 1.2
*/
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
protected final Class> defineClass(String name, byte[] b, int off, int len)又调用了
protected final Class> defineClass(String name, byte[] b, int off, int len ProtectionDomain protectionDomain);
源码
/**
* Converts an array of bytes into an instance of class Class,
* with an optional ProtectionDomain. If the domain is
* null, then a default domain will be assigned to the class as
* specified in the documentation for {@link #defineClass(String, byte[],
* int, int)}. Before the class can be used it must be resolved.
*
* The first class defined in a package determines the exact set of
* certificates that all subsequent classes defined in that package must
* contain. The set of certificates for a class is obtained from the
* {@link java.security.CodeSource CodeSource} within the
* ProtectionDomain of the class. Any classes added to that
* package must contain the same set of certificates or a
* SecurityException will be thrown. Note that if
* name is null, this check is not performed.
* You should always pass in the binary name of the
* class you are defining as well as the bytes. This ensures that the
* class you are defining is indeed the class you think it is.
*
*
The specified name cannot begin with "java.", since
* all classes in the "java.* packages can only be defined by the
* bootstrap class loader. If name is not null, it
* must be equal to the binary name of the class
* specified by the byte array "b", otherwise a {@link
* NoClassDefFoundError NoClassDefFoundError} will be thrown.
*
* @param name
* The expected binary name of the class, or
* null if not known
*
* @param b
* The bytes that make up the class data. The bytes in positions
* off through off+len-1 should have the format
* of a valid class file as defined by
* The Java™ Virtual Machine Specification.
*
* @param off
* The start offset in b of the class data
*
* @param len
* The length of the class data
*
* @param protectionDomain
* The ProtectionDomain of the class
*
* @return The Class object created from the data,
* and optional ProtectionDomain.
*
* @throws ClassFormatError
* If the data did not contain a valid class
*
* @throws NoClassDefFoundError
* If name is not equal to the binary
* name of the class specified by b
*
* @throws IndexOutOfBoundsException
* If either off or len is negative, or if
* off+len is greater than b.length.
*
* @throws SecurityException
* If an attempt is made to add this class to a package that
* contains classes that were signed by a different set of
* certificates than this class, or if name begins with
* "java.".
*/
protected final Class> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
发现自定义MyClassLoader重写的findClass并没有被调用执行。原因: 双亲委托机制,因为MyClassLoader loader1=new MyClassLoader(“loader1”);我们使用的父类加载器是AppClassLoader,所以它交给父类加载器去加载,而系统类类加载器(父类)能够加载成功
所以并不是我们自定义的加载器加载的!
指定要加载MyTest7.class的文件的路径,将工程中生成MyTest7.class的文件放到指定目录下,并删除系统文件中的MyTest7.class。
运行程序,打印结果如下:
程序交给父类加载器去系统目录下加载class文件失败,交回给自定义加载器加载,调用了自定义类加载器的findClass方法
改造代码,设置loader2的父加载器为loader1,MyClassLoader loader2=new MyClassLoader(loader1,“loader2”); 如下
删除生成的MyTest7.class文件,将其放到自定义路径下。
运行结果如下:
分析:
loader1加载MyTest7.class,交给父类加载器APPClassLoader加载,发现系统classpath,路径下找不到文件,加载失败,交回给loader1加载器加载,loader1调用了自己重写的方法findClass函数,打印了 class loader name: MyTest7 ;接着loader2加载MyTest7.class,它交给它的父类加载器loader1加载,而loader1加载器能加载成功,所以loader2没有起到作用,没有执行findClass函数。 而两次MyTest7.class都是同一个类加载器加载loader1加载成功的,所以hashcode值一样。
Loader1 和loader2 这两个加载器实例都是MyClassLoader New出来的,是相同类的两个对象,但是他们在类加载器方面却能父子关系,原因:它们并不是树形结构,而是包含关系。