package com.lsw.classloader;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
class Parent{
public static int a=3;
public int b = 4;
static {
System.out.println("Parent static block");
}
}
class MyClassLoader extends ClassLoader {
private String name = ""; //类加载器的名字
private String path = "";
public MyClassLoader(String name) {
super(); //让系统类加载器成为该类的父加载器
this.name = name;
}
public MyClassLoader(ClassLoader parent,String name){
super(parent); //显示指定该类加载器的父加载器
this.name = name;
}
private byte[] loadByte(String name) throws Exception {
String[] names = name.split("\.");
FileInputStream fis = new FileInputStream(path + names[3] + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException{
//初始化流程 1.给类的静态变量赋默认值 a=0 执行静态代码块
// 2.给静态变量赋初始值a=3
System.out.println(Parent.a);
//static方法没有this的方法,在static方法内部不能调用非静态方法,反过来可以
//而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法,这实际上是static方法的主要用途
//静态变量和非静态变量的区别:静态变量被所有对象共享,在内存这种只有一个副本,在类初次加载的时候初始化。非静态变量是对象拥有,在对象创建的时候被初始化,存在多个副本
/*System.out.println(Parent.b); 这种写法可以,因为类没有创建*/
//连续创建两个Parent对象,静态代码块还是执行一次。说明静态代码块已在内存区域存在(存储在哪里?堆、栈)
System.out.println(new Parent().b);
System.out.println(new Parent().b);
System.out.println("***********************");
//java.lang.String 由根加载器进行加载 bootstrap
Class clazz = Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader()); //结果为null
//系统加载器或应用加载器加载
clazz = Class.forName("com.lsw.classloader.Parent");
System.out.println(clazz.getClassLoader());
System.out.println("********自定义类的加载***********");
//应用类加载器 调用默认的构造方法 默认的构造函数父类加载器是应用类加载器
//父类是系统类加载器 ->扩展类加载器->根类加载器
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("E:\tools\1\");
//父类加载器是loader1
MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
loader2.setPath("E:\tools\2\");
//父类加载器是bootstrap
MyClassLoader loader3 = new MyClassLoader(null,"loader3");
loader3.setPath("E:\tools\3\");
//加载路径解析
//bootstrap 加载路径从系统属性sun.boot.class.path所指定的目录中加载类库
//extension jre/lib/ext或者从系统属性java.ext.dirs
//system 从环境变量 classpath或者系统属性java.class.path
test(loader1);
//loader2 加载流程 1.首先委托loader1进行加载 2.loader1发现还有父类-系统类加载器 委托系统类加载器加载
//3. 系统类依次查找 扩展类加载器加载 根类加载器加载
//4. 根类加载器没有查找到类,扩展类加载查找,系统类查找,loader1查找到了,loader1进行加载
//被加载的加载器被称为定义类加载器 所有的加载器可以称为初始类加载器
test(loader2);
test(loader3);
//解析:loader1和loader3在各自的命名空间之中都存在sample类和dog类
//堆区 方法区
//load1
//代表sample类的class实例 sample来着tools1
//代表dog类的class实例 dog来着tools1
//load3
//代表sample类的class实例 sample来着tools3
//代表dog类的class实例 dog来着tools3
System.out.println(loader2.getParent());
ClassLoader loader = ClassLoader.getSystemClassLoader();
while(loader !=null){
loader = loader.getParent();
System.out.println(loader);
}
//由于根类加载器是C/C++编写的 所以打印不出任何东西
//反射
System.out.println("-----------------------");
//cls 引用变量 引用的是堆区的myclassloader对象
Class cls = loader1.loadClass("com.lsw.classloader.Sample");
//object 引用变量 引用的是堆区的sample类的class对象 指向方法区内的sample二进制数据结构
Object object = cls.newInstance(); //创建一个sample类的对象
System.out.println(object);
//field 引用变量 引用的是堆区的sample对象
Field field = object.getClass().getField("v1");
// field = cls.getField("v1");
int i = field.getInt(object); //the object to extract the int value from
System.out.println(i);
}
public static void test(ClassLoader loader) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
Class clazz2 = loader.loadClass("com.lsw.classloader.Sample");
Object obj = clazz2.newInstance();
}
}