JVM虚拟机类加载器ClassLoader

一.看前须知

    1.jvm会校验class文件的完整性和准确性,然后加载到内存里.

    2.那么靠谁来加载呢?就是类加载器classloader

    3.classloader的定义:JVM加载class文件的工具.

    4.同一个包类名且同一个类加载器加载的,是同一个类.(这意味着使用不同的加载器来加载同一个class文件,那就不是同一个类,我们会在后面去验证这个道理.),

    5.类加载器是全盘加载双亲委派,也就是说类里面的import其他的类,也是由该加载器去自动加载.

二.ClassLoader分类

    直接贴图

    

三.几个疑问

 

  •     我们怎么获得类加载器?
  •     BootStrapClassLoader究竟是什么?
  •     应用程序加载器的ClassPath是什么?
  •      如果我把自己写的jar包,放到拓展包目录下,那么它的加载器是什么?   
  •      自定义类加载器我们该如何使用?
  •     同一个class文件被不同的classloader去加载得到的类是一样的吗?
  •     类加载器在我们平时写代码时有什么用?

四.探究疑问之我们怎么获得类加载器

    怎么获得类加载器?其实是有现成的方法的.

   随便写个类 执行下面这个main方法

public class Girl {
	public static void main(String[] args) {
		System.out.println(Girl.class.getClassLoader().toString());
	}
}

    结果:

sun.misc.Launcher$AppClassLoader@73d16e93

    我们可以看到这个类加载器的是AppClassLoader.正是上面那个图片里第三级的classLoader

    对的.如何获得classLoader呢?就是通过Class.getClassLoader这个方法来获得的.

五.探究疑问之BootStrapClassLoader究竟是什么? 

 在图上可以看到BootStrapClassLoader是加载String ,System这些核心类的加载器.咱们可以试一下.

       

public class Girl {
	public static void main(String[] args) {
		System.out.println(String.class.getClassLoader());
	}
}

运行结果是个null,竟然是个null.为什么会这样呢?原来BootStrapClassLoader根本就不是个JAVA对象,它是用C++编写的.整个JVM都是用C++编写的.然后又编写了个加载核心类的工具就是BootStrapClassLoader.所以我们才获取不到它.但它无处不在.

那么它加载的核心组件是什么呢?

打开我们的jdk,如图

就是加载这些核心组件.但是里面那个ext那个文件夹要注意,那里面不是BootStrapClassLoader加载的,而是拓展类库加载的.

六.探究疑问之应用程序加载器的ClassPath是什么?

 

    其实就是字面意思,class文件的path,就是class文件的系统路径,系统要是想加载什么东西,那必须得有这个东西的资源定位符啊.

    环境变量里的ClassPath就是java.exe执行命令的时候,到哪里去找class文件,切记:并不会去当前目录去找,而是去配置的地方去找,如果想要到当前目录,那么需要在CLASSPTAH这个里面多配置一个点"."

    Tomcat里的classpath略有不同,tomcat启动时会把系统的classpath给清空掉,设定几个自己的classpath,其中有两个就是WEB-INF/lib和WEB-INF/classes.

    那么个人推测,MyEclipse做的和Tomcat一样,在普通java项目里会动态把classPath设置为项目下的bin包.这样才可以加载.

七.探究疑问之如果我把自己写的jar包,放到拓展包目录下,那么它的加载器是什么?  

  写个tttt.jar包,放到ext目录下.然后eclipse切换jdk

  然后调用里面的类,打印class.classLoader() 是ExtClassLoader

八.探究疑问之自定义类加载器我们该如何使用? 

    直接继承java.lang.ClassLoader.然后重写里面的findClass方法.

public class MyLoader extends ClassLoader{
	public static String classFullFileName = "F:\\aaa\\Man.class";
	public static String className = "com.Man";

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		System.out.println("查找:"+name);
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(classFullFileName);
			byte [] b  = new byte[fis.available()];
			fis.read(b);
			return defineClass(name, b, 0, b.length);
		} catch (Exception e) {
			
		}finally{
			if (fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	
}

为什么要重写这个方法?

我们看下classLoader的loadClass方法源码

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

流程是:

1.先去查看本ClassLoader是否已经加载过

2.尝试让父级加载器去加载

3.调用findClass.

所以要重写findClass这个方法,方法里面的defindClass是将字节数组加载到内存形成class的自带方法.

另外由于流程是这样的,所以如果使用同一加载器去加载同一类则会在流程的第一步就去获得缓存而不会到第三步.


九.探究疑问之类加载器在我们平时写代码时有什么用?

1.大型项目,更换某些类时,不用重新部署,直接重新加载即可.

2.加密解密.自定义类加载器,在加载的时候操作.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值