黑马程序员——【Java高新技术】——类加载器

一、概述

  (一)类加载器(class loader

  用来动态加载Java类的工具,它本身也是Java类。

  (二)类加载器作用

  负责加载 Java 类的字节代码到 Java 虚拟机中。

  (三)Java类加载器

  1、Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类加载器负责加载特定位置的类:

  (1)BootStrap(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。

  (2)ExtClassLoader(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

  (3)AppClassLoader(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

  2、BootStrap

  类加载器也是Java类,因为其他java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

  3、类加载器演示,代码示例:

1 public class ClassLoaderDemo {
2     public static void main(String[] args) {
3         System.out.println(
4                 ClassLoaderDemo.class.getClassLoader().getClass().getName()
5                 );//sun.misc.Launcher$AppClassLoader,表示由AppClassLoader加载
6         System.out.println(System.class.getClassLoader());//null,表示System这个类时由RootStrap加载的
7     }
8 }

  (四)类加载器的树状组织结构及管辖范围

  演示类加载器的树状组织结构,代码示例如下:

1 public class ClassLoaderTree {
2    public static void main(String[] args) {
3        ClassLoader loader = ClassLoaderTree.class.getClassLoader();
4        while (loader != null) {
5             System.out.println(loader.toString());
6             loader = loader.getParent();
7        }
8    }
9 }

二、类加载器的委托机制

  (一)加载类的过程

  当Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?

  (1)首先,当前线程的类加载器去加载线程中的第一个类。

  (2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B。

  (3)还可直接调用ClassLoader的LoaderClass()方法,来指定某个类加载器去加载某个类。

  (二)类加载器的委托

  1、每个类加载器加载类时,又先委托给上级类加载器。

  (1)首先,类加载器发起者一级级向上委托,直至BootStrap;

  (2)从BootStrap加载器开始查找,找到了直接返回,没找到再一级级向下返回让其子级加载器查找;

  (3)一直返回到发起者,发起者再没找到,则抛出ClassNotFoundException,而不再找发起者类加载器的子类来找。

  2、面试题:可不可以自己写一个java.lang.System类呢?

  (1)通常是不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给最顶级的BootStrap,由于BootStrap在其指定的目录中加载的是rt.jar中的类,rt.jar中有System类,那么就会直接加载,而不会去加载自定义的System类。

  (2)但是还是有办法加载自定义的System类的,此时就不能交给上级加载了,需要用自定义的类加载器加载,这就需要有特殊的写法才能去加载这个自定义的System类的。

三、自定义类加载器

  (一)概述

  1、自定义的类加载器必须继承ClassLoader,要覆盖其中的findClass(String name)方法,不用覆盖loadClass()方法。

  2、类加载器的原理

  (1)loadClass()内部会先委托给父级,当父级找不到后返回,再调用findClass(String name)方法,即自定义的类加载器去找。所以只需要覆写findClass方法,就能实现用自定义的类加载器加载类的目的。

  (2)一般自定义类加载器,会把需要加载的类放在自己指定的目录中,而java中已有的类加载器是不知道你这个目录的,所以会找不到。这样才会调用你复写的findClass()方法,用你自定义的类加载器去指定的目录加载类。

  3、模板方法设计模式

  模板方法设计模式,保留了loadClass()方法中的流程(此流程就是先找父级,找不到再调用自定义的类加载器),因此只需覆盖findClass()方法,实现局部细节就行了。

  4、当得到了class文件中的二进制数据,如何将class文件的内容转换成字节码?

  ClassLoader提供了一个protected Class<?> defineClass(String name , byte[] b , int off , int len)方法,只需要将类的class文件传入,就可以将其变为字节码。

  (二)编程步骤

  1、编写一个对文件内容进行简单加密的程序

  2、编写好了一个自己的类加载器,可实现对加密过来的类进行加载和解密。

  3、编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中除了可使用ClassLoader的loadClass方法外,还可以使用设置线程的上下文类加载器或系统类加载器,然后再使用Class.forName。

  (三)编码步骤

  1、对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast

  2、运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast

  3、用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。

  4、删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。

  (四)自定义类加载器,代码实现(张孝祥老师讲解)

  1、定义一个测试类:ClassLoaderAttachment

1 // 定义一个测试类,继承Date,用于加载
2 import java.util.Date;
3 public class ClassLoaderAttachment extends Date{
4     public String toString(){
5         return " hello,itcast";
6     }
7 }

  2、自定义类加载器:MyClassLoader,继承ClassLoader,覆盖findClass()方法。

 1 import java.io.ByteArrayOutputStream;
 2 import java.io.FileInputStream;
 3 import java.io.FileOutputStream;
 4 import java.io.InputStream;
 5 import java.io.OutputStream;
 6 public class MyClassLoader extends ClassLoader{
 7     public static void main(String[] args) throws Exception {
 8         String srcPath = args[0];
 9         String destDir = args[1];
10         FileInputStream fis = new FileInputStream(srcPath);
11         String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
12         String destFilePath = destDir+"\\"+destFileName;
13         FileOutputStream fos = new FileOutputStream(destFilePath);
14         cypher(fis,fos);//加密class字节码
15         fis.close();
16         fos.close();
17     }
18     //加密方法
19     private static void cypher(InputStream ips,OutputStream ops) throws Exception{
20         int b = -1;
21         while((b = ips.read())!=-1){
22             ops.write(b^0xff);
23         }
24     }
25
26 private String classDir;
27
28    @Override
29    protected Class<?> findClass(String name) throws ClassNotFoundException {
30     String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf(‘.‘)+1) + ".class";
31     try {
32         FileInputStream fis = new FileInputStream(classFileName);
33         ByteArrayOutputStream bos = new ByteArrayOutputStream();
34         cypher(fis,bos);//解密
35         fis.close();
36         System.out.println("aaa");
37         byte[] bytes = bos.toByteArray();
38         return defineClass(bytes, 0, bytes.length);
39     } catch (Exception e) {
40         e.printStackTrace();
41     }
42     return null;
43    }
44
45    public MyClassLoader(){}
46    public MyClassLoader(String classDir){
47     this.classDir = classDir;
48    }
49 }

  3、定义类加载测试类:ClassLoaderTest,测试自定义的类加载器

1 import java.util.Date;
2 public class ClassLoaderTest {
3     public static void main(String[] args) throws Exception {
4         Class clazz = new MyClassLoader (" itcastlib " ) . loadClass ( " cn.itcast . day2 . ClassLoaderAttachment " ) ;
5         Date d1 = (Date)clazz.newInstance();
6         System.out.println(d1);
7     }
8 }
时间: 2024-08-28 03:20:19

黑马程序员——【Java高新技术】——类加载器的相关文章

黑马程序员——Java高新技术代理

代理 普通代理 很多时候,我们使用别人代码往往会发现别人代码的功能并不是十分符合我们的需求,调用别人的方法的时候,总是先new一个对象,然后我们的前处理做完,然后调用别人代码的方法,再加入后处理,这样做往往十分麻烦.代理就为其他类提供了一种控制其对象的方法.代理类和委托类必须实现同一个接口,这样代理类才能在需要的时候代替委托类对象,执行委托类的方法. interface Solution{ public void doSomething(); } //委托类Demo实现了接口 class Dem

黑马程序员——java高新技术(新特性、反射、泛型)

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- java高新技术 第一部分——JDK1.5新特性 1.增强for循环 格式: for(元素类型 变量名 : Collection集合 & 数组 ) { }//增强for循环括号里写两个参数,第一个是声明一个变量,第二个就是需要迭代的容器 高级for循环和传统for循环的区别: 高级for循环在使用时,必须要明确被遍历的目标.这个目标,可以是Collection集合或者数组,如果遍历Collec

黑马程序员——Java高新技术——反射机制

点击打开链接 点击打开链接 点击打开链接 android培训.<a">点击打开链接 点击打开链接 java培训.期待与您交流!">点击打开链接 点击打开链接 反射的基石--Class类 Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class. Class类没有构造函数,不能new对象.怎么得到Class类的实例,有3中方法: ①类名.Class    Class  c1=Date.class; ②对象.getClass 获取对象所属的字

黑马程序员——Java高新技术——反射的复写

由于第一段视频学习效果不理想,希望重新看一遍反射视频,并多方面寻找资料,重新写一遍总结,以期java能力早日提高. Java——反射 一.Class类 Class 类的实例表示正在运行的 Java 应用程序中的类和接口.所以,Class可以提供方法获得动态的java类中的各个属性: (由定义可以知道Class创建的思路就是获得某一个特定java类的信息然后传给Class的对象,那么具体怎么做呢?) 这里说得java类的信息指的就是该java类的计算机的字节码:传给 Class cls1: 所以有

黑马程序员----Java高新技术之反射学习总结

------- android培训.java培训.期待与您交流! ---------- 反射的概念. 1.Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 精妙的总结就是:反射就是将Java类中的各个成分映射成相应的Java类. 3.在Java中,描述事物的各种类同样也是一种事物,也可以用面向对象的方法来描述,即也有一个类来描述众多的J

黑马程序员——java高新技术——网络编程

点击打开链接 点击打开链接 点击打开链接 android培训.<a">点击打开链接 点击打开链接 java培训.期待与您交流!">点击打开链接 点击打开链接 网络编程 网络模型 l   OSI参考模型 l   TCP/IP参考模型 网络通讯要素 l   IP地址:网络中设备的标识,不易记忆,可用主机名,本地回环地址,127.0.0.1  主机名:localhost l   端口号:用于标识进程的逻辑地址,不同进程的标识,有效端口:0~65535,其中0~1024系统使

黑马程序员——JAVA高新技术——反射

----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 一.对于反射的概念 对于JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. JAVA反射(放射)机制:"程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言".从这个观点看,Perl,Python,Ruby

黑马程序员——java高新技术——IO其他流对象

点击打开链接 点击打开链接 点击打开链接 android培训.<a">点击打开链接 点击打开链接 java培训.期待与您交流!">点击打开链接 点击打开链接 IO其他对象 PrintStream:字节打印流.为其他输出流添加了功能,提供了打印方法,可以将各种数据类型的数据原样打印. 构造函数可以接受的参数类型:file对象,字符串路径,字节输出流. 方法:println():打印各种基本数据类型. PrintWrite:字符打印流.构造函数可以接受的参数类型:file

黑马程序员_Java高新技术

1  JDK5的新特性 1.1 静态导入       在API中那些不需要new对象的类,可以在类文件的开头,import static java.lang.Math.*;这里把Math中的所有的静态方法都导入了,在类中不需要调用Math类就能直接用Math的方法了 package cn.wjd.staticimport; import static java.lang.Math.*; public class StaticImport { public static void main(Str

黑马程序员_高新技术_1_Java反射

------- android培训.java培训.期待与您交流! ---------- 0.反射知识体系 下图为反射整体的知识体系,把握住此图也就全局上掌握住反射所有内容. 1.反射概论 1)反射概念 其实字面上可以这么理解反射,平时使用类时都是由类new出对象,而反射则是通过对象"反射"出类的信息,好比一个人照镜子可以看到人类的特征,而看出机制就是镜子反射. 2)Java对象两种类型 Java程序中的许多对象在运行时会出现两种类型:编译时类型和运行时类型.如下代码: Person p