java中的类加载器与反射

 
类加载器的作用:负责将.class文件加载到内存中
ClassLoader类加载器的加载过程:
类加载的时机:
当一个类被使用的时候,都会加载到内存中
类加载的过程:
加载、验证、准备、初始化
类加载器的分类 
  • Bootsrap class loader:虚拟机内置类加载器
  • Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
  • System class loader:系统类加载器,负责加载当前应用classpath下的所有的jar包和类。
双亲委派模型:
每一个类都有一个相对应的类加载器,系统中的ClassLoader在协同工作会默认使用双亲委派模型,即在类加载的时候,系统就会首先判断当前类是否被加载过,已经加载的类会直接返回,否则都会尝试加载,加载的时候,首先会把该变形受害者不委派该父类加载器的loadClass()处理,因此所有的请求最终都应该传送到顶层的启动类加载器BootstrapClassLoader中,当父类加载器无法处理时,都由自己来处理,当父类加载器为null时,会使用启动类加载器BoostrapClassLoader作为父类加载器。
通过下面的这个例子来验证每个类都有一个父类加载器:
 1 public class ClassLoaderDemo {
 2     public static void main(String[] args) {
 3         System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader());
 4         System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent());
 5         System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent());
 6     }
 7 }
 8 -------------------------------------------------------------------
 9 ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
10 The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586
11 The GrandParent of ClassLodarDemo's ClassLoader is null
12 注意:
13 AppClassLoader的父类加载器为ExtClassLoader
14 ExtClassLoader的父类加载器为null,null并不代表ExtClassLoader没有父类加载器,而是BoostrapClassLoader。
15 类加载器之间的“父子”关系不是通过继承来体现的,是由“优先级”来决定的~16 双亲委派模型实现的源码分析:
17 private final ClassLoader parent;
18 protected Class<?> loadClass(String name, boolean resolve)
19         throws ClassNotFoundException
20     {
21         synchronized (getClassLoadingLock(name)) {
22             // 首先,检查请求的类是否已经被加载过
23             Class<?> c = findLoadedClass(name);
24             if (c == null) {
25                 long t0 = System.nanoTime();
26                 try {
27                     if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理
28                         c = parent.loadClass(name, false);
29                     } else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载
30                         c = findBootstrapClassOrNull(name);
31                     }
32                 } catch (ClassNotFoundException e) {
33                    //抛出异常说明父类加载器无法完成加载请求
34                 }
35                 
36                 if (c == null) {
37                     long t1 = System.nanoTime();
38                     //自己尝试加载
39                     c = findClass(name);
40                     // this is the defining class loader; record the stats
41                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
42                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
43                     sun.misc.PerfCounter.getFindClasses().increment();
44                 }
45             }
46             if (resolve) {
47                 resolveClass(c);
48             }
49             return c;
50         }
51     }

 

