类加载器
(1)什么是类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,
初始化三步来实现对这个类进行初始化。
一个类在加载过程中的三部曲:
1.加载
就是指将class文件读入内存,并为之创建一个Class对象.
任何类被使用时系统都会建立一个Class对象。
2.连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
3.初始化 就是我们以前讲过的初始化步骤
(2)类的加载时机
2.1 创建类的实例
2.2 访问类的静态变量,或者为静态变量赋值
2.3 调用类的静态方法
2.4 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2.5 初始化某个类的子类
2.6 直接使用java.exe命令来运行某个主类
(3)加载器分类
3.1 类加载起的作用?
负责将.class文件加载到内在中,并为之生成对应的Class对象。
3.2 类加载器的分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件
反射
Student.java--Student.class(字节码文件)--看成一个对象,这个对象就叫字节码文件对象--对应的类Class
什么是反射?(画图描述)
答:通过字节码文件对象去使用成员。
(1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
1 public static void main(String[] args) throws ClassNotFoundException { 2 //获取Person类对应的Class字节码文件对象 3 //A:Object类的getClass()方法 4 Person p1 = new Person(); 5 Person p2 = new Person(); 6 7 Class c1 = p1.getClass(); 8 Class c2 = p2.getClass(); 9 System.out.println(p1==p2);//false 10 System.out.println(c1==c2);//true 11 //每一个类会对应一个字节码文件对象,而这个字节码文件对象就是这个类的原型,每一个类有且仅有一个字节码文件对象 12 13 System.out.println("-------------"); 14 //B:数据类型的静态class属性 15 Class c3 = Person.class; 16 System.out.println(c2==c3); 17 18 System.out.println("---------------"); 19 //C:Class类的静态方法forName() 20 //public static Class<?> forName(String className),在这里所说的类名是全类名(带包名的类名) 21 //Class c4 = Class.forName("Person");//java.lang.ClassNotFoundException: Person 22 Class c4 = Class.forName("com.edu_01.Person"); 23 System.out.println(c3==c4); 24 }
获取字节码文件的三种方式
(2)反射的使用步骤
Class:
成员变量 Field
构造方法 Constructor
成员方法 Method
反射:
class字节码文件对象 -- 去得到对应的成员对象 -- 通过该成员的对象调用方法使用
通过反射获取构造方法并使用
(3)案例:
1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
public Constructor getDeclaredConstructor(Class<?>... parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
2.通过反射获取构造方法并创建对象
public T newInstance(Object... initargs)
public static void main(String[] args) throws Exception { //public Constructor[] getConstructors() 获取公共的构造方法 //获取Peroson类对应的字节码文件对象 Class c = Class.forName("com.edu_01.Person"); //获取Perosn类中对应的构造方法 Constructor[] cons = c.getConstructors(); //遍历所有获取到的构造方法 for (Constructor con : cons) { System.out.println(con);//public com.edu_01.Person() } System.out.println("----------------"); //public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有) Constructor[] cons2 = c.getDeclaredConstructors(); for (Constructor con : cons2) { System.out.println(con); } System.out.println("---------------"); //public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造 //获取Person类中的公共的无参数的构造方法 Constructor con = c.getConstructor(); System.out.println(con); //怎么通过我们刚才获取的无参构造创建对象 //public T newInstance(Object... initargs) Object obj = con.newInstance(); System.out.println(obj); System.out.println("-------------"); //获取Person类中的非公共的构造方法 //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor con2 = c.getDeclaredConstructor(String.class,int.class,String.class); //获取构造器的时候,传入的什么参数,在调用获取到的这个构造方法对象的时候也就需要传入什么类型的参数 //取消这个构造器对象所对应的访问权限检测 con2.setAccessible(true); Object obj2 = con2.newInstance("陈奕迅",45,"香港"); System.out.println(obj2); System.out.println("-----------"); //获取被Protected修饰的构造方法 Constructor con3 = c.getDeclaredConstructor(String.class,int.class); //取消访问权限的检测 con3.setAccessible(true); Object obj3 = con3.newInstance("张学友",50); System.out.println(obj3); }
案例1和2
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
public void set(Object obj,Object value)给一个对象的一个字段设置一个值
1 public static void main(String[] args) throws Exception { 2 //获取Perosn类对应的字节码文件对象 3 Class c = Class.forName("com.edu_01.Person"); 4 5 //利用反射获取一个[Perosn对象 6 Constructor con = c.getConstructor(); 7 Object obj = con.newInstance(); 8 9 //获取所有公共的字段对象 10 //public Field[] getFields()获取公有的成员变量 11 Field[] fields = c.getFields(); 12 for (Field field : fields) { 13 System.out.println(field); 14 } 15 16 System.out.println("--------------"); 17 //获取所有的字段对象,包括私有 18 //public Field[] getDeclaredFields()获取全部的成员变量,包括私有 19 Field[] fields2 = c.getDeclaredFields(); 20 for (Field field : fields2) { 21 System.out.println(field); 22 } 23 24 System.out.println("--------------"); 25 //public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有 26 //获取String name;字段 27 Field f = c.getDeclaredField("name"); 28 29 //使用f这个对象给一个Perosn对象设置姓名 30 //public void set(Object obj, Object value) 31 /** 32 * 参数1:需要设置的对象 33 * 参数2:需要给这个对象设置什么值 34 */ 35 //取消访问修饰符的限制 36 f.setAccessible(true); 37 f.set(obj, "谢娜"); 38 39 40 System.out.println("--------------"); 41 //public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取公有的 42 //获取public int age; 43 Field f2 = c.getField("age"); 44 f2.set(obj, 30); 45 46 System.out.println("---------------"); 47 Field f3 = c.getDeclaredField("address"); 48 //取消权限检测 49 f3.setAccessible(true); 50 f3.set(obj, "湖南"); 51 52 System.out.println(obj); 53 54 55 }
案例三
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()获取所有成员方法,包括私有
public Method getMethod(String name, Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有
Object invoke(Object obj, Object... args) 让某一个对象使用这个方法,并且传入参数
1 public static void main(String[] args) throws Exception { 2 //1.获取Person类中所有的公有的成员方法 3 //创建Pwrson类对应的字节码文件对象 4 Class c = Class.forName("com.edu_01.Person"); 5 6 //利用反射的方式创建一个Person对象 7 Constructor con = c.getConstructor(); 8 Object obj = con.newInstance(); 9 10 //public Method[] getMethods()获取所有公共成员方法,包括父类的公共的成员方法 11 Method[] methods = c.getMethods(); 12 for (Method method : methods) { 13 System.out.println(method); 14 } 15 16 System.out.println("---------------"); 17 //获取Person类中的所有的成员方法 18 //public Method[] getDeclaredMethods()获取所有成员方法,包括私有,只能获取本类的所有的成员方法,不能获取父类的 19 Method[] methods2 = c.getDeclaredMethods(); 20 for (Method method : methods2) { 21 System.out.println(method); 22 } 23 24 System.out.println("---------------"); 25 // public Method getMethod(String name, Class<?>... parameterTypes) 26 //参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法 27 //获取method()这个公有的成员方法 28 Method m = c.getMethod("method"); 29 System.out.println(m); 30 31 System.out.println("---------------"); 32 //public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 33 //参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有 34 //需求获取method这个个方法 35 Method m2 = c.getDeclaredMethod("method"); 36 System.out.println(m2); 37 38 //使用m2这个成员方法的对象 39 //public Object invoke(Object obj,Object... args) 40 /** 41 * 参数1:执行m2这个方法的对象 42 * 参数2:执行m2这个方法的时候,需要传入的参数 43 */ 44 m2.invoke(obj); 45 46 System.out.println("-----------------"); 47 //获取Person类中function方法 48 Method m3 = c.getDeclaredMethod("function", int.class); 49 //取消权限检测 50 m3.setAccessible(true); 51 m3.invoke(obj, 10); 52 53 System.out.println("-------------------"); 54 Method m4 = c.getDeclaredMethod("show", String.class,String.class); 55 m4.setAccessible(true); 56 m4.invoke(obj, "张杰","谢娜"); 57 58 }
案例四