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的多态性。在我们调用方法时它仍会遵循多态原则。