深入分析ClassLoader工作机制Word格式文档下载.docx
- 文档编号:20011726
- 上传时间:2023-01-15
- 格式:DOCX
- 页数:17
- 大小:66.89KB
深入分析ClassLoader工作机制Word格式文档下载.docx
《深入分析ClassLoader工作机制Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《深入分析ClassLoader工作机制Word格式文档下载.docx(17页珍藏版)》请在冰豆网上搜索。
MetaIndex.registerDirectory(var0[var2]);
}
returnnewLauncher.ExtClassLoader(var0);
///这里
});
}catch(PrivilegedActionExceptionvar2){
throw(IOException)var2.getException();
privatestaticFile[]getExtDirs(){
Stringvar0=System.getProperty("
java.ext.dirs"
);
APPClassLoader:
使用ExtClassLoader作为父类加载器
staticclassAppClassLoaderextendsURLClassLoader{
publicstaticClassLoadergetAppClassLoader(finalClassLoadervar0)throwsIOException{
finalStringvar1=System.getProperty("
java.class.path"
finalFile[]var2=var1==null?
newFile[0]:
Launcher.getClassPath(var1);
return(ClassLoader)AccessController.doPrivileged(newPrivilegedAction(){
publicLauncher.AppClassLoaderrun(){
URL[]var1x=var1==null?
newURL[0]:
Launcher.pathToURLs(var2);
returnnewLauncher.AppClassLoader(var1x,var0);
这是URLClassLoader的:
publicURLClassLoader(URL[]urls,ClassLoaderparent,....){}
加载Class文件到内存中的2种方式
一种是隐式加载,一种是显式加载。
隐式加载:
就是不需要在代码中调用ClassLoader来加载的类,而是通过JVM来自动加载这些需要的类到内存中的方式,比如在我们继承或引用某些类的时候,JVM分析当前类引用的类不在内存中,那么就会自动加载类到内存中。
显示加载:
就是在代码中调用ClassLoader来加载一个类,比如this.getClass().getClassLoader().loadClass()或者Class.forName()或者自定义的ClassLoader的loadClass方法。
混合的方式,就是在自定义ClassLoader时候引用其他类,就算是隐式加载。
加载class的过程
加载过程,简单地看了一下代码。
大概是:
—>
findClass(finalStringname)
defineClass(Stringname,Resourceres)
Class
加载字节码到内存中
在抽象类ClassLoader中,很多实现都交给子类实现,如如何找到,如何加载到内存中。
就是findClass()方法。
最常用的URLClassLoader的findClass源码如下;
protectedClass<
?
>
findClass(finalStringname)
throwsClassNotFoundException
{
returnAccessController.doPrivileged(
newPrivilegedExceptionAction<
Class>
(){
publicClassrun()throwsClassNotFoundException{
Stringpath=name.replace('
.'
'
/'
).concat("
.class"
Resourceres=ucp.getResource(path,false);
if(res!
=null){
returndefineClass(name,res);
}catch(IOExceptione){
thrownewClassNotFoundException(name,e);
}else{
thrownewClassNotFoundException(name);
},acc);
}catch(java.security.PrivilegedActionExceptionpae){
throw(ClassNotFoundException)pae.getException();
简要说明一下:
首先将Path,类名的包定义,由’.’换为路径的’/’。
并将.class加上。
然后ucp(URLClassPath类型)定义哪里找到这个class文件,读取它的字节流。
然后通过defineClass来实例类class对象。
根据URL传过来的是Jar包还是实际Class文件,就能创建不同的Loader了,这样就实现加载Class文件到内存中。
下图还可以看出Loader是URLClassPath的内部类。
常见加载类错误分析
ClassNotFoundException
这个非常常见的错误,一般问题是找不到.class文件,就是classpath路径和你想要的class文件路径不在一个地方。
如果不知道classpath路径是什么,可以通过下列代码方式找到。
this.getClass().getClassLoader().getResource("
"
).toString();
通常加载一个不知道的类时就会发生这样的错误。
publicclassnotFound{
publicstaticvoidmain(String[]args){
Class.forName("
NotFoundClass"
}catch(ClassNotFoundExceptione){
e.printStackTrace();
显示加载的几种方式:
Class.forName();
ClassLoader的loadClass();
ClassLoader的findSystemClass();
UnsatisfiedLinkError
这个问题不常见,出现的场景就是调用某些本地lib中的native方法时,没有找到这样的lib。
报出的错误。
/**
*文件描述:
*/
publicclassNoLibException{
publicnativevoidnativeMethod();
static{
System.loadLibrary("
NoLib"
publicstaticvoidmain(String[]args){
newNoLibException().nativeMethod();
java.lang.UnsatisfiedLinkError:
noNoLibinjava.library.path
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:
1886)
atjava.lang.Runtime.loadLibrary0(Runtime.java:
849)
atjava.lang.System.loadLibrary(System.java:
1088)
atCCF.NoLibException.<
clinit>
(NoLibException.java:
11)
ClassCastException
强制转换异常,比如说想把String强制转换为Integer
代码
importjava.util.HashMap;
importjava.util.Map;
/**
publicclassCastException{
publicstaticMapm=newHashMap(){
put("
a"
"
2"
};
Integerinteger=(Integer)m.get("
System.out.println(integer);
结果:
Exceptioninthread"
main"
java.lang.ClassCastException:
java.lang.Stringcannotbecasttojava.lang.Integer
atCCF.CastException.main(CastException.java:
19)
atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57)
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43)
atjava.lang.reflect.Method.invoke(Method.java:
606)
atcom.intellij.rt.execution.application.AppMain.main(AppMain.java:
144)
实现自己的ClassLoader
获取类加载器
获取类加载器的实现非常简单,简单到只需要一句话,获取当前线程的ClassLoader即可。
*获取类加载器
*
*@return
*/
publicstaticClassLoadergetClassLoader(){
returnThread.currentThread().getContextClassLoader();
根据类名称加载类
根据类名称加载类,这个方法是利用Class.forName实现的。
相当于在Class.forName修饰了一层。
实现如下:
*加载类
*
*@paramclassName类名
*@paramisInitialized此处的是否初始化标志指的是是否执行类的静态代码块
publicstaticClass<
loadClass(StringclassName,booleanisInitialized){
Class<
cls;
cls=Class.forName(className,isInitialized,getClassLoader());
}catch(ClassNotFoundExceptione){
thrownewRuntimeException(e);
returncls;
一个值得注意的点是:
参数booleanisInitialized表达的标志位指的是是否执行类的静态代码块,为了提高性能可以设置为false。
获取指定包下的所有类
获取指定包下的所有类是最麻烦的一件事情,因为包可以是file,也可是jar包。
另外在包名和实际包所在的路径需要转换,比如需要把”.”转为”/”把”%20”转换为”“;
对于jar包的类,使用JarURLConnnection连接
*获取指定包下的所有类
*@parampackageName
publicstaticSet<
Class<
getClassSet(StringpackageName){
Set<
classSet=newHashSet<
();
/*获取资源,将包名替换为包路径*/
Enumeration<
URL>
urlEnumeration=getClassLoader().getResources(packageName.replaceAll("
."
/"
));
while(urlEnumeration.hasMoreElements()){
URLurl=urlEnumeration.nextElement();
if(url!
Stringprotocol=url.getProtocol();
/*file方式处理*/
if(protocol.equals("
file"
)){
/*%20换成空格*/
StringpackagePath=url.getPath().replaceAll("
%20"
"
addClass(classSet,packagePath,packageName);
}elseif(protocol.equals("
jar"
)){/*jar包处理*/
/*打开连接*/
JarURLConnectionjarURLConnection=(JarURLConnection)url.openConnection();
if(jarURLConnection!
JarFilejarFile=jarURLConnection.getJarFile();
if(jarFile!
/*遍历元素*/
JarEntry>
jarEntries=jarFile.entries();
while(jarEntries.hasMoreElements()){
JarEntryjarEntry=jarEntries.nextElement();
/*以class结尾*/
if(jarEntry.getName().endsWith("
/*获取类名,并将"
转换为"
StringclassName=jarEntry.getName().substring(0,jarEntry.getName().lastIndexOf("
)).replaceAll("
addClassAction(classSet,className);
returnclassSet;
*内部调用loadClass,并将Class添加到Set中
*@paramclassSet
*@paramclassName
privatestaticvoidaddClassAction(Set<
classSet,StringclassName){
cls=loadClass(className,false);
classSet.add(cls);
*@parampackagePath
privatestaticvoidaddClass(Set<
classSet,StringpackagePath,StringpackageName){
File[]files=newFile(packagePath).listFiles(newFileFilter(){
/*将所有的是class文件和目录文件返回*/
publicbooleanaccept(Filefile){
return(file.isFile()&
&
file.getName().endsWith("
))||file.isDirectory();
for(Filefile:
files){
StringfileName=file.getName();
/*文件方式处理*/
if(file.isFile()){
StringclassName=fileName.substring(0,fileName.lastIndexOf("
if(packageName!
className=packageName+"
+className;
}else{/*文件夹方式处理*/
StringsubPackagePath=fileName;
if(packagePath!
/*子目录*/
subPackagePath=packagePath+"
+subPackagePath;
StringsubPackageName=fileName;
/*子包名*/
subPackageName=packageName+"
+subPackageName;
/*递归处理*/
addClass(classSet,subPackagePath,subPackageName);
函数注释写得很清楚,可以细细琢磨。
一个值得注意的细节,关于匿名内部类,如果要访问外部类的变量,则需要再外部变量加final修饰如下所示:
原因是执行匿名内部类的时候,形成的函数的栈帧无法访问到外部类栈帧的局部变量,所有把外部变量final化则可以访问到这个变量,具体可以参考其他文章。
只是突然想到这个知识点并稍微说说而已。
classSet,StringpackagePath,finalStringpackageName){
/*将所有的是class文件和目录文件返回,一个细节,如果匿名内部类里要使用packageName则在addClass参数中要加入修饰词final
*即finalStringpackageName
publicbooleancept(Filefile){
return(packageName!
=null&
file.isFile()&
加载自定义格式的Class文件
实现一个从远端服务器,经过加密的class文件的网络传输,在本地解密,并通过defineClass方法创建这个类的实例的方法。
代码如下:
importjava.io.ByteArray
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入 分析 ClassLoader 工作 机制