当前位置:   article > 正文

怎么把object 转成自定义的类_面试官:谈谈类加载器吧,你有没有看过类加载器的源码?

java 如何实现一个object转对应的class

一、类加载

Java类加载器是Java运行时环境的一部分,负责动态加载Java类到Java虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。学习类加载器时,掌握Java的委派概念很重要。

4729bdd439c4429c390e12a073a71f8b.png

文末有福利~

1、在java代码中,类型的加载,连接,初始化过程都是在程序运行期间完成的。

图示:

0ad97af94b9679e84507982161dba161.png

1.1、类型的加载——这里的类型是指的什么?

答:类型就是指的我们Java源代码通过编译后的class文件。

1.2、类型的来源有哪些?

(1)本地磁盘

(2)网络下载,class文件

(3)war,jar下加载,class文件

(4)从专门的数据库中读取,class文件(少见)

(5)将java源文件动态编译成class文件

1)典型的就是动态代理,通过运行期生成class文件

2)我们的jsp会被转换成servlet,而我们的serlvet是一个java文件,会被编译成class文件

1.3、通过什么来进行加载?(类加载器)

a9297516f3470eccbcf634bc7f19ed3a.png

1.4、类加载的分类以及各种加载职责以及层级结构

(1)系统级别

1)启动类加载器

2)扩展类加载器

3)系统类加载器(App类加载器)

(2)用户级别的

自定义类加载器(继承我们的ClassLoader)

(3)层级结构

c188564af00fef23f26aad059a6d8b99.png

二、类加载器加载我们的Class的时候遵循我们的双亲委派模型

在双亲委派机制中,各个加载器按照父子关系形成树型结构,除了根加载器以外,每一个加载器有且只有一个父加载器

261b297f828395cd1ad5e5f8835fc0d1.png

1、源码分析:

