J2EE基础之反射知识详解
前言
反射机制是现代编程语言实现高级特性和复杂应用的重要基础技术之一,绝大多数语言都提供反射机制支持,开发人员应该更好的掌握这一技术,从而更高效地实现复杂应用。所以今天我来分享一些有关反射的基础知识,根据下面思维导图来了解有关反射的知识点。
1. 什么是反射
1.1 概念:
在计算机科学中,反射(reflection)是指计算机程序在运行时动态地获取和操作自身的信息的一种机制。通过反射机制,程序可以查询类、方法及字段的信息,并在运行时实例化类和调用方法等操作。
在Java语言中,反射机制由java.lang.reflect包提供,可以通过Class类动态获取其他类的信息,包括类名、方法、成员变量等,以及在运行时创建对象、调用方法和修改变量等。反射机制往往与注解和动态代理等语言特性和设计模式一起使用,能够极大地提高Java应用程序的灵活性和可扩展性。
1.2 反射的作用:
- 动态加载类:反射机制可以根据类名加载对应的类,从而实现类动态加载,增加了程序的灵活性和可扩展性。
- 调用任意方法:反射机制可以通过方法名、参数类型和返回值类型等信息获取对应的方法引用,并通过invoke()方法调用,从而实现对任意方法的调用。
- 获取类信息:反射机制可以获取类名、继承关系、方法和字段等信息,从而可以更加灵活地处理程序逻辑。
- 创建对象:反射机制可以使用指定类的构造方法创建对象,从而解除了使用new关键字创建对象的限制。
- 修改私有变量和调用私有方法:反射机制可以使私有的类和方法可见,从而可以改变原有的类和方法的访问修饰符,实现对私有变量和私有方法的修改和调用。
**总结:**反射机制可以在运行时动态获取和操作程序的组件,增加了程序的灵活性和可扩展性,适用于框架、插件、动态代理和工具等应用场景。但是,反射机制也会带来一些性能损失和安全问题,需要根据具体场景合理使用。
1.3 反射的重要性:
- 框架和模板的实现:反射机制可以完成一些通用的任务(如类的实例化、方法的调用等),从而为框架和模板的实现提供关键技术支持。
- 插件和扩展的实现:反射机制可以动态加载代码和资源文件,从而实现插件和扩展的实现,增强程序的灵活性。
- 编译器和调试器的设计:反射机制可以将代码和程序结构作为数据进行处理和操作,从而支持编译器和调试器的设计和实现。
- 测试和调试的辅助工具:反射机制可以用来查看对象的属性和方法,检查代码是否符合预期行为,从而辅助测试和调试工作。
- 反射性能优化:反射机制的性能问题是反射机制应用的主要缺点,但是通过缓存反射对象、避免不必要的类型转换,以及运用其他优化策略,可以减小反射带来的性能影响。
总之,反射机制是现代编程语言实现高级特性和复杂应用的重要基础技术之一,绝大多数语言都提供反射机制支持,开发人员应该更好的掌握这一技术,从而更高效地实现复杂应用。
1.4 原理图解
2. 类类
实体类Student
package com.YX.reflex; /** * 实体类 * @author 86158 * */ 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()); } }
2.1 Class.forName(完整类名)
用途:
应用在数据库的jdbc数据库链接中
代码示例:
package com.YX.reflex; /** * 类类的三种获取方式:一切反射的操作都从获取类对象开始 * 1.Class.forName * 2.类实例化.getClass() * 3.类名.class * * @author 86158 * */ public class Demo1 { public static void main(String[] args) throws Exception { //实例化实体对象 Student stu=new Student(); // 获取类类的方式:Class.forName forName()参数是类的全路径名 Class c1 = Class.forName("com.YX.reflex.Student"); // 输出语句 System.out.println(c1); } }
输出结果:
2.2 类实例化.getClass()
用途:
通用于数据的增改
代码示例:
package com.YX.reflex; /** * 类类的三种获取方式:一切反射的操作都从获取类对象开始 * 1.Class.forName * 2.类实例化.getClass() * 3.类名.class * * @author 86158 * */ public class Demo1 { public static void main(String[] args) throws Exception { //实例化实体对象 Student stu=new Student(); // 2.获取类类的方式:类实例化.getClass() Class c2 = stu.getClass(); // 输出 System.out.println(c2); } }
输出结果:
2.3 类名.class
用途:
通用于数据的查询
代码示例
package com.YX.reflex; /** * 类类的三种获取方式:一切反射的操作都从获取类对象开始 * 1.Class.forName * 2.类实例化.getClass() * 3.类名.class * * @author 86158 * */ public class Demo1 { public static void main(String[] args) throws Exception { //实例化实体对象 Student stu=new Student(); // 3.获取类类的方式 Class c3=Student.class; // 输出 System.out.println(c3); } }
输出结果
3. 反射实例化
3.1 调用无参构造器反射实例化
如果我们将实体类中的无参构造器注释或者未添加,还去调用无参构造器反射实例化的话,控制台会报错误。具体操作如下
3.1.1无参构造器被注释的情况下:
实体类无参构造被注释
package com.YX.reflex; /** * 实体类 * @author 86158 * */ 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()); } }
测试类代码:
package com.YX.reflex; /** * 反射实例化 * @author 86158 *所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c=Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 Student s1 = (Student) c.newInstance(); // 输出 System.out.println(s1); } }
控制台结果
3.1.2无参构造器被注释的情况下:
实体类无参构造未被注释:
package com.YX.reflex; /** * 实体类 * @author 86158 * */ 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()); } }
测试类代码:
package com.YX.reflex; /** * 反射实例化 * @author 86158 *所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c=Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 Student s1 = (Student) c.newInstance(); // 输出 System.out.println(s1); } }
控制台输出结果
3.2 调用一个参数构造器的反射实例化
测试类代码
package com.YX.reflex; import java.lang.reflect.Constructor; /** * 反射实例化 * @author 86158 *所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c=Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 // 2.调用一个参数的构造器实例化 // getConstructor(parameterTypes) 参数parameterTypes:代表参数的类别 Constructor cr1 = c.getConstructor(String.class); Student s2 = (Student) cr1.newInstance("s001"); System.out.println(s2); } }
控制台输出结果
3.3 调用两个参数构造器的反射实例化
测试类代码
package com.YX.reflex; import java.lang.reflect.Constructor; /** * 反射实例化 * * @author 86158 所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c = Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 // 3. 调用两个参数的构造器实例化 Constructor cr2 = c.getConstructor(String.class, String.class); Student s3 = (Student) cr2.newInstance("s002", "nb"); System.out.println(s3); } }
控制台输出结果
3.4 调用私有的带一个参数的构造器的反射实例化
错误测试类代码
package com.YX.reflex; import java.lang.reflect.Constructor; /** * 反射实例化 * * @author 86158 所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c = Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 // 4.调用私有的带一个参数的构造器的反射实例化 // getConstructor()方法只能获取公有的构造器对象 Constructor cr3 = c.getDeclaredConstructor(Integer.class); cr3.newInstance(18); System.out.println(cr3); } }
控制台输出结果
Class com.YX.reflex.Demo2 can not access a member of class com.YX.reflex.Student with modifiers "private"代表的是该私有化的构造器不能被反射实例化。
正确的测试类代码
package com.YX.reflex; import java.lang.reflect.Constructor; /** * 反射实例化 * * @author 86158 所有实体类,添加了有参构造器,一定记得补一个无参构造器 */ public class Demo2 { public static void main(String[] args) throws Exception { // 一切的反射从获取类类开始 Class c = Student.class; // 反射实例化实体类 // 添加了有参构造器,一定记得补一个无参构造器 // 4.调用私有的带一个参数的构造器的反射实例化 // getConstructor()方法只能获取公有的构造器对象 Constructor cr3 = c.getDeclaredConstructor(Integer.class); // 打开私有修饰符的访问权限 cr3.setAccessible(true); Student s4 = (Student) cr3.newInstance(18); System.out.println(cr3); } }
控制台输出结果
3.5 反射实例化的方法归纳
方法名 | 方法说明 |
newInstance() | 可用于创建任意类的实例,并调用其相应的构造函数。将使用默认的无参构造函数并创建一个新的MyClass 实例。 |
getConstructor() | 获取特定参数类型的构造函数 |
setAccessible() | 它允许我们在访问非公共成员时取消Java语言中的访问限制。该方法主要用于访问私有成员、受保护成员和默认访问级别的成员。 |
getDeclaredConstructor() | 是一个可以用来获取指定类的构造函数(包括私有构造函数)的方法。 |
getDeclaredConstructors() | 可以用来获取指定类的所有构造函数(包括私有构造函数)。 |
4. 反射动态方法调用
4.1 反射动态方法
方法名 | 方法说明 |
getMethod() | 用于获取指定类的公共方法 |
getDeclaredField() | 用于获取特定类的私有字段。 |
invoke() | 用于调用特定对象的方法。 |
4.2 反射调用无参方法
测试类代码
package com.YX.reflex; import java.lang.reflect.Method; /** * 反射动态方法调用 * @author 86158 * */ public class Demo3 { public static void main(String[] args) throws Exception { //获取类类 Class c=Student.class; // 反射实例化实体对象 Student stu=(Student) c.newInstance(); // 1.反射调用无参方法 先获取到方法对象 // getMethod方法中参数name:方法名;parameterTypes:调用这个方法要传的参数类型 Method m1 = c.getMethod("hello"); // obj:类实例;args:参数值 Object invoke = m1.invoke(stu); // 打印 System.out.println(invoke); } }
控制台输出结果
4.3 反射调用有参方法
测试类代码
package com.YX.reflex; import java.lang.reflect.Method; /** * 反射动态方法调用 * @author 86158 * */ public class Demo3 { public static void main(String[] args) throws Exception { //获取类类 Class c=Student.class; // 反射实例化实体对象 Student stu=(Student) c.newInstance(); // 2.反射调用有参方法 Method m2=c.getMethod("hello", String.class); // 调用指定类的方法 Object invoke2 = m2.invoke(stu, "君易"); System.out.println(invoke2); } }
控制台输出结果
4.4 反射调用私有有参方法
测试类代码
package com.YX.reflex; import java.lang.reflect.Method; /** * 反射动态方法调用 * @author 86158 * */ public class Demo3 { public static void main(String[] args) throws Exception { //获取类类 Class c=Student.class; // 反射实例化实体对象 Student stu=(Student) c.newInstance(); // 3. 反射调用有参私有方法 Method m3=c.getDeclaredMethod("add", Integer.class,Integer.class); // 获取访问权限 m3.setAccessible(true); // 调用指定方法 Object invoke3 = m3.invoke(stu, 1,3); // 打印 System.out.println(invoke3); } }
控制台输出结果
5. 反射读写属性
5.1 反射读写方法
方法 | 方法说明 |
getDeclaredField | 用于获取指定类的字段(包括私有字段) |
getDeclaredFields | 用于获取指定类声明的所有字段(包括私有字段) |
setAccessible() | 将私有字段设置为可访问 |
5.2 反射读取属性值
测试类代码
package com.YX.reflex; import java.lang.reflect.Field; /** * 反射读写属性方法 * * @author 86158 * */ public class Demo4 { public static void main(String[] args) throws Exception { //获取类类 Class c=Student.class; // 实例化实体类 Student stu=new Student("s001", "nb"); stu.age=18; // 获取学生实体的所有信息 Field[] fields = c.getDeclaredFields(); for (Field f : fields) { // 允许私有化访问 f.setAccessible(true); //打印 System.out.println(f.getName()+":"+f.get(stu)); } } }
控制输出结果
5.3 反射修改属性值
修改前的控制台输出结果
测试类方法
package com.YX.reflex; import java.lang.reflect.Field; /** * 反射读写属性方法 * * @author 86158 * */ public class Demo4 { public static void main(String[] args) throws Exception { //获取类类 Class c=Student.class; // 实例化实体类 Student stu=new Student("s001", "nb"); stu.age=18; // 获取单个实体对象的属性值 Field SnameField = c.getDeclaredField("sname"); // 打开权限 SnameField.setAccessible(true); // 修改属性值 SnameField.set(stu, "木易"); // 获取学生实体的所有信息 Field[] fields = c.getDeclaredFields(); for (Field f : fields) { // 允许私有化访问 f.setAccessible(true); System.out.println(f.getName()+":"+f.get(stu)); } } }
修改后的控制台输出结果