Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,RTTI,即Run-Time Type Identification,运行时类型识别。RTTI能在运行时就能够自动识别每个编译时已知的类型。它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。
(1).获取类信息
举例,有一个testreflection.Student类
package testreflection;
public class Student {
public Student(String sex, int age) {
this.sex=sex;
this.age=age;
}
public Student() {};
private String sex="男";
private int age=15;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study()
{
System.out.println(sex + " "+ String.valueOf(age)+ " 好好学习");
}
}
测试类:
package testreflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class testor
{
@Test
public void test() throws ClassNotFoundException
{
Class<?> clazz=Class.forName("testreflection.Student");
Field[] field=clazz.getDeclaredFields();
System.out.println("----------显示类的属性----------------------------");
for(Field f:field)
{
System.out.println(f.getName()+" "+f.getType());
}
System.out.println("----------显示类的方法-----------------------------");
Method[] methods=clazz.getDeclaredMethods();
for(Method m:methods)
{
System.out.println(m.getName());
}
System.out.println("---------显示类的构造方法-----------------------------");
Constructor[] constructors=clazz.getDeclaredConstructors();
for(Constructor<?> c: constructors)
{
System.out.println(c);
}
System.out.println("---------获取类的相关的信息--------------------------");
System.out.println("类所在的包为:"+ clazz.getPackage().getName());
System.out.println("类名:"+ clazz.getName());
System.out.println("父类的名称:"+ clazz.getSuperclass().getName());
}
}
运行结果:
这个例子通过java.lang.reflect中的反射,通过类的全名“testreflection.Student”就得到了该类的所有信息,这就是反射,这样就可以在运行时动态的获得类的信息,接下来要将如何生成Student类的实例对象
PS:Class<?>和Class,class有什么区别?
Class<?>和Class这两者并没有什么区别,最好写成Class<?>.
class是java中的关键字,而Class是一个类,就是可以看成Student类,Student类中有sex,age属性,Class类中则有和类相关的一些属性方法,如private transient String name、 public static Class<?> forName(String),也就是说Class也是一个普通类,跟Student,Person并无二致
Student stu=new Student(); 这里的stu就等价于上例中的clazz, 因为Class类构造函数是私有的:
private Class(ClassLoader loader) {
classLoader = loader;
}
所以只能通过静态方法如forName来得到实例clazz, 总而言之,Class就是一个包含类信息的普通类,跟Student一样
(2)得到类Class的三种方式
@Test
public void test2() throws ClassNotFoundException
{
//第一种,对象.getClass
Student student=new Student();
Class clazz=student.getClass();
System.out.println(clazz.toString());
//第二种,类名.class
clazz=Student.class;
System.out.println(clazz.toString());
// 第三种:Class.forName("类的路径")
clazz=Class.forName("testreflection.Student");
System.out.println(clazz.toString());
}
运行结果:
(3)使用反射动态创建对象实例的方式
@Test
public void test3() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException
{
//方法1:通过Class的newInstance()方法
//该方法要求该Class对象的对应类有无参构造方法
//执行newInstance()实际上就是执行无参构造方法来创建该类的实例
Class<?> clazz=Class.forName("testreflection.Student");
Object obj = clazz.newInstance();
((Student)obj).study();
//方法2:通过Constructor的newInstance()方法
//先使用Clazz对象获取指定的Constructor对象
//再调用Constructor对象的newInstance()方法来创建该Class对象对应类的对象
//通过该方法可选择使用指定构造方法来创建对象
Constructor constructor=clazz.getConstructor(new Class[] {String.class,int.class});
Object obj1 =constructor.newInstance(new Object[]{"女",30});
((Student)obj1).study();
//以下也可以调用无参构造方法
Object obj2 = clazz.getConstructor().newInstance();
((Student)obj2).study();
}
运行结果:
男 15 好好学习
女 30 好好学习
男 15 好好学习
(4)使用反射动态修改和查询字段的值
getXxx(Object obj) & setXxx(Object obj,Xxx val) :获取或设置obj对象该Field的属性值。此处的Xxx对应8个基本数据类型,如果该属性类型是引用类型则直接使用get(Objectobj)
setAccessible(Boolean flag):若flag为true,则取消属性的访问权限控制,即使private属性也可以进行访问
@Test
public void test4() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchFieldException, SecurityException {
Class<?> clazz = Class.forName("testreflection.Student");
Object object = clazz.newInstance();
//获得 Student类中的指定属性对应的Field对象
Field field = clazz.getDeclaredField("age");
//取消属性的访问权限控制,即使private 属性也可以进行访问
field.setAccessible(true);
//通过field对象获取真实field的值,有两种方式
System.out.println(field.get(object));
System.out.println(field.getInt(object));
//通过field给真实对象中的field赋值
field.set(object, 33);
System.out.println(field.getInt(object));
}
运行结果:
15
15
33
(5)使用反射动态执行方法
通过Class对象的getMethods()方法可以获得该类所包括的全部方法,返回值是Method[]
通过Class对象的getMethod() 方法可以获得该类所包括的执行方法,返回值是Method
每个Method对象对应一个方法,获得Method对象后,可以调用其invoke() 来调用对应方法
@Test
public void test5()throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException,
SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
Class<?> clazz = Class.forName("testreflection.Student");
Object obj = clazz.newInstance();
//调用该对象的 setSex方法
Method method = clazz.getMethod("setSex", new Class[] { String.class });
Object result = method.invoke(obj, new Object[] { "iam a man" }); // obj.setName("iam a man");
//调用对象的getSex()方法
Method method1 = clazz.getMethod("getSex", new Class[] {});
Object obj1 = method1.invoke(obj, new Object[] {});
System.out.println("已设置为" + obj1);
}
运行结果:
已设置为iam a man
(6)使用反射动态创建数组并存取元素
在java.lang.reflect包提供了Array类,包括一系列static方法,可动态的创建数组、给元素赋值、取出元素值等
Array提供的主要方法如下:
static ObjectnewInstance(Class<?> componentType, int[] dim) :创建一个具有指定的组件类型和维度的新数组
static void setXxx(Objectarray, int index ,xxx val):给数组对象array中第index个元素赋值val
static xxx getXxx(Objectarray, int index):以 xxx形式返回指定数组对象array中第index个元素值
@Test
public void test6()throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException,
SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
Class clazz = Class.forName("java.lang.Integer");
Object array = Array.newInstance(clazz, 10);// 根据类的class 创建大小为10的数组
Array.set(array, 5, 111);// 给数组的第5个元素赋值为111
Object el = Array.get(array, 5);// 取出数组的第5个元素值显示
System.out.println(el);
}
运行结果:
111
总结:
1.反射就是java.lang.reflect包所提供的一系列类和方法,里面包含了Class,Field,Method等类
2.Felid类跟Method,Class类本质上是一样的,他们都是用来保存class相关信息的类,跟Student类并无二致
3.通过反射,我们可以知道类的所有信息,可以动态创建类,调用类方法,查看修改类字段,动态创建类的数组等等