bootstrap模板样式(bootstrap模板免费)

  来源:huachao1001,

  www.jianshu.com/p/acc7595f1b9d

  如有好文章投稿,请点击 → 这里了解详情

  其实,双亲委派模型并不复杂。自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果,然后copy一下就能用。但是,如果每次想自定义类加载器就必须搜一遍别人的文章,然后复制,这样显然不行。可是自定义类加载器又不经常用,时间久了容易忘记。相信你经常会记不太清loadClass、findClass、defineClass这些函数我到底应该重写哪一个?它们主要是做什么的?本文大致分析了各个函数的流程,目的就是让你看完之后,难以忘记!或者说,延长你对自定义类加载器的记忆时间!随时随地想自定义就自定义!

  1. 双亲委派模型

  关于双亲委派模型,网上的资料有很多。我这里只简单的描述一下,就当是复习。

  1.1 什么是双亲委派模型?

  首先,先要知道什么是类加载器。简单说,类加载器就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。如果站在JVM的角度来看,只存在两种类加载器:

启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。

其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:

扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>libext目录或java.ext.dirs系统变量指定的路径中的所有类库。

应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

bootstrap模板样式(bootstrap模板免费)

  双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

类加载器的双亲委派模型

  1.2 为什么需要双亲委派模型?

  为什么需要双亲委派模型呢?假设没有双亲委派模型,试想一个场景:

  黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。

  而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。

  或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。

  举个简单例子:

  ClassLoader1、ClassLoader2都加载java.lang.String类,对应Class1、Class2对象。那么Class1对象不属于ClassLoad2对象加载的java.lang.String类型。

  1.3 如何实现双亲委派模型?

  双亲委派模型的原理很简单,实现也简单。每次通过先委托父类加载器加载,当父类加载器无法加载时,再自己加载。其实ClassLoader类默认的loadClass方法已经帮我们写好了,我们无需去写。

  2. 自定义类加载器

  2. 1几个重要函数

  2.1.1 loadClass

  loadClass默认实现如下:

  public Class<?> loadClass(String name) throws ClassNotFoundException {

  return loadClass(name, false);

  }

  再看看loadClass(String name, boolean resolve)函数:

  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;

  }

  }

  从上面代码可以明显看出,loadClass(String, boolean)函数即实现了双亲委派模型!整个大致过程如下:

首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。

如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。

如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。

  换句话说,如果自定义类加载器,就必须重写findClass方法!

  2.1.2 find Class

  findClass的默认实现如下:

  protected Class<?> findClass(String name) throws ClassNotFoundException {

  throw new ClassNotFoundException(name);

  }

  可以看出,抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道,loadClass在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass函数,因此我们必须要在loadClass这个函数里面实现将一个指定类名称转换为Class对象.

  如果是是读取一个指定的名称的类为字节数组的话,这很好办。但是如何将字节数组转为Class对象呢?很简单,Java提供了defineClass方法,通过这个方法,就可以把一个字节数组转为Class对象啦~

  2.1.3 defineClass

  defineClass主要的功能是:

  将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。如,假设class文件是加密过的,则需要解密后作为形参传入defineClass函数。

  defineClass默认实现如下:

  protected final Class<?> defineClass(String name, byte[] b, int off, int len)

  throws ClassFormatError {

  return defineClass(name, b, off, len, null);

  }

  2.2 函数调用过程

  上一节所提的函数调用过程如下:

自定义函数调用过程

  2.3 简单示例

  首先,我们定义一个待加载的普通Java类:Test.java。放在com.huachao.cl包下:

  package com.huachao.cl;

  public class Test {

  public void hello() {

  System.out.println("恩,是的,我是由 " + getClass().getClassLoader().getClass()

  + " 加载进来的");

  }

  }

  注意:

  如果你是直接在当前项目里面创建,待Test.java编译后,请把Test.class文件拷贝走,再将Test.java删除。因为如果Test.class存放在当前项目中,根据双亲委派模型可知,会通过sun.misc.Launcher$AppClassLoader 类加载器加载。为了让我们自定义的类加载器加载,我们把Test.class文件放入到其他目录。

  在本例中,我们Test.class文件存放的目录如下:

class文件目录

  接下来就是自定义我们的类加载器:

  import java.io.FileInputStream;

  import java.lang.reflect.Method;

  public class Main {

  static class MyClassLoader extends ClassLoader {

  private String classPath;

  public MyClassLoader(String classPath) {

  this.classPath = classPath;

  }

  private byte[] loadByte(String name) throws Exception {

  name = name.replaceAll(".", "/");

  FileInputStream fis = new FileInputStream(classPath + "/" + name

  + ".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 static void main(String args[]) throws Exception {

  MyClassLoader classLoader = new MyClassLoader("D:/test");

  Class clazz = classLoader.loadClass("com.huachao.cl.Test");

  Object obj = clazz.newInstance();

  Method helloMethod = clazz.getDeclaredMethod("hello", null);

  helloMethod.invoke(obj, null);

  }

  }

  最后运行结果如下:

  恩,是的,我是由 class Main$MyClassLoader 加载进来的

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能


【免责声明】:

本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

【关于转载】:

本站尊重互联网版权体系,本站部分图片、文章大部分转载于互联网、所有内容不代表本站观点、不对文章中的任何观点负责、转载的目的只用于给网民提供信息阅读,无任何商业用途,所有内容版权归原作者所有
如本站(文章、内容、图片、视频)任何资料有侵权,先说声抱歉;麻烦您请联系请后台提交工单,我们会立即删除、维护您的权益。非常感谢您的理解。

【附】:

二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!

注:本站资源来自网络转载,版权归原作者和公司所有,如果有侵犯到您的权益,请第一时间联系我们处理!

-----------------------------------------------------------------------------------------------------------

【版权声明】:

一、本站致力于为源码爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、如有内容侵犯您的版权或其他利益的,请编辑邮件并加以说明发送到站长邮箱。站长会进行审查之后,情况属实的会在三个工作日内为您删除。
-----------------------------------------------------------------------------------------------------------


内容投诉
源码村资源网 » bootstrap模板样式(bootstrap模板免费)

1 评论

您需要 登录账户 后才能发表评论

发表评论

欢迎 访客 发表评论