最近面试过程中被问到,Java反射中Class.forName()加载类和使用ClassLoader加载类的区别。当时没想到,后来自己研究了一下,写下来记录一下。说明Class.forName()和ClassLoader都可以加载java中的类。ClassLoader是一个类加载器,遵循双亲委派模型,最终调用启动类加载器。它的作用是“通过一个类的全限定名获取一个描述这个类的二进制字节流”,获取二进制流后放入JVM中。.Class.forName()方法实际上是通过调用CLassLoader来实现的。Class.forName(StringclassName);该方法源码为@CallerSensitivepublicstaticClass>forName(StringclassName)throwsClassNotFoundException{Class>caller=Reflection.getCallerClass();returnforName0(className,true,ClassLoader.getClassLoader(caller),caller);}最后调用的方法是forName0方法。此forName0方法中的第二个参数默认设置为true。该参数表示是否对加载的类进行初始化。当它设置为true时,类将被初始化。执行类中的静态代码块,给静态变量赋值。也可以在加载类时调用Class.forName(Stringname,booleaninitialize,ClassLoaderloader)方法手动选择是否初始化类。Class.forName(Stringname,booleaninitialize,ClassLoaderloader)的源代码如下:*类**@exceptionLinkageErrorifthelinkagefails*@exceptionExceptionInInitializerErroriftheinitializationprovoked*bythismethodfails*@exceptionClassNotFoundExceptioniftheclasscannotlocatedby*thespecifiedclassloader**@seejava.lang.Class#forName(String)*@seejava.lang.ClassLoader*@since1.2*/@CallerSensitivepublicstaticClass>forName(Stringname,booleaninitialize,ClassLoaderloader)throwsClassNotFoundException{Class>caller=null;SecurityManagersm=System.getSecurityManager();if(sm!=null){//Reflectivecalltogetcallerclassisonlyneededifsecuritymanager//存在。Avoidtheoverheadofmakingthiscallotherwise.caller=Refelection.getCallerClass();if(sun.misc.VM.isSystemDomainLoader(loader)){ClassLoaderccl=ClassLoader.getClassLoader(caller);if(!sun.misc.VM.isSystemDomainLoader(ccl)){sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}}}returnforName0(name,initialize,loader,caller);}源码中的注释只摘录了一部分,对参数initialize的描述为:*if{@codetrue}classwillbeinitialized.*含义:如果参数为真,加载的类将被初始化下面举个例子来说明一下结果:一个包含静态代码块、静态变量、静态方法赋值给静态变量的类publicclassClassForName{//staticcodeblockstatic{System.out.println("Executedstaticcodeblock");}//静态变量privatestaticStringstaticFiled=staticMethod();//给静态变量赋值的静态方法publicstaticStringstaticMethod(){System.out.println("执行的静态方法");return"assignedtostaticfield";}}使用Class.forName()测试方法:@Testpublicvoidtest44(){try{Class.forName("com.eurekaclient2.client2.ClassForName");System.out.println("#########-----------终止符------------##########");}catch(ClassNotFoundExceptione){e.printStackTrace();}}运行结果:#########------------终结符------------###########根据运行结果,Class.forName加载类来初始化类,但是ClassLoader的loadClass并没有初始化类,只是将类加载到虚拟机中。在我们熟悉的Spring框架中应用场景中IOC的实现就是ClassLoader的使用。我们在使用JDBC时,通常会使用Class.forName()方法加载数据库连接驱动。这是因为JDBC规范明确要求Driver(数据库驱动)类必须向DriverManager注册自己。以MySQL驱动为例进行说明:publicclassDriverextendsNonRegisteringDriverimplementsjava.sql.Driver{//~Staticfields/initializers//--------------------------------------------////向DriverManager注册我们自己//static{try{java.sql.DriverManager.registerDriver(newDriver());}catch(SQLExceptionE){thrownewRuntimeException("无法注册驱动程序!");}}//~构造函数//----------------------------------------------------------/***构造新驱动程序并用DriverManager注册**@throwsSQLException*ifadatabaseerroroccurs.*/publicDriver()throwsSQLException{//RequiredforClass.forName().newInstance()}}我们看到,Driver注册到DriverManager的操作是写在静态代码块中的,这就是为什么在写JDBC时要用到Class.forName()的原因。好了,今天就到这里。最近在面试中遇到了很多问题,也学到了很多东西。虽然很累,但是也让人成长了很多。毕竟面试是一个剥皮的过程。各种企业各种面试官的各种问题和场景。给自己打气,找个至少能让你工作几年的公司,别总是让我遇到工作,遇到没多久公司就倒闭了。否则,我也很无奈。
