一、概念
J2EE中的反射(Reflection)是指在程序运行期间访问、检测和修改自身的状态或行为。通过反射,可以动态地获取一个类的所有成员变量、方法、构造函数等信息,并且可以在运行时调用这些成员。在J2EE中,反射可以方便地实现一些框架、ORM工具,以及其他动态性能要求的应用程序。
什么是反射?
反射(Reflection)是一种在运行时动态地获取和操作对象的能力,它允许程序通过对象的类类型来了解对象的成员和方法,并且可以在运行时调用这些成员和方法。反射可以用于许多目的,例如在运行时动态加载类、实例化对象、查看和修改对象的属性和方法、调用私有方法和构造函数,等等。在Java中,反射通过java.lang.reflect包中的类和接口来实现。反射的优点是可以使我们的代码更加灵活和动态,但是反射的开销比较大,因此在性能要求较高的情况下,应该谨慎使用。
总的来说:反射Java语言中的一种机制,通过这种机制可以动态的实例化对象、读写属性、调用方法
二、类类
在Java反射(reflection)中,Class类被称为“类类”(class of class),因为它代表了JVM中的一个类。每个Java类在内存中都有一个对应的Class类实例对象,可以通过Java的反射机制获取。通过这个实例,我们可以动态地获取、创建、操作一个类的各种元数据(如属性、方法、注解等),并可以在程序运行时动态地创建、修改该类的对象。通过反射,我们可以实现更加灵活和动态的编程方式,可以在编码阶段不确定访问的类和方法,直到运行时才确定需要获取和调用的类和方法。
1、类类的获取方式
1. Object类中的getClass()方法:可以获取一个对象所属的类。
2. 类名.class:直接通过类名这种语法形式获取一个类的Class实例对象。
3. Class.forName(String className):通过类的全限定名获取Class实例对象,需要处理ClassNotFoundException异常。
这些方式均可以获得一个Java类的Class实例对象,从而可以利用反射机制获取该类的一系列信息,如属性、方法、构造方法、注解等。
1)类名.Class
在开始示例的前期准备工作我们写一个类(Class)
package com.tgq.reflect; /** * 一个类 * * @author tgq * */ 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.tgq.reflect; /** * 类类的获取方式: 1、类名.Class 2、对象.getClass(); 3、Class.forName(); * * @author tgq * */ public class Demo_01 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 1、类名.Class Class c1 = Student.class; System.out.println(c1); } }
输出结果:
编辑
2)对象.getClass()
package com.tgq.reflect; /** * 类类的获取方式: 1、类名.Class 2、对象.getClass(); 3、Class.forName(); * * @author tgq * */ public class Demo_01 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 1、类名.Class // Class c1 = Student.class; // System.out.println(c1); // 2、对象.getClass(); Student student = new Student(); Class c2 = student.getClass(); System.out.println(c2); } }
输出结果:
编辑
3)Class.forName()
package com.tgq.reflect; /** * 类类的获取方式: 1、类名.Class 2、对象.getClass(); 3、Class.forName(); * * @author tgq * */ public class Demo_01 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 1、类名.Class // Class c1 = Student.class; // System.out.println(c1); // 2、对象.getClass(); // Student student = new Student(); // Class c2 = student.getClass(); // System.out.println(c2); // 3、Class.forName(); Class c3 = Class.forName("com.tgq.reflect.Student"); System.out.println(c3); } }
输出结果:
编辑
三、反射实例化
在Java中,可以使用反射来实例化一个类。具体实现可以通过Class类的newInstance()方法或者Constructor类的newInstance()方法来实现。
三个常用的方法:getConstructor (),getDeclaredConstructor () ,newInstance() 。
一切的反射从类类开始
1、调用一个公有的无参构造方法
public class Demo_02 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 一切反射从类类开始 // 不管是哪一种获取类类的方法都是可以的 // Class c1 = Class.forName("com.tgq.reflect.Student"); // Student student = new Student(); // Class c1 = student.getClass(); Class c1 = Student.class; /* * 1、调用一个无参构造器反射实例化: 如果没有无参构造器是会报错的 */ Student stu1 = (Student) c1.newInstance(); System.out.println(stu1); } }
输出结果:
编辑
2、调用一个公有的一个参构造方法
public class Demo_02 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 一切反射从类类开始 // 不管是哪一种获取类类的方法都是可以的 // Class c1 = Class.forName("com.tgq.reflect.Student"); // Student student = new Student(); // Class c1 = student.getClass(); Class c1 = Student.class; /* * 1、调用一个无参构造器反射实例化: 如果没有无参构造器是会报错的 */ Student stu1 = (Student) c1.newInstance(); System.out.println(stu1); /* * 调用有参构造器必须要实例化构造器对象 2、调用一个有一个参数的构造器:getConstructor == 获取构造器对象 * * parameterTypes:代表了参数的类别Class */ Constructor con1 = c1.getConstructor(String.class); Student stu2 = (Student) con1.newInstance("s1"); System.out.println(stu2); } }
输出结果:
编辑
3、调用一个公有的两个参构造方法
public class Demo_02 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 一切反射从类类开始 // 不管是哪一种获取类类的方法都是可以的 // Class c1 = Class.forName("com.tgq.reflect.Student"); // Student student = new Student(); // Class c1 = student.getClass(); Class c1 = Student.class; /* * 1、调用一个无参构造器反射实例化: 如果没有无参构造器是会报错的 */ Student stu1 = (Student) c1.newInstance(); System.out.println(stu1); /* * 调用有参构造器必须要实例化构造器对象 2、调用一个有一个参数的构造器:getConstructor == 获取构造器对象 * * parameterTypes:代表了参数的类别Class */ Constructor con1 = c1.getConstructor(String.class); Student stu2 = (Student) con1.newInstance("s1"); System.out.println(stu2); /* * 3、调用两个参数的构造器实例化 */ Constructor con2 = c1.getConstructor(String.class, String.class); Student stu3 = (Student) con2.newInstance("s1", "sb"); System.out.println(stu3); } }
输出结果:
编辑
4、调用一个私有的一个参构造方法
public class Demo_02 { @SuppressWarnings("rawtypes") public static void main(String[] args) throws Exception { // 一切反射从类类开始 // 不管是哪一种获取类类的方法都是可以的 // Class c1 = Class.forName("com.tgq.reflect.Student"); // Student student = new Student(); // Class c1 = student.getClass(); Class c1 = Student.class; /* * 1、调用一个无参构造器反射实例化: 如果没有无参构造器是会报错的 */ Student stu1 = (Student) c1.newInstance(); System.out.println(stu1); /* * 调用有参构造器必须要实例化构造器对象 2、调用一个有一个参数的构造器:getConstructor == 获取构造器对象 * * parameterTypes:代表了参数的类别Class */ Constructor con1 = c1.getConstructor(String.class); Student stu2 = (Student) con1.newInstance("s1"); System.out.println(stu2); /* * 3、调用两个参数的构造器实例化 */ Constructor con2 = c1.getConstructor(String.class, String.class); Student stu3 = (Student) con2.newInstance("s1", "sb"); System.out.println(stu3); /* * 4、调用一个私有的带一个参数的构造器实例化: getConstructor只能获取公有的构造器对象 * * getDeclaredConstructor:获取私有的构造器对象 */ Constructor con3 = c1.getDeclaredConstructor(Integer.class); // 打开私有的访问权限 con3.setAccessible(true); Student stu4 = (Student) con3.newInstance(1); System.out.println(stu4); } }
输出结果:
编辑
四、反射:方法调用
1、公有无参
public class Demo_03 { public static void main(String[] args) throws Exception { Class c = Student.class; // 1、反射调用 无参方法 Student s = (Student) c.newInstance(); // 反射调用无参方法,先获取到方法对象 // name:方法名 // parameterTypes:调用这个方法要传的参数类型 Method m1 = c.getMethod("hello"); // invoke:方法的返回值 // obj:类示例 // args:参数值 Object invoke = m1.invoke(s); System.out.println(invoke); } }
输出结果:
编辑
2、公有有参
public class Demo_03 { public static void main(String[] args) throws Exception { Class c = Student.class; // 1、反射调用 无参方法 Student s = (Student) c.newInstance(); // 反射调用无参方法,先获取到方法对象 // name:方法名 // parameterTypes:调用这个方法要传的参数类型 Method m1 = c.getMethod("hello"); // invoke:方法的返回值 // obj:类示例 // args:参数值 Object invoke = m1.invoke(s); System.out.println(invoke); // 2、调用有参方法 Method m2 = c.getMethod("hello", String.class); Object invoke2 = m2.invoke(s, "asdfghjk"); System.out.println(invoke2); } }
输出结果:
编辑
3、私有有参
public class Demo_03 { public static void main(String[] args) throws Exception { Class c = Student.class; // 1、反射调用 无参方法 Student s = (Student) c.newInstance(); // 反射调用无参方法,先获取到方法对象 // name:方法名 // parameterTypes:调用这个方法要传的参数类型 Method m1 = c.getMethod("hello"); // invoke:方法的返回值 // obj:类示例 // args:参数值 Object invoke = m1.invoke(s); System.out.println(invoke); // 2、调用有参方法 Method m2 = c.getMethod("hello", String.class); Object invoke2 = m2.invoke(s, "asdfghjk"); System.out.println(invoke2); // 3、调用有参私有的方法 Method m3 = c.getDeclaredMethod("add", Integer.class, Integer.class); // 打开私有权限 m3.setAccessible(true); Object invoke3 = m3.invoke(s, 1, 2); System.out.println(invoke3); } }
输出结果:
编辑
完整代码:
package com.tgq.reflect; import java.lang.reflect.Method; /** * 反射动态方法的调用 * * @author tgq * */ public class Demo_03 { public static void main(String[] args) throws Exception { Class c = Student.class; // 1、反射调用 无参方法 Student s = (Student) c.newInstance(); // 反射调用无参方法,先获取到方法对象 // name:方法名 // parameterTypes:调用这个方法要传的参数类型 Method m1 = c.getMethod("hello"); // invoke:方法的返回值 // obj:类示例 // args:参数值 Object invoke = m1.invoke(s); System.out.println(invoke); // 2、调用有参方法 Method m2 = c.getMethod("hello", String.class); Object invoke2 = m2.invoke(s, "asdfghjk"); System.out.println(invoke2); // 3、调用有参私有的方法 Method m3 = c.getDeclaredMethod("add", Integer.class, Integer.class); // 打开私有权限 m3.setAccessible(true); Object invoke3 = m3.invoke(s, 1, 2); System.out.println(invoke3); } }
五、反射:读写属性
通过Field类的get()和set()方法来实现。
拿到我们所有的属性:
public class Demo_04 { public static void main(String[] args) throws Exception { Class c = Student.class; Student stu = new Student("1", "1A"); // 我们把age设置一个值 stu.age = 18; // System.out.println(stu.getSid()); // 获取对象中全部的属性 Field[] fields = c.getDeclaredFields(); // 遍历 for (Field f : fields) { // 因为sid、sname是私有的,所以我们要打开私有权限 f.setAccessible(true); // 获取所有属性 System.out.println(f.getName() + ":" + f.get(stu)); } } }
输出结果:
编辑
我们进行设置属性值:
public class Demo_04 { public static void main(String[] args) throws Exception { Class c = Student.class; Student stu = new Student("1", "1A"); // 我们把age设置一个值 stu.age = 18; // System.out.println(stu.getSid()); // 拿到属性 Field snamefield = c.getDeclaredField("sname"); // 打开权限 snamefield.setAccessible(true); // 进行设置 snamefield.set(stu, "2B"); // 获取对象中全部的属性 Field[] fields = c.getDeclaredFields(); // 遍历 for (Field f : fields) { // 因为sid、sname是私有的,所以我们要打开私有权限 f.setAccessible(true); // 获取所有属性 System.out.println(f.getName() + ":" + f.get(stu)); } } }
输出结果:
编辑
完整代码:
package com.tgq.reflect; import java.lang.reflect.Field; /** * 反射:读写属性 * * @author tgq * */ public class Demo_04 { public static void main(String[] args) throws Exception { Class c = Student.class; Student stu = new Student("1", "1A"); // 我们把age设置一个值 stu.age = 18; // System.out.println(stu.getSid()); // 拿到属性 Field snamefield = c.getDeclaredField("sname"); // 打开权限 snamefield.setAccessible(true); // 进行设置 snamefield.set(stu, "2B"); // 获取对象中全部的属性 Field[] fields = c.getDeclaredFields(); // 遍历 for (Field f : fields) { // 因为sid、sname是私有的,所以我们要打开私有权限 f.setAccessible(true); // 获取所有属性 System.out.println(f.getName() + ":" + f.get(stu)); } } }