Java中用反射获取类的属性,方法和构造函数
通过反射机制可以获取一个类的属性,构造函数,方法。
下面以创建的Person类为例。
package com.java.classtest;
public class Person {
public String name;
private int age;public Person() {
System.out.println("这是无参构造函数");
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public int getAge() {
return age;
}public void setAge(int age) {
this.age = age;
}@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}}
通过Person类的包路径,获取Class对象。
Class c = Class.forName("com.java.classtest.Person");
目录
一、 对Class对象的操作
//获取该类的权限修饰符
String modifiers = Modifier.toString(c.getModifiers());
System.out.println(modifiers);
//获取该Class对象的全路径
String name = c.getName();
System.out.println(name);
//获取该Class对象的简写名字
String simpleName = c.getSimpleName();
System.out.println(simpleName);//获取父类的名字
while(c!=null) {
System.out.println(c.getName());
c = c.getSuperclass();
}
使用的都是Class对象的方法,显而易见的是,对于获取某个东西的值的方法大多是get+获取某事物的英文名(首字母大写)。比如说getPackage() ,返回该类包名。
其中为了方便,权限修饰符是可以有多个的,如public static final 。 获取权限修饰符getModifiers()是返回一个int型的数字,需要通过Modifier类的toString解析。
getSuperclass(),就是获取该Class对象的父类Class对象。
运行结果为:
也可以对其Class对象的类型进行判断。判断一个Class对象是一个接口?是一个枚举?是一个数组? 返回一个boolean值,如果判断的符合就会返回true,否则返回false.
格式为: is+需要判断类型的英语单词(首字母大写)。
如isAnnotation(),判断该Class对象是否是一个注解。Annotation的意思是注解。isInterface() 显然就是判断该Class对象是否是接口类型。
例如:
System.out.println("该Class对象是否是注解:"+c.isAnnotation());
System.out.println("该Class对象是否有@Deprecated注解:"+c.isAnnotationPresent(Deprecated.class));
System.out.println("该Class对象是否是一个接口:"+c.isInterface());
System.out.println("该Class对象是否是一个枚举:"+c.isEnum());
System.out.println("该Class对象是否是一个数组:"+c.isArray());
显然,Person所对于的Class对象都不符合上述的条件。
也可以通过该Class对象new一个无参对象。
Object obj = c.newInstance();
当我们执行这条语句时,就会new一个该类的无参构造函数的对象:
二、获取Class对象的属性
属性就是变量那些,按照对应上述的规律,其对应方法就是获取字段getField()。不过也有区别。返回的都是Field类对象。
getField(String name); //获取名为name的公共字段对象
getFields(); //获取Class对象所有可访问的公共字段对象数组
上述的都是必须要字段的权限修饰符为public才能访问。
Field[] fields = c.getFields();
System.out.println("Class对象public的属性数为:"+fields.length);Field nameField = c.getField("name");
System.out.println(nameField.getName());
Field ageField = c.getField("age"); //报错找不到
System.out.println(ageField.getName());
运行结果:
如果想要获取字段不受权限修饰符干扰,也是有方法的,按照上述的两个方法 在get 和 Field(String name)/Fields() 之间加上Declared 就可以了。
按照上述改进:
Field[] fields = c.getFields();
System.out.println("Class对象属性数为:"+fields.length);
for(Field field : fields) {
System.out.println(field.getName());
}
System.out.println("------");
Field nameField = c.getDeclaredField("name");
System.out.println(nameField.getName());
Field ageField = c.getDeclaredField("age");
System.out.println(ageField.getName());
运行结果为:
得到了Field对象之后,就可以获取到该指定属性的信息了,如属性名,属性权限修饰符等,其实跟获取类的信息的方法是一样的,这里只不过是Field对象调用了而已。
Field ageField = c.getDeclaredField("age");
System.out.println(ageField.getName());
String ageModifier = Modifier.toString(ageField.getModifiers());
System.out.println(ageModifier);
运行结果:
Field对象也可以设置对应传进来的类对象的指定字段的值。使用set(Class c ,Object obj)方法。获取指定的字段值就是用get(Class c)方法; 这个set()方法,只能设置除private以外的值。
如对Person类设置name的值。
Object personClass = c.newInstance();
nameField.set(personClass,"Kiss");
System.out.println(personClass);Object namePerson = nameField.get(personClass);
System.out.println(namePerson);
运行结果为:
三、获取Class对象的方法
获取Class对象的方法和获取Class对象的属性的方法模式是一模一样的,只不过是将Field改成Method就行,返回也就Method对象/Method对象数组。就不一一多说了。
另外,值得一提的是,当我们获取的是当个Method对象时,实参要加上该方法的形参的参数类型的Class对象,因为方法会有重载,要在方法名的前提下,也要确定参数的规格。
例如: 获取Person类的方法中的信息
Class c = Class.forName("com.java.classtest.Person");
Method [] methods = c.getDeclaredMethods();
for(Method method:methods) {
System.out.print("方法名为: "+ method.getName()+",");
System.out.print("该方法的权限修饰符为: "+Modifier.toString(method.getModifiers()));
System.out.print("该方法的返回类型为:"+method.getReturnType().getSimpleName());
//获取该方法的形参参数的数据类型 简称获取参数类型
Class [] parameterTypes = method.getParameterTypes();
System.out.print("该方法的形参参数类型为: ");
for( Class parameterType : parameterTypes) {
System.out.print(parameterType.getSimpleName()+" ");
}
System.out.println();
}
运行结果为:
属性可以被设置(set)值,那么方法则可以被调用(invoke) 。我们获得到了一个方法的Method对象,我们就可以通过invoke() 来调用某对象的方法。
Object objPerson = c.newInstance();
Method method = c.getDeclaredMethod("setName",String.class);
method.invoke(objPerson,"Kiss");
System.out.println(objPerson);
运行结果为:
四、获取Class对象的构造方法
构造函数的英语单词为Constructor ,与上述获取方法和属性的使用方法有异曲同工之妙。另外构造方法其实也是方法。所有获取函数方法名,形参参数类型,权限修饰符都是一样的。
Constructor [] constructors = c.getConstructors();
for(Constructor constructor :constructors) {
System.out.print(Modifier.toString((constructor.getModifiers()))+" ");
System.out.print(c.getSimpleName()+" ");
Class [] parameterTypes = constructor.getParameterTypes();
for(Class parameterType:parameterTypes) {
System.out.print(parameterType.getSimpleName()+" ");
}
System.out.println();
}
运行结果为:
既然是获取构造函数对象,那么势必是可以调用构造函数的,那无疑就是new一个对象。没错,跟上述的调用无参构造函数一样Class对象可以newInstance()。构造函数对象的话也可以newInstance(),只是这个时候就可以调用有参构造函数了。
上述的获取字段和方法中,获取的都是某一个准确的字段和方法对象。是一个对象对应一个具体的字段和方法。构造函数也是一样。
Constructor constructor = c.getConstructor(String.class,int.class);
Object objPerson = constructor.newInstance("Kiss",20);
System.out.println(objPerson);
运行结果为:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。