1、反射的原理是什么
一般情况下,如果想生成这个类的对象时,运行这个程序的JVM会确认这个类的Class对象是否已经加载,如果尚未加载,JVM就会根据类名查找.class文件,并将其载入,一旦这个类的Class对象被载入内存,它就可以被用来创建这个类的所有对象。
另外,对于一个未知类型的引用来说,通常会采用强制类型转换的形式来得到开发者想要的类型引用,如果只选了一个错误的类型转换,就会抛出一个ClassCastException异常。
在以上两个过程中,Class类一直都在起作用。因为Class类实例包含一个Java类的全部信息,包括类名、方法、属性等。Class对象就是代表一个类的类。
Java用Class类来代表了所有的类,方便了开发者掌控信息,通过Class,开发者可以得到属性(Field)、方法(Method)、构造器(Constructor)、修饰符(Modifier)等信息。在动态的获取这些信息以后,开发者就可以用该Class创建实例、调用任何方法、访问任何属性等操作,这些也是反射的主要用途。
反射机制相关的API主要集中在java.lang.reflect包下面,开发者也就是利用该包下面的接口和类进行相关的反射开发的。大多数的框架,如Struts、Hiberbate和Spring,都会频繁的使用反射API来完成他们的动态功能。
总结:
反射就是为了能够动态的加载一个类,动态的调用一个方法,动态地访问一个属性等动态要求设计的。它的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下的API已达到各种动态需求。
2、Class类的含义和作用
Class是用来记录类的各种信息的,伴随着类的加载而创建。一个普通的Java类会在一下几种情况,被加载到JVM中:
- 需要使用该类创建的对象。如下代码就会使Student类被加载到JVM中:
Student stu = new Student();
- 访问该类的静态成员;
System.out.println(Student.count);//访问静态的count成员变量
- 使用Class类的静态方法forName()方法,动态的加载一个指定类名的类,如果类没有找到,则抛出ClassNotFoundException异常:
Class.forName("com.test.Studeng");//动态加载Student类
JDBC导入驱动类,就是Class.forName()的一个常用例子。
类一旦被加载到JVM中,就会为它创建一个Class类的实例对象。要得到一个类的Class对象,可以通过以下几种途径。
- Class的forName()方法的返回值就是Class类型,也就是动态导入类的Class对象的引用。forName()方法的完整定义:
public static Class<?> forName(String className) throws ClassNotFoundEception
- 每个类都会有一个名称为Class的静态属性,通过它也可以获取Class对象的,
Class<Student> cla = Student.class;//访问Student类的class属性
- Object类中有一个名为getClass的成员方法,它返回的是对象运行时类的Class对象。因为Object类是所有类的父类,所以,所有的对象都可以使用该方法得到它运行时类的Class对象,
Student stu = new Student();
Class<Student> cla = stu.getClass();
获取到Class对象后,就可以通过调用它的一些成员方法来获取他所代表的类的属性、方法、修饰符等信息,以及调用newInstance()方法来创建新的实例对象。
总结
每一个Class类的对象就代表了一种被加载进入JVM的类,它代表了该类的一种信息映射。开发者可以通过一下3中途径获取到Class对象。
- Class类的forName()方法的返回值;
- 访问所有类都会拥有的静态的class属性;
- 调用所有对象都有的getClass()方法。
在Class类中,定义许多关于类信息的方法,例如getName()、getMethod()、getConstructor()、newInstance()等可以用于反射开发,还有isInstance()和isInterface()等一些关于类的功能方法。
3、如何操作类的成员变量(Field)
Field对象通过Class类的getDeclareField()或getDeclareFields()方法获取到,处于java.lang.reflect包。Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限,反射的字段可能是一个静态的字段或实例的字段。
Field等方法主要分为两大类,getXXX和setXXX。其中getXXX是用于获取某个对象的该字段的值,并且有一定的类型规定,一般有两个参数,一个是对象引用,一个是需要设置的值。以下是一个Field的使用示例,通过反射比较两个对象的大小:
import java.lang.reflect.Field;
//测试类
class FieldTest{
String name;
int age;
public FieldTest(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
public class TestField {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
FieldTest obj1 = new FieldTest("hello1", 100);
FieldTest obj2 = new FieldTest("hello2", 200);
System.out.println(compare(obj1,obj2).name+"is bigger");
}
//用反射定义一个通用的比较方法,返回age相对更大的对象
private static FieldTest compare(FieldTest obj1,FieldTest obj2) throws IllegalArgumentException, IllegalAccessException {
try {
Field field = obj1.getClass().getDeclaredField("age");
field = FieldTest.class.getDeclaredField("age");
int val1 = (Integer) field.get(obj1);
int val2 = (Integer) field.get(obj2);
if(val1 > val2) {
return obj1;
}else {
return obj2;
}
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
总结
Field提供有关类或接口的单个静态或实例字段的信息,它通过Class类的getDeclaredField()或getDeFields()方法获取到,再置于java.lang.reflect包下。Field的方法主要分为两大类,即getXXX和setXXX,都需要提供相应的实例对象,setXXX还需要提供设置的值。
4、如何操作类的方法(Method)
Method对象通过Class类的getMethod()或getMethods()方法获取到,也处于java.lang.reflect包下。Method提供关于类或接口中的某个方法的信息,所反映的方法可能是类方法或实例方法。
Method类中使用最多的是invoke(),通过它来完成方法被动态调用的目的。
5、如何利用反射实例化一个类
通常我们使用new关键字就可以实例化一个类,也就是创建一个对象。使用new关键字创建对象的时候,系统将调用类的某个构造方法。
根据调用构造方法的不同,用反射机制来实例化一个类,有两种途径。如果使用无参构造方法,则直接使用Class类的newInstance()方法即可;
若需要使用特定的构造方法来创建对象,则需要先获取Constructor实例,再用newInstance()方法创建对象。
6、如何利用反射机制来访问一个类的私有成员
在使用反射机制访问私有成员的时候,它们的可访问性是为false的。需要调用setAccessible(true)方法,把原本不可访问的私有成员变为可以访问以后,才能进行成功的访问或调用。
7、如何利用反射来覆盖数据对象的toString()方法
- 通过getDeclaredFields()方法得到所有的Field对象;
- 把上一步得到的Field对象数组进行遍历;
- 每一次循环加上字段名和字段值;
- 返回循环叠加以后的字符串结果。