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

浙公网安备 33010602011771号