1:什么是反射机制?反射机制的应用场景有哪些?
反射机制介绍JAVA 反射机制是在运行状态中, 对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。 静态编译和动态编译 静态编译:在编译时确定类型,绑定对象 动态编译:运行时确定类型,绑定对象 反射机制优缺点优点: 运行期类型的判断,动态加载类,提高代码灵活度。 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。 反射是框架设计的灵魂 在我们平时的项目开发过程中,基本上很少会直接使用到反射机制, 但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关; 1: 我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序; 2:Spring 框架也用到很多反射机制,最经典的就是 xml 的配置模式。
2:反射的作用
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码) 反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有: 构造方法、成员变量、成员方法、包等等信息, 利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。 (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 主要分为以下: 1:获取Class对象的方法 2:获取构造方法 3:获取成员变量 4:获取成员方法 5:通过反射运行配置文件内容 6:通过反射越过泛型检查
3:反射的加载 与 Class类的描述
如图是类的正常加载过程:反射的原理在与class对象。 加载的时候:将class文件读入内存,并为之创建一个Class对象。 Class 类的实例表示正在运行的 Java 应用程序中的类和接口。 也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。 Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。 也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。


4:获取Class对象的三种方式
在运行期间,一个类,只有一个Class对象产生。三种方式常用第三种: 第一种:对象都有了还要反射干什么。 第二种:需要导入类的包,依赖太强,不导包就抛编译错误。 第三种:一个字符串可以传入也可写在配置文件中等多种方法。
/** * 获取Class对象的三种方式 * 1 Object ——> getClass(); * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性 * 3 通过Class类的静态方法:forName(String className)(常用) * */public class ClassDemo { public static void main(String[] args) { //第一种方式获取Class对象 //这一new 产生一个Student对象,一个Class对象。 Student stu1 = new Student(); //获取Class对象 Class stuClass1 = stu1.getClass(); System.out.println(stuClass1.getName()); //第二种方式获取Class对象 Class stuClass2 = Student.class; //判断第一种方式获取的Class对象和第二种方式获取的是同一个 System.out.println(stuClass1 == stuClass2); //第三种方式获取Class对象 try { //注意此字符串必须是真实路径,就是带包名的类路径,包名.类名 Class stuClass3 = Class.forName("base.model.entity.Student"); //判断三种方式获取的是同一个Class对象 System.out.println(stuClass3 == stuClass2); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}
5:获取Class对象的构造方法 并 调用
package base.model.entity ;public class Student { //---------------构造方法------------------- //无参构造方法 public Student(){ System.out.println("调用了公有、无参构造方法执行了。。。"); } //私有构造方法 private Student(int age){ System.out.println("私有的构造方法 年龄:"+ age); } //(默认的构造方法) Student(String str){ System.out.println("(默认)的构造方法 s = " + str); } //受保护的构造方法 protected Student(boolean n){ System.out.println("受保护的构造方法 n = " + n); } //有一个参数的构造方法 public Student(char name){ System.out.println("姓名:" + name); } //有多个参数的构造方法 public Student(String name ,Integer age){ System.out.println("姓名:"+name+"年龄:"+ age); }}
import java.lang.reflect.Constructor; /* * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员; * * 1.获取构造方法: * 1).批量的方法: * 所有"公有的"构造方法 * public Constructor[] getConstructors() * 获取所有的构造方法(包括私有、受保护、默认、公有) * public Constructor[] getDeclaredConstructors() * * 2).获取单个的方法,并调用: * 获取单个的"公有的"构造方法: * public Constructor getConstructor(Class... parameterTypes) * 获取某个构造方法(私有的,或受保护、默认、公有) * public Constructor getDeclaredConstructor(Class... parameterTypes): * * 调用构造方法: * Constructor-->newInstance(Object... initargs) */public class Constructors { public static void main(String[] args) throws Exception { //1.加载Class对象 Class clazz = Class.forName("base.model.entity.Student"); //2.获取所有公有构造方法 Constructor[] pubConArray = clazz.getConstructors(); for(Constructor c : pubConArray){ System.out.println(c); } //3.获取所有的构造方法(包括私有、受保护、默认、公有) Constructor[] allConArray = clazz.getDeclaredConstructors(); for(Constructor c : allConArray){ System.out.println(c); } //获取公有、无参的构造方法 //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型 //2>、返回的是描述这个无参构造函数的类对象。 Constructor con = clazz.getConstructor(null); System.out.println("con = " + con); //调用构造方法 Object obj = con.newInstance(); System.out.println("******************获取 某个构造方法 并调用*******************************"); con = clazz.getDeclaredConstructor(char.class); System.out.println("con = " +con); //调用构造方法 //暴力访问(忽略掉访问修饰符) con.setAccessible(true); obj = con.newInstance('hesuijin'); } }//控制台打印结果**********************所有公有构造方法*********************************public base.model.entity.Student()public base.model.entity.Student(char)public base.model.entity.Student(java.lang.String,java.lang.Integer)************所有的构造方法(包括:私有、默认、受保护、公有)***************public base.model.entity.Student()private base.model.entity.Student(int) base.model.entity.Student(java.lang.String)protected base.model.entity.Student(booleanpublic base.model.entity.Student(char))public base.model.entity.Student(java.lang.String,int)*****************获取公有、无参的构造方法*******************************con = public base.model.entity.Student()调用了公有、无参构造方法执行了。。。******************获取私有构造方法,并调用*******************************con = public base.model.entity.Student(char)姓名:hesuijin
6:获取成员变量并调用
package base.model.field;@Datapublic class Student { public Student(){ } //**********字段*************// public String name; protected int age; char sex; private String phone;}import java.lang.reflect.Field;/* * 获取成员变量并调用: * * 1.批量的 * 获取所有的"公有字段" * 1).Field[] getFields() * 获取所有字段,包括:私有、受保护、默认、公有; * 2).Field[] getDeclaredFields() * * 2.获取单个的: * 1).public Field getField(String fieldName) * 2).public Field getDeclaredField(String fieldName) * * 设置字段的值: * Field --> public void set(Object obj,Object value): * 参数说明: * 1.obj:要设置的字段所在的对象; * 2.value:要为字段设置的值; * */public class Fields { public static void main(String[] args) throws Exception { //1.获取Class对象 Class stuClass = Class.forName("base.model.field.Student"); //2.获取字段 System.out.println("************获取所有公有的字段********************"); Field[] fieldArray = stuClass.getFields(); for(Field f : fieldArray){ System.out.println(f); } System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************"); fieldArray = stuClass.getDeclaredFields(); for(Field f : fieldArray){ System.out.println(f); } //获取一个对象 //产生Student对象--》Student stu = new Student(); Object obj = stuClass.getConstructor().newInstance(); System.out.println("*************获取公有字段**并调用***********************************"); Field f = stuClass.getField("name"); System.out.println(f); //为字段设置值 //为Student对象中的name属性赋值--》stu.name = "刘德华" f.set(obj, "刘德华"); //验证 Student stu = (Student)obj; System.out.println("验证姓名:" + stu.name); System.out.println("**************获取私有字段****并调用********************************"); f = stuClass.getDeclaredField("phone"); System.out.println(f); //暴力反射,解除私有限定 f.setAccessible(true); f.set(obj, "18718747777"); System.out.println("验证电话:" + stu.phone); } }控制台输出*************获取公有字段**并调用***********************************public java.lang.String base.model.field.Student.name验证姓名:刘德华**************获取私有字段****并调用********************************private java.lang.String base.model.field.Student.phone验证电话:18718747777
7:获取成员方法并调用
package base.model.method ;public class Student { //**************成员方法***************// public void show1(String s){ System.out.println("调用了:公有的,String参数的show1(): s = " + s); } protected void show2(){ System.out.println("调用了:受保护的,无参的show2()"); } void show3(){ System.out.println("调用了:默认的,无参的show3()"); } private String show4(int age){ System.out.println("调用了:私有的,并且有返回值的,int参数的show4(): age = " + age); return "我是返回值"; }}import java.lang.reflect.Method;/* * 获取成员方法并调用: * 1.批量的: * 获取所有"公有方法";(包含了父类的方法也包含Object类) * public Method[] getMethods() * 获取所有的成员方法,包括私有的(私有、受保护、默认、公有)(不包括继承的) * public Method[] getDeclaredMethods() * * 2.获取单个的: * public Method getMethod(String name,Class<?>... parameterTypes): * 参数: * name : 方法名; * Class ... : 形参的Class类型对象 * public Method getDeclaredMethod(String name,Class<?>... parameterTypes) * * 调用方法: * Method --> public Object invoke(Object obj,Object... args): * 参数说明: * obj : 要调用方法的对象; * args:调用方式时所传递的实参;): */public class MethodDemo { public static void main(String[] args) throws Exception { //1.获取Class对象 Class stuClass = Class.forName("base.model.method.Student"); //2.获取所有公有方法 System.out.println("***************获取所有的”公有“方法*******************"); stuClass.getMethods(); Method[] methodArray = stuClass.getMethods(); for(Method m : methodArray){ System.out.println(m); } System.out.println("***************获取所有的方法,包括私有的*******************"); methodArray = stuClass.getDeclaredMethods(); for(Method m : methodArray){ System.out.println(m); } //实例化一个Student对象 Object obj = stuClass.getConstructor().newInstance(); Method mehtod; System.out.println("***************获取公有的show1()方法*******************"); m = stuClass.getMethod("show1", String.class); //需要两个参数,一个是要调用的对象(获取有反射),一个是实参 System.out.println(m); m.invoke(obj, "刘德华"); System.out.println("***************获取私有的show4()方法******************"); m = stuClass.getDeclaredMethod("show4", int.class); System.out.println(m); //暴力反射,解除私有限定 m.setAccessible(true); //需要两个参数,一个是要调用的对象(获取有反射),一个是实参 Object result = m.invoke(obj, 20); System.out.println("返回值:" + result); }}控制台输出***************获取公有的show1()方法*******************public void fanshe.method.Student.show1(java.lang.String)调用了:公有的,String参数的show1(): s = 刘德华***************获取私有的show4()方法******************private java.lang.String fanshe.method.Student.show4(int)调用了,私有的,并且有返回值的,int参数的show4(): age = 20返回值:我是返回值
8:反射方法的其它使用之—-通过反射运行配置文件内容
public class Student { public void show(){ System.out.println("is show()"); }}//配置文件以txt文件为例子(properties.txt):className = base.model.property.StudentmethodName = show/* * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改 * 我们只需要将新类发送给客户端,并修改配置文件即可 */public class Demo { public static void main(String[] args) throws Exception { //1:通过反射获取Class对象 //"base.model.property.Student" Class stuClass = Class.forName(getValue("className")); //2:获取show()方法 //show Method m = stuClass.getMethod(getValue("methodName")); //3:调用show()方法 m.invoke(stuClass.getConstructor().newInstance()); } //此方法接收一个key,在配置文件中获取相应的value public static String getValue(String key) throws IOException{ //获取配置文件的对象 Properties pro = new Properties(); //获取输入流 FileReader fr = new FileReader("properties.txt"); //将流加载到配置文件对象中 pro.load(fr); fr.close(); //返回根据key获取的value值 return pro.getProperty(key); }}
9:反射方法的其它使用之—-通过反射越过泛型检查
/* * 通过反射越过泛型检查 * * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值? */public class Demo { public static void main(String[] args) throws Exception{ ArrayList<String> strList = new ArrayList<>(); strList.add("string"); // strList.add(100); //获取ArrayList的Class对象,反向的调用add()方法,添加数据 Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象 //获取add()方法 Method m = listClass.getMethod("add", Object.class); //调用add()方法 m.invoke(strList, 100); //遍历集合 for(Object obj : strList){ System.out.println(obj); } }}