Java反射机制是Java语言的一个重要特性,在实际生产生活中,Java反射都得到了广泛的应用。例如,在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法;此外,在ORM中间件的实现中,运用Java反射机制可以读取任意一个JavaBean的所有属性,或者给这些属性赋值,这都是Java反射的实际应用。
Java反射机制主要提供了以下功能:
(1)在运行时判断任意一个对象所属的类
(2)在运行时构造任意一个类的对象
(3)在运行时判断任意一个类所具有的成员变量和方法
(4)在运行时调用任意一个对象的方法
(5)生成动态代理
要想实现上述功能,必须要先能获取到一个类的Class对象,以String类为例,一般有三种方法可以获取String类的Class类对象
(1)通过String类的对象调用对象的getClass()方法
- String str = "";
- Class clazz = str.getClass();
(2)通过String类.class来获取Class对象
Class clazz = String.class;
(3)通过Class类的静态方法forName()获取,方法内传入String类的完全限定名
- try {
- Class clazz = Class.forName("Java.lang.String");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
得到了Class对象后我们可以使用Java反射技术的一些功能,例如,获取该类的构造方法、继承关系、访问字段、调用方法等等
(1)首先看如何来得到该类的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息。
- public class Main {
- public static void main(String[] args) throws Exception {
- // 获取构造方法Integer(int):
- Constructor cons1 = Integer.class.getConstructor(int.class);
-
- // 调用构造方法:
- Integer n1 = (Integer) cons1.newInstance(123);
- System.out.println(n1);
-
- // 获取构造方法Integer(String)
- Constructor cons2 = Integer.class.getConstructor(String.class);
- Integer n2 = (Integer) cons2.newInstance("456");
- System.out.println(n2);
- }
- }
(2)再看如何获取某个类的继承关系
- public class Main {
- public static void main(String[] args) throws Exception {
- Class i = Integer.class;
- Class n = i.getSuperclass();
- System.out.println(n);
-
- Class o = n.getSuperclass();
- System.out.println(o);
- System.out.println(o.getSuperclass());
- }
- }
也可以获取这个类继承的接口
- public class Main {
- public static void main(String[] args) throws Exception {
- Class s = Integer.class;
- Class[] is = s.getInterfaces();
- for (Class i : is) {
- System.out.println(i);
- }
- }
- }
如果一个类没有继承任何接口,那么调用getInterfaces()会得到一个空数组
(3)同样的,要想获取到一个类的字段,也可以使用下面这些方法
- public class Main {
- public static void main(String[] args) throws Exception {
- Class stdClass = Student.class;
-
- // 获取public字段"score":
- System.out.println(stdClass.getField("score"));
-
- // 获取继承的public字段"name":
- System.out.println(stdClass.getField("name"));
-
- // 获取private字段"grade":
- System.out.println(stdClass.getDeclaredField("grade"));
- }
- }
-
- class Student extends Person {
- public int score;
- private int grade;
- }
-
- class Person {
- public String name;
- }
输出结果
- public int Student.score
- public java.lang.String Person.name
- private int Student.grade
当我们拿到一个field字段实例时,我们还可以拿到一个实例对应的该字段的值,例如,对于一个Person实例,我们可以先拿到name字段对应的Field,再获取这个实例的name字段值
- import java.lang.reflect.Field;
-
- public class Main {
-
- public static void main(String[] args) throws Exception {
- Object p = new Person("贝吉塔");
-
- Class c = p.getClass();
-
- Field f = c.getDeclaredField("name");
-
- Object value = f.get(p);
-
- System.out.println(value); // "贝吉塔"
- }
- }
-
- class Person {
- private String name;
-
- public Person(String name) {
- this.name = name;
- }
- }
既然通过Field实例可以拿到指定实例的字段值,那么也可以设置字段的值,设置字段的值通过Field.set(Object,Object)实现的,其中第一个Object参数是指定的实例,第二个参数是待修改的值。
- import java.lang.reflect.Field;
-
- public class Main {
-
- public static void main(String[] args) throws Exception {
- Person p = new Person("Xiao Ming");
- System.out.println(p.getName()); // "Xiao Ming"
-
- Class c = p.getClass();
- Field f = c.getDeclaredField("name");
- f.setAccessible(true);
- f.set(p, "Xiao Hong");
-
- System.out.println(p.getName()); // "Xiao Hong"
- }
- }
-
- class Person {
- private String name;
-
- public Person(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
- }
(4)我们既然能通过Class实例获取所有的Field对象,同样的,也就可以通过Class实例来获取所有方法(Method类型的对象)
- public class Main {
- public static void main(String[] args) throws Exception {
- Class stdClass = Student.class;
-
- // 获取public方法getScore,参数为String:
- System.out.println(stdClass.getMethod("getScore", String.class));
-
- // 获取继承的public方法getName,无参数:
- System.out.println(stdClass.getMethod("getName"));
-
- // 获取private方法getGrade,参数为int:
- System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
- }
- }
-
- class Student extends Person {
- public int getScore(String type) {
- return 99;
- }
- private int getGrade(int year) {
- return 1;
- }
- }
-
- class Person {
- public String getName() {
- return "Person";
- }
- }
输出结果
- public int Student.getScore(java.lang.String)
- public java.lang.String Person.getName()
- private int Student.getGrade(int)
既然我们可以通过反射调用Field对象,那么同样的我们也可以通过反射来调用Method类的对象,即方法。如果我们来调用String类的substring()方法,代码如下
- public class Main {
- public static void main(String[] args) throws Exception {
- // String对象:
- String s = "Hello world";
-
- // 获取String substring(int)方法,参数为int:
- Method m = String.class.getMethod("substring", int.class);
-
- // 在s对象上调用该方法并获取结果:
- String r = (String) m.invoke(s, 6);
-
- // 打印调用结果:
- System.out.println(r);
- }
- }
同样的我们也可以调用非public方法以及静态方法
调用非public方法代码如下:
- public class Main {
- public static void main(String[] args) throws Exception {
- Person p = new Person();
- Method m = p.getClass().getDeclaredMethod("setName", String.class);
- m.setAccessible(true);
- m.invoke(p, "Bob");
- System.out.println(p.name);
- }
- }
-
- class Person {
- String name;
- private void setName(String name) {
- this.name = name;
- }
- }
调用静态方法代码如下:
- public class Main {
- public static void main(String[] args) throws Exception {
- // 获取Integer.parseInt(String)方法,参数为String:
- Method m = Integer.class.getMethod("parseInt", String.class);
-
- // 调用该静态方法并获取结果:
- Integer n = (Integer) m.invoke(null, "12345");
-
- // 打印调用结果:
- System.out.println(n);
- }
- }
Java的反射虽然可以对于一些功能可以进行很好的展开,但是,反射它违反了Java三大特性中的封装性,但是不会影响Java的多态性。在我们调用方法时它仍会遵循多态原则。