什么是反射机制?
Java反射机制是在运行过程中借助Reflection API,对于任意一个类,都能够知道这个类的所有属性和方法;对于任何一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能成为java语言的反射机制。
java反射机制提供了那些功能?
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的成员变量和方法;
- 生成动态代理。
那么也许就会有一些小朋友问:这个对象属于哪个类我直接看以下我写的代码不就知道了,为什么还要使用反射机制呢?那么接下来我举一个例子:
public class Demo {
public Object object;
public Demo(Object object){
this.object = object;
}
}
在上面的例子中,我们通过构造参数给Demo类中的Object类型的属性进行了注入。调用Demo类的此构造方法的时候,就必须传入一个任意类型的对象进来。但是如果这个对象是外部传入的,并不知道它是什么类型的,那么这个时候我们就应该用到了反射。
跟反射相关的API:
- java.lang.Class: 反射的源头,要想使用java中的反射机制去完成一些事,那么必须要面对它。我们创建的类通过编译(javac.exe)之后会生成对应的.class文件,之后我们运行(java.exe)的时候JVM中的类加载器就会把.class文件加载至内存中。到了内存之后,.class文件就变成了一个运行时类被存储在缓存区中。而这个运行时类本身就是Class类的一个实例。 对于每一个类而言,JRE都为其保留一个不变的Class类型的对象(一个类在JVM中只会有一个Class的实例),其中包含了特定的某个类的相关信息,我们以后正是通过它来回去此对象所对应的类的信息。 又此过程可知,每一个类对应的Class类型的对象都是只能由系统建立。 只有有了Class实例之后才可以创建对应的运行时类的对象;获取对应的运行时类的完整结构(属性,方法,构造器,内部类,父类等信息);调用对应的运行时类的指定的结构。
- java.lang.reflect.Method(java.lang.reflect包中存储着与反射相关的类):提供关于类或者接口上单独某个方法以及如何访问该方法的信息。
- java.lang.reflect.Field:提供有关类或者接口的单个字段的信息以及它的动态访问权限。反射的字段可能是一个类的静态字段或者实例字段。
- java.lang.reflect.Constructor:提供关于类的单个构造方法的信息以及对它的访问权限。
获取运行时类的Class实例的方法:
1. 调用运行时类本身的.class属性;
//得到Person类的Class类的实例 可以理解成clazz在栈中指向了运行时类Person
Class clazz = Person.class;
2. 通过运行时类的对象的getClass( )获取;
Person person = new Person();
Class clazz = person.getClass();
3. 通过Class类的静态方法forName( )获取;
Class clazz = Class.forName("com.demo.Person");//参数为运行时类的类路径
上面的1,2在编译器就会报错,但是3中如果路径写错的话那么只有在运行时才可以报错。
得到运行时类的对象:
//通过Class类的实例的到运行时类的对象
Person person = (Person)clazz.newInstance();
Class类中与运行时类的属性相关方法:
1. getField("")得到运行时类或者其父类中使用public修饰的属性:
//获得Person类的属性名为name的属性
Field field = clazz.getField("name");//抛出NoSuchFieldException
//通过属性为运行时类的实例的相关属性进行赋值
//抛出IllegalArgumentException/IllegalAccessException
//第一个参数指明要操作的类的实例,第二个参数指明属性值
field.set(person,"张三");
2. getDeclaredFields("")获取运行时类全部的自身的属性(包括私有化属性)
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();
Field field = clazz.getDeclaredField("age");
//对私有化的属性进行操作setAccessible必须为true 默认为false
field.setAccessible(true);
field.set(person, 80);
3. getFields( )获取运行时类及其父类使用public修饰的全部属性
Class clazz = Person.class;
//获取运行时类被Public修饰的全部属性和其父类中声明为Public的属性
Field[] fields = clazz.getFields();
//遍历数组,并输出
for(Field f : fields){
System.out.println(f);
}
输出结果如下(Person类只有一个使用public修饰的name属性):
public java.lang.String po.Person.name
4. getDeclaredFields( )获取运行时类自身的全部属性
Field[] fields2 = clazz.getDeclaredFields();
for(int i = 0;i<fields2.length;i++){
System.out.println(fields2[i]);
}
输出结果如下:
public java.lang.String po.Person.name
private int po.Person.age
5. 在Field[ ]中的到属性的修饰符:
Field[] fields3 = clazz.getDeclaredFields();
for(Field f : fields3){
//每一个权限修饰符对应一个int型数据 public-->1 private-->2 default-->0
int i = f.getModifiers();
//将Int型数据转换成权限修饰符
String string = Modifier.toString(i);
System.out.println(string);
}
Class类中与运行时类的方法相关方法:
Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
//关于得到方法中的其他信息。具体看java.lang.reflect.Method API
for(Method method:methods){
System.out.println(method);
}
其输出结果如下:
public java.lang.String po.Person.toString()
public int po.Person.compareTo(java.lang.Object)
public java.lang.String po.Person.getName()
public void po.Person.setName(java.lang.String)
public void po.Person.setAge(int)
public void po.Person.show()
public int po.Person.getAge()
public void po.Person.display(java.lang.String) throws java.lang.Exception
Class类中与运行时类的构造器相关方法:
Class clazz = Person.class;
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor con : constructors){
System.out.println(con.getName());
}
由上面的方法可以看出,其中的方法名都是大同小异:
- 由get开头,由Fields,Constructors或者Methods结尾,则表示得到运行时类及其父类的所有的使用public修饰的属性,构造器或者方法;
- 由get开头,由Field,Constructor或者Method结尾,就是得到运行时类及其父类的特定的使用public修饰的属性,构造器或者方法;
- 由getDeclared开头,则具有得到运行时类的私有化属性的“权利”。