反射是框架设计的灵魂
(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码),一切反射的操作都是从类对象开始)
一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
二、反射机制有什么用?
- 运行时动态获取类的信息:在编写代码时,对于类的信息是必须在编译时确定的,但在运行时,有时需要根据某些条件,动态获取某个类的信息,这时就可以使用Java中的反射机制。
- 动态生成对象:反射机制可以在运行时生成对象,这样就可以根据参数的不同,动态的创建不同的类的实例对象。
- 动态调用方法:通过反射机制可以调用类中的方法,不论这些方法是否是公共的,也不论这些方法的参数个数和类型是什么,反射机制都具有这样的能力。
- 动态修改属性:利用反射机制可以获取到类中的所有成员变量,并可以对其进行修改。
- 实现动态代理:利用反射机制可以实现代理模式,通过代理对象完成原对象对某些方法的调用,同时也可以在这些方法的调用前后做一些额外的处理。
三、Java反射机制的优点
- 增加灵活性和扩展性:使用反射机制可以在程序运行时动态加载、修改、创建、调用类和方法等,从而增加了程序的灵活性和可扩展性。
- 提高代码的通用性:通过反射机制可以动态的获取类信息,从而可以编写通用的代码,使得不同的类能够以相同的方式来处理。
- 规范代码结构:反射机制可以使代码结构清晰明了,减少了代码中的冗余部分。
- 实现框架和插件:反射机制在很多框架和插件中都有广泛的应用,比如Spring框架、JUnit测试框架等。
- 动态代理:反射机制的另一个重要应用是实现动态代理,可以在不修改原来代码的情况下,通过代理对象对原对象的方法进行增强。
四、反射机制相关的重要的类有哪些?
注:必须先获得Class才能获取Method、Constructor、Field。
Class类
Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
- 获得类相关的方法
- 获得类中属性相关的方法
- 获得类中构造器相关的方法
- 获得类中方法相关的方法
接下来,我们已Student实体为实例
package com.ctb.reflect; /** * 学生实体 * @author biao * */ public class Student { private String sid; private String sname; public Integer age; static{ System.out.println("加载进jvm中!"); } public Student() { super(); System.out.println("调用无参构造方法创建了一个学生对象"); } public Student(String sid) { super(); this.sid = sid; System.out.println("调用带一个参数的构造方法创建了一个学生对象"); } public Student(String sid, String sname) { super(); this.sid = sid; this.sname = sname; System.out.println("调用带二个参数的构造方法创建了一个学生对象"); } @SuppressWarnings("unused") private Student(Integer age) { System.out.println("调用Student类私有的构造方法创建一个学生对象"); this.age = age; } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public void hello() { System.out.println("你好!我是" + this.sname); } public void hello(String name) { System.out.println(name + "你好!我是" + this.sname); } @SuppressWarnings("unused") private Integer add(Integer a, Integer b) { return new Integer(a.intValue() + b.intValue()); } }
五、获取Class的三种方式
1.Class.forname--应用在jdbc数据库连接中
2.类实例.getclass()---通用增删改
3.类名.class--通用查询
实例:
package com.ctb.reflect; /** * 类类的获取方式 * 一切反射的操作都是从类对象开始 * @author biao * */ public class Demo1 { public static void main(String[] args) throws Exception { //1.Class.forname--应用在jdbc数据库连接中 Class<?> forName = Class.forName("com.ctb.reflect.Student"); System.out.println(forName); Student stu=new Student(); //2.类实例.getclass()---通用增删改 Class<? extends Student> class1 = stu.getClass(); System.out.println(class1); //3.类名.class--通用查询 Class<Student> class2 = Student.class; System.out.println(class2); } }
输出结果:
六、反射实例化
对象.newInstance()
注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
否则会抛出java.lang.InstantiationException
异常。
package com.ctb.reflect; import java.lang.reflect.Constructor; /** * 反射实例化 * 添加有参构造器,一定要补一个无参构造器 * @author biao * */ public class Demo2 { public static void main(String[] args) throws Exception { //一切反射从类类开始 Class<Student> c1=Student.class; Student stu=new Student(); //调用无参构造器反射实例化 Object o=c1.newInstance(); System.out.println(o); //调用1个参数的构造器反射实例化 Constructor<Student> ct = c1.getConstructor(String.class); Student student = ct.newInstance("5869"); System.out.println(student); //调用2个参数的构造器反射实例化 Constructor<Student> ct1 = c1.getConstructor(String.class,String.class); Student s2 = ct1.newInstance("66744", "小文子"); System.out.println(s2); //getConstructor方法只能获取到公有的构造器对象 //调用私有化的构造器反射实例化 Constructor<Student> ct2 = c1.getDeclaredConstructor(Integer.class); //打开私有修饰的访问权限 ct2.setAccessible(true); Student ss = ct2.newInstance(89); System.out.println(ss); } }
输出结果:
七、反射动态方法调用
Method类方法
实例:
package com.ctb.reflect; import java.lang.reflect.Method; /** * 反射动态方法调用 * @author biao * */ public class Demo3 { public static void main(String[] args) throws Exception { Student stu = new Student(); Class c = stu.getClass(); //反射的无参数方法调用 //getMethod(?,?)方法名,调用方法要传参数类型 Method m1 = c.getMethod("嘿嘿"); //invoke(?,?)类实例,参数值 Object invoke = m1.invoke(stu); System.out.println(invoke); //反射的有参数方法调用 Method m2 = c.getMethod("嘿嘿", String.class); System.out.println(m2.invoke(stu, "牛马")); //反射的私有化有参数方法调用 Method m3 = c.getDeclaredMethod("add", Integer.class, Integer.class); m3.setAccessible(true); System.out.println(m3.invoke(stu, 90, 10)); } }
输出结果:
八、反射读写属性
Field类方法
实例:
package com.ctb.reflect; import java.lang.reflect.Field; /** * 反射读写属性 * * @author biao * */ public class Demo4 { public static void main(String[] args) throws Exception { Student stu = new Student("5499", "酰基"); stu.age = 3040; Class clz = stu.getClass(); //获取stu实例对象中的所有属性值 //反射存值 Field sname = clz.getDeclaredField("sname"); sname.setAccessible(true); sname.set(stu, "犹记"); //反射获取属性值 Field[] fields = clz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); System.out.println(field.getName() + ":" + field.get(stu)); } } }
输出结果:
注:当对象中存入值后,再次调用set方法存值时会覆盖
九、Java反射机制应用场景
- 框架开发:许多流行的Java框架,比如Spring、Hibernate、Struts等,都使用了反射机制,以提供更灵活、可扩展的特性。
- 应用程序开发:反射机制常常用于某些需要动态加载或访问类信息的应用程序中,比如动态配置,插件管理等。
- 单元测试:JUnit测试框架中,反射机制被广泛应用,可以方便地创建测试对象和调用测试方法。
- 动态代理:反射机制可以实现动态代理,实现不改变原来代码的情况下,对原来对象的方法进行增强。
- JavaBean工具:JavaBean工具中,使用反射机制可以获取类的属性名、属性值、调用属性的setter和getter方法等信息,方便进行对象的序列化与反序列化操作。