1 protected Class> loadClass(String name, boolean resolve)2  throws ClassNotFoundException3 {4  synchronized (getClassLoadingLock(name)) {5  //检查当前的class对象是否被加载过,被加载过就返回6  Class> c = findLoadedClass(name);7  if (c == null) {8  long t0 = System.nanoTime();9  try {10  //判断当前的classLoader是否有父类11  //若存在父类12  if (parent != null) {13  //调用父类的loadClass14  c = parent.loadClass(name, false);15  } else {//不存在父类,表示当前的classLoader是extClassLoader16  //那么就会调用启动类判断是否加载过17  c = findBootstrapClassOrNull(name);18  }19  } catch (ClassNotFoundException e) {20  // ClassNotFoundException thrown if class not found21  // from the non‐null parent class loader22  }23  //到目标位置,app ext boot都没有去加载过24  if (c == null) {25  // If still not found, then invoke findClass in order26  // to find the class.27  long t1 = System.nanoTime();28  //委托我们的子类的classLoader去找29  c = findClass(name);3031  // this is the defining class loader; record the stats32  sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 ‐ t0);33  sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);34  sun.misc.PerfCounter.getFindClasses().increment();35  }36  }37  if (resolve) {38  resolveClass(c);39  }40  return c;41  }42 }

2、双亲委派模型加载的流程图

1cd9faa5d503bd61b3e7ca86f8b0bc96.png

3、类加载器的双亲委派模型的好处:

总所周知:java.lang.object类是所有类的父类,所以我们程序在运行期间会把java.lang.object类加载到内存中,假如java.lang.object类能够被我们自定义类加载器去加载的话,那么jvm中就会存在多份Object的Class对象,而且这些Class对象是不兼容的。

所以双亲委派模型可以保证java核心类库下的类型的安全。

借助双亲委派模型,我们java核心类库的类必须是由我们的启动类加载器加载的,这样可以确保我们核心类库只会在jvm中存在一份这就不会给自定义类加载器去加载我们核心类库的类。

根据我们的演示案例,一个class可以由多个类加载器去加载,同事可以在jvm内存中存在多个不同版本的Class对象,这些对象是不兼容的。

4、如何手写一个自定义类加载器(根据ClassLoader的doc文档)

cff1a5c047823ebb85fa9bd4de6d125d.png

(1)我们自定义类加载器必须要继承ClassLoader

(2)我们必须要findClass(String name)方法

1 /**2  * Finds the class with the specified binary name.3  * This method should be overridden by class loader implementations that4  * follow the delegation model for loading classes, and will be invoked by5  * the {@link #loadClass loadClass} method after checking the6  * parent class loader for the requested class. The default implementation7  * throws a ClassNotFoundException.8  *9  * @param name10  * The binary name of the class11  *12  * @return The resulting Class object13  *14  * @throws ClassNotFoundException15  * If the class could not be found16  *17  * @since 1.218  */19 protected Class> findClass(String name) throws ClassNotFoundException{20  throw new ClassNotFoundException(name);21 }

(3)写方法loadClassData(String name)去读取我们的class数据(非必须)

1 /**2  * 自定义的加载器3  * Created by smlz on 2019/10/22.4  */5 public class TulingClassLoader extends ClassLoader {67  private final static String fileSuffixExt = ".class";89  private String classLoaderName;1011  private String loadPath;1213  public void setLoadPath(String loadPath) {14  this.loadPath = loadPath;15  }1617  public TulingClassLoader(ClassLoader parent, String classLoaderName) {18  /**19  * 指定当前类加载器的父类加载器20  */21  super(parent);22  this.classLoaderName = classLoaderName;23  }2425  public TulingClassLoader(String classLoaderName) {26  /**27  * 使用appClassLoader加载器 作为本类的加载器28  */29  super();30  this.classLoaderName = classLoaderName;31  }3233  public TulingClassLoader(ClassLoader classLoader) {34  super(classLoader);35  }3637  /**38  * 方法实现说明:创建我们的class 的二进制名称39  * @author:smlz40  * @param name: 类的二进制名称41  * @return:42  * @exception:43  * @date:2019/10/22 14:4244  */45  private byte[] loadClassData(String name) {46  byte[] data = null;47  ByteArrayOutputStream baos = null;48  InputStream is = null;4950  try {51  name = name.replace(".","");52  String fileName = loadPath+name+fileSuffixExt;53  File file = new File(fileName);54  is = new FileInputStream(file);5556  baos = new ByteArrayOutputStream();57  int ch;58  while (‐1 != (ch = is.read())){59  baos.write(ch);60  }61  data = baos.toByteArray();62  }catch (Exception e) {63  e.printStackTrace();64  }finally {65  try{66  if(null != baos) {67  baos.close();68  }69  if(null !=is) {70  is.close();71  }72  }catch (Exception e) {73  e.printStackTrace();74  }75  }7677  return data;78  }7980  protected Class> findClass(String name) throws ClassNotFoundException{81  byte[] data = loadClassData(name);82  System.out.println("TulingClassLoader 加载我们的类:===>"+name);83  return defineClass(name,data,0,data.length);84  }85 }

(4)特别需要注意:我们自定义的类加载器默认情况下的父类加载器是我们的系统AppClassLoader

代码证据:

1 public TulingClassLoader(String classLoaderName) {2  /**3  * 使用appClassLoader加载器 作为本类的加载器4  */5  super();6  this.classLoaderName = classLoaderName;7 }89 //调用super()的时候10 protected ClassLoader() {11  //在这里,把getSystemClassLoader()作为我们自定义类加载器的12  //父亲13  this(checkCreateClassLoader(), getSystemClassLoader());14 }1516 private ClassLoader(Void unused, ClassLoader parent) {17  this.parent = parent;18  if (ParallelLoaders.isRegistered(this.getClass())) {19  parallelLockMap = new ConcurrentHashMap<>();20  package2certs = new ConcurrentHashMap<>();21  domains =22  Collections.synchronizedSet(new HashSet());23  assertionLock = new Object();24  } else {25  // no finer‐grained lock; lock on the classloader instance26  parallelLockMap = null;27  package2certs = new Hashtable<>();28  domains = new HashSet<>();29  assertionLock = this;30  }31 }

5、怎么用实验证明我们的自定义类加载器的父加载器就是系统类加载器

(1)把我们的Person.class文件copy的指定的磁盘目录下。同时classpath下 存在我们的Person.class文件

1 /**2  * 证明系统类加载器就是我们的自定义类加载器的父类3  * Created by smlz on 2019/11/12.4  */5 public class AppClassLoaderIsCustomerClassLoaderParent {67  public static void main(String[] args) throws ClassNotFoundException {8  /**9  * 执行test1()方法的时候打印结果式我们的系统类加载器去加载我们的10  Person的,虽然我们是通过TulingClassLoader 去加载我们的Person.class11  但是由于双亲委派模型会委托我们的AppClassLoader去我们的classes路面下去12  加载Person.class由于我们的classes目录下存在我们的Person.class13  所以我们的Person.class被我们的AppClassLoader去加载.14151617  ===================================================18  若我们把classpath下的Person.class给删除掉,那么我们的19  TulingClassLoader尝试去加载我们的Person.class,由于双亲委派模型下会委托父类AppClassLoader20  加载,但是我们人工把类路径下的Person.class给删除掉了后,那么我们的AppClassLoader加载不了21  我们的Person.class,从而是由我们的TulingClassLoader去加载.22  **/23  test1();2425  }2627  //正常情况下,把我们的AppIsCustParentDemo放到D:smlz的目录下28  public static void test1() throws ClassNotFoundException {2930  TulingClassLoader tulingClassLoader = new TulingClassLoader("tulingClassLoader");31  //设置加载路径32  tulingClassLoader.setLoadPath("D:smlz");33  //通过自定义类加载器加载我们的AppIsCustParentDemo34  Class> targetClass = tulingClassLoader.loadClass("com.tuling.smlz.jvm.open.AppIsCustParentDemo");3536  System.out.println("targetClass 被class加载器加载..."+targetClass.getClassLoader());3738  }39 }

6、同一个Person.class文件 被我们的不同的类加载器去加载,那么我们的jvm内存种会生成二个对应的Person的Class对象,而且这二个对应的Class对象是相互不可见的(通过Class对象反射创建的实例对象相互是不能够兼容的不能相互转型) 这一点也很好的解释了

1 public class Person {23  private Person person;45  public void setPerson(Object person) {6  this.person = (Person) person;7  }8 }
1 public class Demo {2  //需要把我们的ClassPath下的Person.class给删除3  public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {45  TulingClassLoader classLoader1 = new TulingClassLoader("tulingClassLoader1");6  classLoader1.setLoadPath("D:smlz");78  TulingClassLoader classLoader2 = new TulingClassLoader("tulingClassLoader2");9  classLoader2.setLoadPath("D:smlz");1011  //通过classLoader1加载我们的Person12  Class> class1 = classLoader1.loadClass13("com.tuling.smlz.jvm.open.TheSameClassLoadedByDiffClassLoader.Person");14  System.out.println("class1的类加载器:‐>"+class1.getClassLoader());1516  Class> class2 = classLoader2.loadClass17("com.tuling.smlz.jvm.open.TheSameClassLoadedByDiffClassLoader.Person");18  System.out.println("class2的类加载器:‐>"+class2.getClassLoader());1920  System.out.println("class1==class2:"+(class1==class2));2122  //模拟问题23  Object person = class1.newInstance();2425  Object person2 = class2.newInstance();2627  Method method = class1.getMethod("setPerson",Object.class);28  //会抛出类型转换错误29  method.invoke(person,person2);30  }31 }

7、类加载器的全盘委托机制以及 类加载器的命名空间

(1)类加载器的全盘委托机制:比如我们的Person类是由我们的AClassLoader进行加载的,那么我们Person引用的Dog类就会委托给我们的A ClassLoader进行加载

1 public class Person {23  public Person() {4  System.out.println("Dog类是由我们的类加载器:‐>"+Dog.class.getClassLoader());5  }6 }78 public class Dog {9 }1011 public class MainTest {1213  public static void main(String[] args) {1415  Person person = new Person();16  System.out.println("Person的classLoader:‐>"+person.getClass().getClassLoader());1718  }19 }

(2)类加载器的命名空间

类加载器的命名空间 是有类加载器本身以及所有父加载器所加载出来的binary name(full class name)组成。

1)在同一个命名空间里,不允许出现二个完全一样的binary name。

2)在不同的命名空间种,可以出现二个相同的binary name。当时二 者对应的Class对象是相互不能感知到的,也就是说Class对象的类型是不一样的

3)子加载器的命名空间中的binary name对应的类中可以访问 父加 载器命名空间中binary name对应的类,反之不行

1a8277ebcdf0733f7a566f60ddf21bd5.png

8、验证子加载器加载出来的类可以访问父加载器加载的类

测试环境:我们的Person是由我们的自定义类加载器(把classpath下的Person.class删除,并且把Person.class copy到磁盘文件上)TulingClassLoader进行加载的,Dog 是由我们的AppClassLoader进行加载的. 我们在Person中访问Dog。

1 public class Dog {2 }34 public class Person {56  public Person() {7  new Dog();8  System.out.println("Dog的classLoader:‐‐>"+Dog.class.getClassLoader());9  }1011 }1213 public class TestDemo {14  public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {15  TulingClassLoader classLoader = new TulingClassLoader("tulingClassLoader");16  classLoader.setLoadPath("D:smlz");17  Class> clazz = classLoader.loadClass("com.tuling.smlz.jvm.open.classloadernamespace.Person");18  clazz.newInstance();1920  System.out.println("Person的类加载器:"+clazz.getClassLoader());21  }22 }

9、如何证明父加载加载的类不能访问子加载器加载的类

测试环境:把我们的Person.class放置在C:ProgramFilesJavajdk1.8.0_131jreclasses这个目录下,那么我们的Person.class就会被我们的启动类加载器加载,而我们的Dog类是被AppClassLoader进行加载,我们的Person类 中引用我们的Dog类会抛出异常。

1 public class Dog {2 }34 public class Person {56  public Person() {7  new Dog();8  System.out.println("Dog的classLoader:‐‐>"+ Dog.class.getClassLoader());9  }10 }111213 public class TestDemo {1415  public static void main(String[] args) throws IllegalAccessException, InstantiationException {161718  System.out.println("Person的类加载器:"+Person.class.getClassLoader());1920  System.out.println("Dog的类加载器:"+Dog.class.getClassLoader());2122  Class> clazz = Person.class;23  clazz.newInstance();2425  }26 }2728 运行结果:29  Person的类加载器:null30 Dog的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac231 Exception in thread "main" java.lang.NoClassDefFoundError: com/tuling/smlz/jvm/open/ParentClassLoaderNotAccessSonClassLoader/Dog32  at com.tuling.smlz.jvm.open.ParentClassLoaderNotAccessSonClassLoader.Person.(Person.java:11)33  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(NativeMethod)34  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)35  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)36  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)37  at java.lang.Class.newInstance(Class.java:442)38  at com.tuling.smlz.jvm.open.ParentClassLoaderNotAccessSonClassLoader.TestDemo.main(TestDemo.java:16)

10、打破双亲委派模型之 线程上下文类加载器

场景:JDBC接口技术之SPI之应用。

92b2a7b2ac12a50467b5a5b1b31bc4ec.png

类的首次主动使用会触发类的初始化。

1)调用静态方法

2)给静态变量赋值获取读取一个静态变量

3)反射 Class.forName

4)new 出一个对象

5)执行main方法的时候

6)初始化子类会初始化他的父类


本文福利:

私信回复 888 领取一套200页2020最新的Java面试题总结手册


最后

欢迎大家一起交流,喜欢文章记得关注我点赞转发哟,感谢支持!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/219924
推荐阅读
相关标签
  

闽ICP备14008679号