Java大联盟
致力于最高效的Java学习
什么是反射?
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
如何理解反射?
简单来讲反射就是将传统的开发方式进行反转,传统的方式是通过类创建对象,反射则是通过对象获取类的内部结构,当然也可以通过其他方式来获取类的内部结构。
通过对象得到类,如何表示?
我们知道 Java 是面向对象的编程语言,世间万物都可以抽象成对象,反射也不例外,即通过反射机制获取的类的结构也可以抽象成一个对象,如何来创建这个对象呢?对象一定是通过类来创建的,这个类就是 Class 类,所以我们说 Class 类是反射的源头,Class 类的每一个实例化对象都表示某个类的结构。
如何获取 Class 实例化对象?
不能在外部通过构造函数创建 Class 的实例化对象,这是因为 Class 类的构造函数是私有的,如下图所示。
Java 提供了三种方式来实例化 Class:
1、调用 Class 的静态方法 forName(String className) 创建,将目标类的全限定类名(全限定类名就是包含所在包信息的类名全称,如java.lang.String)作为参数传入,即可获取对应的 Class 对象,forName(StringclassName) 方法的定义如下图所示。
2、通过目标类的 class 创建,Java 中的每一个类都可以调用类.class,这里的 class 不是属性,它叫作“类字面量”,其作用是获取内存中目标类型 class 对象的引用。
3、通过目标类实例化对象的 getClass() 方法创建。getClass() 方法定义在 Object 类中,被所有类继承,通过实例对象获取内存中该类 class 对象的引用,getClass() 方法的定义如下图所示。
上述三种实例化 Class 对象的具体操作如下所示。
public class Test {
public static void main(String[] args) {
try {
Class clazz = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
}
Class clazz2 = String.class;
String str = new String("Hello");
Class clazz3 = str.getClass();
}
}
获取类的结构
我们可以通过 Class 实例化对象获取类的结构,那么类的结构包括哪些内容呢?比如成员变量、成员方法、构造函数、父类、实现的接口等等,都是类的结构,反射也提供了相应的 API 来获取这些内容。
Class 用来描述目标类的结构,叫做目标类的运行时类,常用方法如下。
public Class<?>[] getInterfaces():返回运行时类实现的全部接口。
public Class<? Super T> getSuperclass():返回运行时类的父类。
public Constructor<T>[] getConstructors():返回运行时类的public构造方法。
public Constructor<T>[] getDeclaredConstructors():返回运行时类的全部构造方法。
public Method[] getMethods():返回运行时类的public方法。
public Method[] getDeclaredMethods(): 返回运行时类的全部方法。
public Field[] getFields() :返回运行时类的public成员变量。
public Field[] getDeclaredFields() :返回运行时类的全部成员变量。
Method 用来描述运行时类的方法,常用方法如下。
public Class<?> getReturnType() : 返回方法的返回值类型。
public Class<?>[] getParameterTypes() : 返回方法的参数列表
public int getModifiers() : 返回方法的访问权限修饰符。
public String getName(); : 返回方法名。
public Class<?>[] getExceptionTypes() : 返回方法的异常信息。
Field 用来描述运行时类的成员变量,常用方法如下。
public int getModifiers() : 返回成员变量的访问权限修饰符。
public Class<?> getType() : 返回成员变量的数据类型。
public String getName() : 返回成员变量的名称。
Constructor 用来描述运行时类的构造方法,常用方法如下。
public int getModifiers() : 返回构造方法的访问权限修饰符。
public String getName() : 返回构造方法名。
public Class<?>[] getParameterTypes() : 返回构造方法参数列表。
反射的应用
1、通过反射调用成员方法,反射就是将常规方式进行反转,首先创建实例化对象,然后该对象的方法也抽象成对象,我们称之为方法对象。调用方法对象的 invoke 方法来实现业务需求,并将实例化对象作为参数传入 invoke 方法中。整个过程操作的是方法对象,与常规方式恰好相反,如下所示。
public class Student{
public static void main(String[] args) {
Student student = new Student();
student.setId(1);
student.setName("张三");
Class clazz = student.getClass();
try {
//获取showInfo()方法
Method method = clazz.getDeclaredMethod("showInfo", null);
//通过invoke方法完成调用
method.invoke(student, null);
} catch (NoSuchMethodException e) {
}
}
}
2、通过反射机制,我们也可以在程序运行期间访问成员变量,并获取成员变量的相关信息,如名称、数据类型、访问权限等,具体操作如下所示。
public class Student{
private int id;
public String name;
}
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
int modifiers = field.getModifiers();
Class typeClass = field.getType();
String name = field.getName();
System.out.println("成员变量"+name+"的数据类型是:"+typeClass.getName()+",访问权限是:"+getModifiers(modifiers));
}
}
public static String getModifiers(int modifiers) {
String result = null;
switch (modifiers) {
case 0:
result = "";
break;
case 1:
result = "public";
break;
case 2:
result = "private";
break;
case 4:
result = "protected";
break;
}
return result;
}
}
3、通过反射机制修改成员变量的值,我们知道 private 成员变量在外部是无法访问的,所以直接通过反射来修改 private 成员变量会抛出异常,如何解决呢?通过反射的“暴力修改”即可,所谓的“暴力修改”是指修改 private 成员变量的访问权限,设置为 ture 时,表示可以修改,false 表示不能修改,会抛出异常,默认值为false 。可通过调用 setAccessible(booleanflag) 方法完成权限设置,具体操作如下所示。
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
Student student = new Student();
for (Field field : fields) {
try {
if(field.getName().equals("id")) {
field.setAccessible(true);
field.set(student, 1);
}
if(field.getName().equals("name")) {
field.set(student, "张三");
}
} catch (IllegalArgumentException e) {
}
}
System.out.println("学生信息");
System.out.println("ID:"+student.getId());
System.out.println("姓名:"+student.name);
}
}
4、通过反射机制调用构造函数来创建对象,这种操作在一些工具类或者框架中应用非常广泛,通过配置文件来创建实例化对象就是通过这种方式来完成的,具体操作如下所示。
public class Student{
private int id;
public String name;
public Student() {
}
public Student(int id,String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
public class Test {
public static void main(String[] args) {
Class clazz = Student.class;
try {
//获取Student无参构造函数
Constructor<Student> constructor = clazz.getConstructor(null);
Student student = constructor.newInstance(null);
System.out.println(student);
//获取Student有参构造函数
Constructor<Student> constructor2 = clazz.getConstructor(int.class,String.class);
Student student2 = constructor2.newInstance(1,"张三");
System.out.println(student2);
} catch (NoSuchMethodException e) {
}
}
}
动态代理
动态代理是反射非常重要的一个实际应用,是指在编译期并没有确定具体的代理类,在程序运行期间根据Java代码的指示动态地生成的方式,那么这个动态生成的功能是谁来完成的呢?
java.lang.reflect 包中提供了 InvocationHandler 接口,通过该接口可以在程序运行期间动态生成代理类。首先,自定义一个类MyInvocationHandler,实现 InvocationHandler 接口,这个类就是动态代理类的模版,如下所示。
public class MyInvocationHandler implements InvocationHandler {
private Object obj = null;
public Object bind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = method.invoke(obj, args);
return result;
}
}
在 invoke 方法中可以添加非业务代码,从而做到业务代码和非业务代码的分离,动态代理的具体使用如下所示。
public interface Phone {
public String salePhone();
}
public class Apple implements Phone{
@Override
public String salePhone() {
// TODO Auto-generated method stub
return "销售iPhone手机";
}
}
public interface Car {
public String saleCar();
}
public class BMW implements Car{
@Override
public String saleCar() {
// TODO Auto-generated method stub
return "销售宝马汽车";
}
}
public class Test {
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Phone phone = (Phone)myInvocationHandler.bind(new Apple());
System.out.println(phone.salePhone());
Car car = (Car)myInvocationHandler.bind(new BMW());
System.out.println(car.saleCar());
}
}