类加载器与反射
一、类加载器
1、类的加载:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
1.1 加载:
就是指将class文件读入内存,并为之自动创建一个Class对象(字节码对象)。
任何类被使用时系统都会建立一个Class对象。
1.2 连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
1.3 初始化
就是初始化步骤
2、类初始化时机(也就是什么时候类会初始化):
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类
3、类加载器:
作用: 负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
4、类加载器的组成
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
二、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(包含私有化的属性);对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
1、class类 字节码文件类
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造的
获得字节码文件的三个方法:
方式一: 通过Object类中的getObject()方法
方式二:通过 类名.class获得
方式三:通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)
package com.oracle.Demo02; public class Demo01 { //反射:目的就是不通过new 对象();得到所有属性和方法 public static void main(String[] args) throws ClassNotFoundException { // 获取字节码对象的三个方式 //1.通过对象获取 Person p=new Person(); Class c=p.getClass(); System.out.println(c); //2.通过类名获取(不管是引用类型还是基本数据类型都具备) Class c1=Person.class; System.out.println(c1); //都为true 因为指向的字节码文件都是同一个 System.out.println(c==c1); //true System.out.println(c.equals(c1)); //true //3.通过Class类中的静态方法forName(完整的包名.类名);获取 Class c2=Class.forName("com.oracle.Demo02.Person"); System.out.println(c2); } }
2、通过反射获得构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法。
2.1 返回一个构造方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
2.2 返回多个构造方法:
public Constructor<?>[ ] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[ ] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
示例代码:
自定义Person类:
package com.oracle.Demo02; public class Person { public String name; private int age; static{ System.out.println("静态代码块"); } public Person(){ System.out.println("空参构造"); } public Person(String name,int age){ this.name=name; this.age=age; System.out.println("有参构造"); } private Person(int age,String name){ this.name=name; this.age=age; System.out.println("私有构造"); } public void eat(){ System.out.println("吃饭"); } public void work(String name){ System.out.println(name+"工作"); } private void run(String name){ System.out.println(name+"跑步"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
获得构造方法并运行:
package com.oracle.Demo02; import java.lang.reflect.Constructor; public class Demo02 { public static void main(String[] args) throws Exception { //反射获取空参构造方法并运行:.getConstructor(); Class c=Class.forName("com.oracle.Demo02.Person"); //获取该类中所有的公共构造方法数组 // Constructor[] con=c.getConstructors(); 本方法还需要循环数组,比较麻烦 // for(Constructor c1:con){ // System.out.println(c1); // } Constructor con=c.getConstructor(); //调用空参构造方法 .newInstance(); // Object obj=con.newInstance(); // System.out.println(obj); //调用work()方法 // Person p=(Person)con.newInstance(); // p.work("张三"); } }
通过反射获取有参构造方法并运行:
package com.oracle.Demo02; import java.lang.reflect.Constructor; public class Demo03 { public static void main(String[] args) throws Exception { // 通过反射获取有参构造方法并运行 Class c=Class.forName("com.oracle.Demo02.Person"); Constructor con=c.getConstructor(String.class,int.class); Object obj=con.newInstance("张胜男",16); System.out.println(obj); } }
快速获取空参并创建对象:
package com.oracle.Demo02; import java.lang.reflect.Constructor; public class Demo04 { public static void main(String[] args) throws Exception { // 快速获取空参并创建对象的方式 //1.类必须有空参构造,2.构造方法必须是public // Class c=Class.forName("com.oracle.Demo02.Person"); // Object obj=c.newInstance(); // System.out.println(obj); //获取私有的构造方法(不推荐) //暴力反射,破坏了程序的封装性和安全性 Class c=Class.forName("com.oracle.Demo02.Person"); //获取所有的构造方法.getDeclaredConstructors(); // Constructor[] con=c.getDeclaredConstructors(); // for(Constructor cc:con){ // System.out.println(cc); // } //调用私有化构造方法 Constructor con=c.getDeclaredConstructor(int.class,String.class); con.setAccessible(true); //暴力反射的开关,为true的时候,可以得到私有的成员 Object obj=con.newInstance(18,"张三"); System.out.println(obj); } }
3、通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
3.1 返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量(不包含私有)
public Field getDeclaredField(String name) 获取指定的任意变量(包含私有)
3.2 返回多个成员变量
public Field [ ] getFields() 获取所有public 修饰的变量 (不包含私有)
public Field [ ] getDeclaredFields() 获取所有的 变量 (包含私有)
获取成员变量,步骤如下:
1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
5. 通过方法,给指定对象的指定成员变量赋值或者获取值
public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值
package com.oracle.Demo02; import java.lang.reflect.Field; public class Demo05 { public static void main(String[] args) throws Exception { // 通过反射获取成员变量并改值 Class c=Class.forName("com.oracle.Demo02.Person"); Object obj=c.newInstance(); //反射获得的类对象 //获取所有的公共的成员变量 Field[] fields=c.getFields(); //遍历 // for(Field f:fields){ // System.out.println(f); // } //通过名字获取公共的成员变量 Field field=c.getField("name"); //给成员变量赋值 field.set(obj, "张三"); System.out.println(obj); } }
4、通过反射获得成员方法并使用:
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
4.1 返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
4.2 返回获取多个方法:
public Method[ ] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[ ] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
package com.oracle.Demo02; import java.lang.reflect.Method; public class Demo06 { public static void main(String[] args) throws Exception { Class c=Class.forName("com.oracle.Demo02.Person"); Object obj=c.newInstance(); //获取所有公共的方法(包括从父类继承来的) // Method[] method=c.getMethods(); // for(Method m:method){ // System.out.println(m); // } //获取空参成员方法并运行,如果是空参方法,只需要写方法名就可以 Method method01=c.getMethod("eat"); //调用方法 method01.invoke(obj); //如果是空参方法,只需要写方法名就可以 System.out.println(method01); } }
获取有参方法并运行:
package com.oracle.Demo02; import java.lang.reflect.Method; public class Demo07 { public static void main(String[] args) throws Exception { //获取有参方法并运行 Class c=Class.forName("com.oracle.Demo02.Person"); Method method=c.getMethod("work", String.class); //添加 .invoke method.invoke(c.newInstance(), "韩凯"); } }
5、通过反射,创建对象,调用指定的方法 重点
获取成员方法,步骤如下:
1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的方法
5. 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
package com.oracle.Demo02; import java.lang.reflect.Method; public class Demo07 { public static void main(String[] args) throws Exception { //获取有参方法并运行 Class c=Class.forName("com.oracle.Demo02.Person"); Method method=c.getMethod("work", String.class); //添加 .invoke method.invoke(c.newInstance(), "韩凯"); } }
6、通过反射,创建对象,调用指定的private 方法
获取私有成员方法,步骤如下:
1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的方法
5. 开启暴力访问
6. 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
7、擦除泛型,添加元素
将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢?
我来告诉大家,其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素
package com.oracle.Demo02; import java.lang.reflect.Method; import java.util.ArrayList; public class Demo08 { //有一个ArrayList<String> list //然后往里面添加int类型数据 public static void main(String[] args) throws Exception { ArrayList<String> arr=new ArrayList<String>(); arr.add("aaa"); //获取集合的字节码对象 Class c=arr.getClass(); //用反射获取 add的方法,Object.calss无关泛型 Method addd=c.getMethod("add", Object.class); //添加int类型的元素 addd.invoke(arr, 1); for(Object obj:arr){ System.out.println(obj); } } }
8、反射配置文件:
package com.oracle.Demo03; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.Properties; public class Demo { //反射配置文件 public static void main(String[] args) throws IOException, Exception { //类不清楚,方法也不清楚 //通过配置文件实现,运行的类名和方法名以键值对的形式 //保存到properrties中,具体运行哪个类里面的方法通过该配置文件去设置 //步骤: //1.准备配置文件,写好键值对 //2.IO读取配置文件Reader //3.文件中的键值对存储到集合中,集合中保存的键值对就是类和方法名 //4.反射获取指定类的class文件对象 //5.class文件对象获取指定方法 //6.运行方法 // Person p=new Person(); // p.eat(); FileReader fr=new FileReader("config.properties");//读取配置好的配置文件 Properties pro=new Properties(); //创建个集合,用来存键值对的 pro.load(fr); //将文件中的键值对存到集合中 fr.close(); //记得关闭 String className=pro.getProperty("className"); //获得类名和方法名 String methodName=pro.getProperty("methodName"); Class c=Class.forName(className); //反射获取字节码文件对象 Object obj=c.newInstance(); //创建类对象 Method method=c.getMethod(methodName); //利用反射获得方法 method.invoke(obj); //执行方法 } }
自定义Person类:
package com.oracle.Demo03; public class Person { public void eat(){ System.out.println("人在吃饭"); } }
自定义Student类:
package com.oracle.Demo03; public class Student { public void study(){ System.out.println("学生在学习"); } }
自定义Worker类:
package com.oracle.Demo03; public class Worker { public void work(){ System.out.println("工人在工作"); } }
练习题:
自定义类:
Person类
package com.oracle.Demo04; public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public Person(){ } public Person(String name,int age){ this.name=name; this.age=age; } }
Two类
package com.oracle.Demo04; public class Two { private String name; public Two(){ System.out.println(name); } public Two(String name) { super(); this.name = name; System.out.println(name); } public void Work(){ System.out.println("键盘敲烂,月薪过万"); } @Override public String toString() { return "Two [name=" + name + "]"; } }
DamoClass自定义类:
package com.oracle.Demo04; public class DemoClass { public void run(){ System.out.println("welcome to oracle"); } }
1、ArrayList<Integer> list = new ArrayList<Integer>(); //这个泛型为Integer的ArrayList中存放一个String类型的对象
//1.ArrayList<Integer> list = new ArrayList<Integer>(); //这个泛型为Integer的ArrayList中存放一个String类型的对象 public class Demo01 { public static void main(String[] args) throws Exception{ ArrayList<Integer> list=new ArrayList<Integer>(); list.add(666); //获取集合的字节码对象 Class c=list.getClass(); //获取集合类中的添加元素方法 Method addd=c.getDeclaredMethod("add", Object.class); //调用方法,并添加元素 addd.invoke(list, "abc"); for(Object a:list){ System.out.println(a); } } }
2、用反射去创建一个对象,有2种方式,尽量用代码去体现
package com.oracle.Demo04; import java.lang.reflect.Constructor; public class Demo02 { //2.用反射去创建一个对象,有2种方式,尽量用代码去体现 public static void main(String[] args) throws Exception { //创建一个关于Two自定义类的字节码对象 要有完整的包名 Class c=Class.forName("com.oracle.Demo04.Two"); //调用.newInstance();空参构造方法 Object obj=c.newInstance(); System.out.println(obj); //调用有参构造方法 Constructor构造器类 Constructor con=c.getConstructor(String.class); Object obj01=con.newInstance("王五"); System.out.println(obj01); } }
3、利用反射创建一个类对象,获取指定的方法并执行
package com.oracle.Demo04; import java.lang.reflect.Method; public class Demo03 { public static void main(String[] args) throws Exception { // 创建一个字节码对象 Class c=Class.forName("com.oracle.Demo04.Two"); //利用反射创建一个类对象 Object obj=c.newInstance(); //通过反射获得方法名为“work”的方法 Method m=c.getMethod("Work"); //调用obj类中的m方法 m.invoke(obj); } }
4、定义一个标准的JavaBean,名叫Person,包含属性name、age。 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置, 不使用setAge方法直接使用反射方式对age赋值。
package com.oracle.Demo04; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Demo4 { // 定义一个标准的JavaBean,名叫Person,包含属性name、age。 // 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置, // 不使用setAge方法直接使用反射方式对age赋值。 public static void main(String[] args) throws Exception { //创建字节码对象 Class c=Class.forName("com.oracle.Demo04.Person"); //获取构造方法 Constructor con=c.getConstructor(String.class,int.class); //使用构造方法 创建类对象 Object obj=con.newInstance("张三",16); Method m=c.getMethod("setName", String.class); m.invoke(obj, "李四"); //通过反射与成员变量名称,获得成员变量对象 Field ageField=c.getDeclaredField("age"); //因为是私有化的成员变量,所以需要暴力访问 ageField.setAccessible(true); //给年龄赋值 ageField.set(obj, 60); System.out.println(obj); } }
5、从文件中通过反射读取键值对
package com.oracle.Demo04; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; public class Demo05 { public static void main(String[] args) throws Exception { //从文件中读取键值对 FileReader fr=new FileReader("democlass.properties"); //创建一个集合存放键值对 Properties pro=new Properties(); pro.load(fr); fr.close(); //通过键获取值的字符串 String name=pro.getProperty("name"); String method=pro.getProperty("method"); //创建字节码对象 Class c=Class.forName(name); //创建类对象 Object obj=c.newInstance(); //获得方法 Method m=c.getMethod(method); //调用方法 m.invoke(obj); } }