当前位置: 首页 > >

怎么把object 转成自定义的类_面试官:谈谈类加*靼桑阌忻挥锌垂嗉釉*鞯脑绰耄

发布时间:

一、类加载

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





文末有福利~


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

图示:





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、通过什么来进行加载?(类加*)





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


(1)系统级别


1)启动类加*


2)扩展类加*


3)系统类加*(App类加*)


(2)用户级别的


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


(3)层级结构





二、类加*骷釉匚颐堑腃lass的时候遵循我们的双亲委派模型

在双亲委派机制中,各个加*靼凑崭缸庸叵敌纬墒餍徒峁,除了根加*饕酝,每一个加*饔星抑挥幸桓龈讣釉*





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、双亲委派模型加载的流程图



3、类加*鞯乃孜赡P偷暮么:

总所周知:java.lang.object类是所有类的父类,所以我们程序在运行期间会把java.lang.object类加载到内存中,假如java.lang.object类能够被我们自定义类加*魅ゼ釉氐幕埃敲磈vm中就会存在多份Object的Class对象,而且这些Class对象是不兼容的。


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


借助双亲委派模型,我们java核心类库的类必须是由我们的启动类加*骷釉氐模庋梢匀繁N颐呛诵睦嗫庵换嵩趈vm中存在一份这就不会给自定义类加*魅ゼ釉匚颐呛诵睦嗫獾睦唷


根据我们的演示案例,一个class可以由多个类加*魅ゼ釉兀驴梢栽趈vm内存中存在多个不同版本的Class对象,这些对象是不兼容的。


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



(1)我们自定义类加*鞅匦胍坛蠧lassLoader


(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)特别需要注意:我们自定义的类加*髂锨榭鱿碌母咐嗉釉*魇俏颐堑南低矨ppClassLoader


代码证据:


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 //通过自定义类加*骷釉匚颐堑腁ppIsCustParentDemo34 Class> targetClass = tulingClassLoader.loadClass("com.tuling.smlz.jvm.open.AppIsCustParentDemo");3536 System.out.println("targetClass 被class加*骷釉..."+targetClass.getClassLoader());3738 }39 }
6、同一个Person.class文件 被我们的不同的类加*魅ゼ釉兀敲次颐堑膉vm内存种会生成二个对应的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)类加*鞯娜涛谢疲罕热缥颐堑腜erson类是由我们的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)类加*鞯拿占


类加*鞯拿占 是有类加*鞅旧硪约八懈讣釉*魉釉爻隼吹腷inary name(full class name)组成。


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


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


3)子加*鞯拿占渲械腷inary name对应的类中可以访问 父加 *髅占渲衎inary name对应的类,反之不行





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之应用。





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


1)调用静态方法


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


3)反射 Class.forName


4)new 出一个对象


5)执行main方法的时候


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



本文福利:


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



最后

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



友情链接: hackchn文档网 营销文档网 爱linux网 爱行业网 时尚网