学习资料来源:https://www.bilibili.com/video/av30023103/
部分段落链接:https://blog.csdn.net/ln152315/article/details/79223441
一个Java文件从编码完成到最终执行,一般主要包括两个过程
编译,即把我们写好的java文件,通过javac命令编译成字节码,也就是我们常说的.class文件。
运行,则是把编译声称的.class文件交给Java虚拟机(JVM)执行。
而我们所说的类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。
举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。
由此可见,JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时的数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区数据的访问入口。
标准的javaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象。
注:为什么会有自定义类加载器
java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个java类,即java.lang.Class类的一个实例
除此之外,ClassLoader还负责加载java应用所需的资源,如图像文件和配置文件等。
对以上给出的方法,表示类名称的name参数的值是类的二进制名称,需要注意的是内部类的表示,如com.example.Sample$1和com.example.Sample$Inner等表示方式。
注意:被两个类加载器加载的同一个类,JVM不认为是相同的类。
package classinit;
import java.io.*;
public class FileSystemClassLoader extends java.lang.ClassLoader {
//传一个类名,在对应的文件目录中寻找class文件
private String rootDir; //根目录
public FileSystemClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c =findLoadedClass(name);
//查询有没有加载过这个类,如果已经加载,则直接返回加载好的类,如果没有,则加载新的类
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载器
}catch (Exception e){
//e.printStackTrace();
}
if(c!=null){
return c;
}else{
//想办法读取这个文件,转化为字节数组
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name,classData,0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname) {
String path = rootDir + "/" +classname.replace(".","/")+".class";
//将流中的数据转化为字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
is = new FileInputStream(path);
byte[] buffer = new byte[1024];
int temp = 0;
while((temp = is.read(buffer))!=-1){
baos.write(buffer,0,temp);
}
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
if(is!=null){
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package classinit;
public class Demo03 {
public static void main(String[] args){
FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava");
FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/myjava");
try {
Class<?> c1 = loader.loadClass("pri.stu.lwj.HelloWorld");
Class<?> c2 =loader.loadClass("pri.stu.lwj.HelloWorld");
Class<?> c3 =loader2.loadClass("pri.stu.lwj.HelloWorld");
Class<?> c4 =loader2.loadClass("java.lang.String");
Class<?> c5 = loader2.loadClass("classinit.Demo03");
System.out.println(c1);
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
System.out.println(c5.hashCode());
System.out.println(c1.getClassLoader());
System.out.println(c3.getClassLoader());
System.out.println(c4.getClassLoader());
System.out.println(c5.getClassLoader());
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
}
}
package classinit;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
public class NetClassLoader extends ClassLoader {
//www.XXX....
private String rootUrl; //Url
public NetClassLoader(String rootUrl){
this.rootUrl = rootUrl;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c =findLoadedClass(name);
//查询有没有加载过这个类,如果已经加载,则直接返回加载好的类,如果没有,则加载新的类
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载器
}catch (Exception e){
//e.printStackTrace();
}
if(c!=null){
return c;
}else{
//想办法读取这个文件,转化为字节数组
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name,classData,0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname) {
String path = rootUrl + "/" +classname.replace(".","/")+".class";
//将流中的数据转化为字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
URL url = new URL(path);
//is = new FileInputStream(path);
is = url.openStream();
byte[] buffer = new byte[1024];
int temp = 0;
while((temp = is.read(buffer))!=-1){
baos.write(buffer,0,temp);
}
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
if(is!=null){
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package classinit;
import java.io.*;
public class EncriptUtil {
public static void main(String[] args){
encript(new File("d:/myjava/HelloWorld.class"),new File("d:/myjava/temp/HelloWorld.class"));
}
public static void encript(File src, File dest){
FileInputStream fis = null;
FileOutputStream fos =null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
//进行加密解密操作
int temp = -1;
while((temp = fis.read())!=-1){
fos.write(temp^0xff);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis!=null) {
fis.close();
}
}catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null) {
fos.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
package classinit;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class DecriptClassLoader extends ClassLoader {
//传一个类名,在对应的文件目录中寻找class文件
private String rootDir; //根目录
public DecriptClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c =findLoadedClass(name);
//查询有没有加载过这个类,如果已经加载,则直接返回加载好的类,如果没有,则加载新的类
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载器
}catch (Exception e){
//e.printStackTrace();
}
if(c!=null){
return c;
}else{
//想办法读取这个文件,转化为字节数组
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name,classData,0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname) {
String path = rootDir + "/" +classname.replace(".","/")+".class";
//将流中的数据转化为字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
is = new FileInputStream(path);
int temp = -1;
while((temp = is.read())!=-1){
baos.write(temp^0xff);
}
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
if(is!=null){
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package classinit;
public class Demo04 {
public static void main(String[] args) throws Exception {
//测试取反操作
//int a = 3; //0000 0011
//System.out.println(Integer.toBinaryString(a^0xff));
//测试正常类加载器加载加密类
//FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava/temp");
//Class<?> c =loader.loadClass("HelloWorld");
//System.out.println(c);
//测试解密类加载器解密
DecriptClassLoader loader = new DecriptClassLoader("d:/myjava/temp");
Class<?> c = loader.loadClass("HelloWorld");
System.out.println(c);
}
}
比如,ClassA本身在Ext下找到,那么他里面new出来的一些类也就只能用Ext去查找了(不会低一个级别),所以有些命名App可以找到的,却找不到了。
简而言之,接口是由一方设计(Oracle),由某个类加载器实现(第三方),实现类由另一方设置,由另一个类加载实现,产生了冲突,因而要用新的机制暂时代替双亲委托机制。实现方法就是在类加载器的代码中不去遵守双亲委托机制的代码。
package classinit;
public class Demo05 {
public static void main(String[] args){
ClassLoader loader = Demo05.class.getClassLoader();
System.out.println(loader);
ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
System.out.println(loader2);
Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("d:/myjava/"));
System.out.println(Thread.currentThread().getContextClassLoader());
try {
Class<Demo01> c = (Class<Demo01>) Thread.currentThread().getContextClassLoader().loadClass("classinit.Demo01");
System.out.println(c);
System.out.println(c.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。