目录
1. 所需基础
- java基础语法
2. 反射介绍
Reflection(反射) 是 Java 程序开发语言的特征之一,他的作用简单来说就是可以在程序运行过程中获取一个类的所有信息(包括成员变量,成员方法,构造器等),并且能够进行变量修改和方法调用。
使用场景较多的是:在项目中接入了第三方框架,而其中某些类的属性和方法是私有的,无法获取、调用。这时可通过反射获取其成员变量、方法等信息,进行值的获取、修改、方法调用等等操作。
3. 常用方法介绍
首先有这么一个类需要我们用发射去获取他的基本信息:
PS:暂时不需要细看类的内容,只需要知道这个类有继承父类,实现接口,有属性,有方法即可。
package base;
public class Person extends Animal implements HumanBehavior
{
private static int maxAge = 200;
//私有属性
private String name = "Tom";
//公有属性
public int age = 18;
//构造方法
public Person()
{
System.out.println("无参构造方法");
}
public Person(String name, int age)
{
this.name = name;
this.age = age;
System.out.println("有参构造方法: age: " + age + " name: " + name);
}
//公有方法
@Override
public void say(String content)
{
System.out.println(name + " say: " + content);
}
//私有方法
private void work()
{
System.out.println(name + " working");
}
@Override
public String toString()
{
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName()
{
return name;
}
public static int getMaxAge()
{
return maxAge;
}
@Override
public void play()
{
System.out.println(name+" play");
}
@Override
public void write(String content)
{
System.out.println(name+" write: "+content);
}
}
3.1 获取类对象
接下来,要想获取到类的信息,首先我们要获取到他的class对象。有以下三种方式可以获取类对象:
第一种:
Class<?> clazz;
clazz = Person.class;
第二种:
Class<?> clazz;
clazz = Class.forName("base.Person");
其中base.Person是类的包名+类名。
一般当我们进行模块化开发的时候,有些类我们无法像第一种方式那样直接获取他的类对象,从而用这种方式来获取。
第三种:
Object person = new Person();
Class<?> clazz = person.getClass();
这种方式是通过一个实例化了的对象获取到他的类对象。需要注意的是,这里获取到的类对象是Person.class,而非Object.class哦。
3.2 获取类的基本信息
这里我列出来常用的获取基本信息的方法:
System.out.println("包名+类名为: "+clazz.getName());
System.out.println("类名为: "+clazz.getSimpleName());
System.out.println("包名为: "+clazz.getPackage());
System.out.println("父类为: " + clazz.getSuperclass());
System.out.println("是否为内部类: " + clazz.isAnonymousClass());
System.out.println("是否为Animal类的子类: " + Animal.class.isAssignableFrom(clazz));
System.out.println("是否为抽象类: " + Modifier.isAbstract(clazz.getModifiers()));
System.out.println("是否为接口: " + clazz.isInterface());
打印结果如下:
包名+类名为: base.Person
类名为: Person
包名为: package base
父类为: class base.Animal
是否为内部类: false
是否为Animal类的子类: true
是否为抽象类: false
是否为接口: false
3.3 实例化类对象
我们通过调用类的构造方法来示例化对象。
1. 调用无参构造方法实例化对象:
Class<?> clazz = Person.class;
Person person;
try
{
person = (Person) clazz.newInstance();//调用该类的无参构造方法
System.out.println(person.toString());
}
catch (IllegalAccessException | InstantiationException e)
{
e.printStackTrace();
}
打印结果如下:
无参构造方法
Person{name='Tom', age=18}
可以看到,clazz.newInstance();实际上会调用该类的无参构造方法
2. 调用有参构造方法实例化对象:
try
{
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
person = (Person) constructor.newInstance("张三", 14);
System.out.println(person.toString());
}
catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e)
{
e.printStackTrace();
}
其中,clazz.getConstructor(String.class, int.class);是获取该类中输入参数类型为String和int的构造方法。其实就是前面展示的Person类中的这个方法:
public Person(String name, int age)
{
this.name = name;
this.age = age;
System.out.println("有参构造方法: age: " + age + " name: " + name);
}
constructor.newInstance("张三", 14);表示调用上面获取到方法,并且输入参数为"张三",和14 打印结果如下:
有参构造方法: age: 14 name: 张三
Person{name='张三', age=14}
3. 获取所有构造方法
for (Constructor<?> constructor : clazz.getConstructors())
{
System.out.println("构造方法打印: " + constructor);
System.out.println("输入参数为:");
for (Class<?> parameterType : constructor.getParameterTypes())
{
System.out.println(parameterType);
}
}
打印结果为:
构造方法打印: public base.Person()
输入参数为:
构造方法打印: public base.Person(java.lang.String,int)
输入参数为:
class java.lang.String
int
可以看到,通过clazz.getConstructors()即可获取所有的构造方法。
另外,直接打印constructor变量是可以获取到该构造函数的详细信息的。也可以使用constructor.toString()方法获取其详细信息,与上面打印出来的结果是一致的。
3.4 获取class中属性,并获取和修改其值
1.获取和修改public属性
在前面的类中,我们定义了age这一变量,接下来通过反射的方式来获取以及修改他的值
Class<?> clazz = Person.class;
try
{
Person person = new Person();
Field field = clazz.getField("age");
System.out.println("通过反射得到age: " + field.get(person));
person.age = 20;
System.out.println("通过反射得到age: " + field.get(person));
field.set(person, 30);
System.out.println(person.age);
}
catch (NoSuchFieldException | IllegalAccessException e)
{
e.printStackTrace();
}
clazz.getField传入参数为变量名,需要注意的是该方法返回值并非age属性,它并不能直接获取和修改值。其次,需要特别注意的是,该方法只能获取public修饰的属性。 field.get可获取person对象中age变量的值 field.set可修改person对象中age变量的值
打印结果如下:
无参构造方法
通过反射得到age: 18
通过反射得到age: 20
30
2.获取和修改private属性
try
{
Person person = new Person();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
System.out.println("通过反射得到age: " + field.get(person));
field.set(person, "Any");
System.out.println(person.getName());
}
catch (NoSuchFieldException | IllegalAccessException e)
{
e.printStackTrace();
}
可以看到,用法跟前面获取和修改public属性几乎是一样的,不同的点是使用了clazz.getDeclaredField方法获取私有属性。这里也需要特别说明下,该方法其实是可以获取所有属性,包括public。其实可以完全用这方法替代前面介绍的方式。
另外,若要获取或修改私有属性的值,则必须调用field.setAccessible(true);来允许私有属性可访问
打印结果如下:
无参构造方法
通过反射得到age: Tom
Any
3. 获取和修改静态属性
try
{
Field field = clazz.getDeclaredField("maxAge");
field.setAccessible(true);
System.out.println("通过反射得到age: " + field.get(null));//因为是静态属性,所以不用传入实例化对象
field.set(null, 199);
System.out.println("通过反射得到age: " + field.get(null));//因为是静态属性,所以不用传入实例化对象
}
catch (NoSuchFieldException | IllegalAccessException e)
{
e.printStackTrace();
}
由于静态属性不需要实例化对象,因此在调用field.get和field.set方法时,传入对象为null即可
打印结果如下:
通过反射得到age: 200
通过反射得到age: 199
4. 反射获取获取所有属性
Field[] declaredFields = clazz.getDeclaredFields();
Person person = new Person();
for (Field declaredField : declaredFields)
{
try
{
declaredField.setAccessible(true);
System.out.println("属性名为: " + declaredField.getName() + ", 属性类名为: " + declaredField.getType().getName() + ", 值为:" + declaredField.get(person));
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
通过clazz.getDeclaredFields();可获取类中所有属性。通过代码可以看到除了前面介绍的可以获取和修改属性值外,我们还可获取其基本信息,如,变量名,变量属性等等。
打印结果如下:
无参构造方法
属性名为: maxAge, 属性类名为: int, 值为:199
属性名为: name, 属性类名为: java.lang.String, 值为:Tom
属性名为: age, 属性类名为: int, 值为:18
4. 获取Class中方法及调用
4.1 调用public方法
Class<?> clazz = Person.class;
try
{
Method say = clazz.getMethod("say", String.class);
Person person = new Person("Any", 32);
say.invoke(person, "lalala");
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
{
e.printStackTrace();
}
通过clazz.getMethod获取方法对象,其中第一个参数为方法名,之后的参数为say这一方法的输入参数类型。
需要特别说明的是,getMethod第二个参数是可变数组参数,被反射的输入参数有多少个输入参数,getMethod第二个参数就填多少项。
获取到method对象后,通过method.invoke进行方法调用,其中第一个参数为示例化对象,第二个参数同样是可变数组参数,输入的是被反射方法的输入值。
打印结果为:
有参构造方法: age: 32 name: Any
Any say: lalala
4.2 调用private方法
try
{
Method work = clazz.getDeclaredMethod("work");
work.setAccessible(true);
Person person = new Person("Any", 32);
work.invoke(person);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
{
e.printStackTrace();
}
可以看到,跟前面讲的获取私有属性很类似,获取私有方法则用 clazz.getDeclaredMethod方法,之后也同样需要调用work.setAccessible(true);来允许私有方法可访问。
打印结果如下:
有参构造方法: age: 32 name: Any
Any working
4.3 调用静态方法
try
{
Method say = clazz.getDeclaredMethod("getMaxAge");
say.setAccessible(true);
Object value = say.invoke(null);//因为是静态方法,所以不用传入实例化对象
System.out.println("getMaxAge返回值为:" + value);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
{
e.printStackTrace();
}
调用静态方法,只需invoke第一个参数传入null即可。
打印结果如下:
getMaxAge返回值为:200
4.4 调用所有方法
Person person = new Person();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods)
{
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
try
{
declaredMethod.setAccessible(true);
Object result;
if (parameterTypes.length == 0)
{
result = declaredMethod.invoke(person);
}
else
{
result = declaredMethod.invoke(person, "hi!");
}
System.out.println("方法名为: " + declaredMethod.getName() + ", 输入参数类型为:" + Arrays.toString(parameterTypes) + ", 返回值为: " + result);
}
catch (IllegalAccessException | InvocationTargetException e)
{
e.printStackTrace();
}
}
可以看到,通过clazz.getDeclaredMethods();就可以获取所有的方法了 同样,当我们获取到method对象后,也可调用getName,getParameterTypes方法来获取方法名,方法输入参数等等
打印结果如下:
无参构造方法
方法名为: getName, 输入参数类型为:[], 返回值为: Tom
方法名为: toString, 输入参数类型为:[], 返回值为: Person{name='Tom', age=18}
Tom write: hi!
方法名为: write, 输入参数类型为:[class java.lang.String], 返回值为: null
Tom working
方法名为: work, 输入参数类型为:[], 返回值为: null
方法名为: getMaxAge, 输入参数类型为:[], 返回值为: 200
Tom say: hi!
方法名为: say, 输入参数类型为:[class java.lang.String], 返回值为: null
Tom play
方法名为: play, 输入参数类型为:[], 返回值为: null