双亲委派模型的好处:
双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载(JVM区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了Java的核心API不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为java.lang.Object类的话,程序运行时,系统就会出现多个不同的Object类。
自定义类加载器:
除了BootstrapClassLoaderader其他类加载器均由Java实现且全部继承处java.lang.ClassLoader如果要自定义类加载器,就要继承ClassLoader。

反射:
    把.class文件加载进内存,把.class中的所有内容,封装成 一个一个的对象的过程,在程序中可以通过这些对象动态的完成方法的调用,成员高变量的赋值,对象的创建!
反射的好处:
    可以提高程序的灵活度
反射获取Class对象的三种方式:
  • 1、Class.forName(全类名)
  • 2、类名.class
  • 3、对象名.getClass();
注意:一个类在一次程序的运行过程中,只会被加载一次,也就是说,每一个.class文件,在内存中相对应的一个唯一的class类对象!
反射获取Constructor对象:
Class:
    Constructor[] getConstructors();//返回所有public修改的构造方法
    Constructor[] getDeclaredConstructors();//返回所有的构造方法
    //小括号一定要跟构造方法中的形参保持一致,顺序也不能乱,注意:
    Constructor getConstructor(Class...parameterTypes);//返回单个的公共的构造方法
    //
    Constructor getDeclaredConstructor(Class...parameterTypes);//获取指定形参的私有的构造方法
--------------------------------------------------------------
使用的步骤:
1.首先得拿到某个类的class对象
2.然后再对象名.getConstructors
------------------------------------------------------
    getXXX();只能获取被public修改的内容
    getDeclaredXXX();可以获取任意修改符的内容
反射复用Constructor创建对象
Constructor:
    Object newInstance(Object...args);//根据指定构造方法创建对象
Constructor:
    Object newInstance();//无参构造创建对象
 
Class:
    Object newInstance();
//可以直接通过Class对象创建空参对象
Class<?> aClass = Class.forName(全类名);
Object o = aClass.newInstance();
 
如果通过反射获取到一个私有的构造方法,能不能直接创建对象?
    public void setAccessible(boolean flag)//true 暴力反射
反射获取Field对象(成员变量)
Class:
    Field[] getFields();
    Field[] getDeclaredFields();
    Field getField(String name);
    Field getDeclaredField(String name);
利用Field赋值和获取值
Field:
    注意:
        //target是用来获取指定对象的成员变量的值
    Object get(Object target); 返回指定对象的Field值
    void set(Object target,Object value)
获取Method对象
Class:
    Method[] getMethods();
    Method[] getDeclaredMethods();
    Method getMethod(String name,Class...parameterTypes);
    Method getDeclaredMethod(String name,Class...parameterTypes);
反射利用Method对象运行方法:
Method:
    Object invoke(Object obj,Object...args);
    obj:指定该方法的调用者是谁
    args:该方法执行时需要的参数
    返回值:
        该方法执行后的结果是什么就返回
关于反射的两个小练习:
 1  2 public class Test {
 3     public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
 4  
 5  
 6         //1.准备一个配置文件,配置文件是要测试的类和测试的方法
 7         //2.使用ClassLoader,读取prop.properties文件进内存,返回一个输入流对象
 8         InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("prop.properties");
 9         //3.创建一个Properties集合对象
10         Properties prop = new Properties();
11         //4.调用Properties集合的load(InputStream in);
12         prop.load(in);
13         //5.调用Class.forName(String path)把类加载进内存,生成一个Class对象
14         Class<?> clazz = Class.forName(prop.getProperty("className1"));
15         //6.通过Class对象的getMethod方法,得到即将要测试的方法
16         Method method = clazz.getMethod(prop.getProperty("method1"));
17         //7.执行Method
18         method.invoke(clazz.newInstance());
19     }
20 }
21 ------------------------------------------------------------------
22 #ClassName=com.hk.Student
23 #Method=study
24 ClassName=com.hk.Teacher
25 Method=teach
26 27 public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, NoSuchFieldException {
28         //获取某个类的类对象,注意是全类名的形式
29         Class<?> clazz = Class.forName("com.hk.myclassloader.Student");
30         //创建无参构造,如果是private修改的要加Declared!!!
31         Student student = (Student) clazz.newInstance();
32         //获取带参构造
33         Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
34         //使用带参构造创建对象
35         Student student1 = (Student) constructor.newInstance("zs", 22);
36         //获取私有的成员变量name
37         Field name = clazz.getDeclaredField("name");
38         //暴力反射
39         name.setAccessible(true);
40         //设置student对象的name值
41         name.set(student, "zs");
42         name.set(student, "ls");
43         //获得clazz类对象的toString方法
44         Method toString = clazz.getMethod("toString");
45         //打印
46         System.out.println(toString);
47         //使用invoke执行student12对象的toString就去
48         System.out.println(toString.invoke(student1));
49     }
50  

 

posted @ 2020-09-02 22:53  闪电蛙  阅读(232)  评论(0)    收藏  举报