这是我的第三篇博客
本篇旨在记录和分享对于Java的反射机制的学习和理解,也希望本篇可以对正在学习的小伙伴有一定的帮助
反射机制是Java最重要的机制之一,是java.lang包下的java.lang.Class类
在Java程序的运行状态中,对于任意一个类,都能知道这个类的属性和方法,对于任意一个对象,都能调用它的任意属性和方法;这种动态获取信息和动态调用对象的方法的功能,就是Java语言的反射机制
Class类的实例表示正在运行的Java程序中的类或接口,每个类或接口都会产生且只会产生一个Class实例对象,这是在JVM对类进行加载的时候就会自动创建出来的
首先创建两个用来测试的普通类
/**
* @author Gavin
* @Description 定义一个Person类,拥有两个public属性,一个无参构造方法,一个两个参数的构造方法,以及
* toString()方法和一个用来测试的方法
*/
public class Person {
public int id;
public String name;
public Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public void eatting(){
System.out.println("eatting");
}
}
/**
* @author Gavin
* @description 定义一个Student类,从Person类中继承
* 有一个public的属性和一个private的属性
* 一个空参构造器,一个两个参数的构造器,还有一个私有的调用了父类构造器的构造器
* 以及一个用来做加法的私有方法
*/
public class Student extends Person{
public String grade;
private String className;
public Student() {
super();
}
private Student(String grade,String className){
this.grade = grade;
this.className = className;
}
public Student(int id, String name, String grade, String className) {
super(id, name);
this.grade = grade;
this.className = className;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public String toString() {
return "Student{" +
"grade='" + grade + '\'' +
", className='" + className + '\'' +
'}';
}
private Integer add(Integer a,Integer b){
return a + b;
}
}
一、获取一个类的Class实例对象
1、通过Class.forName()的方式来获取
//Class.forName()方法里的参数必须是类的完全限定名,及包名+类名
Class clazz = Class.forName("com.gavin.reflect.Person");
2、通过 T.class 的方式来获取,T表示你要获取的类名
Class clazz = Person.class;
3、通过对象的.getClass()方式来获取
//Person person= new Person();
//Class clazz = person.getClass();
Class clazz = new Person().getClass();
//这种方式严格来说是非常不推荐的,因为每个类的Class实例在JVM加载这个类的.class文件的时候就已经被创建了
//所以没有必要再去new这个类的对象了
4、对于基本数据类型的包装类来说,它们还有第四种方式可以获取,即通过类名.TYPE的方式获取
Class clazz = Integer.TYPE;
clazz = Character.TYPE;
clazz = Boolean.TYPE;
//这里要注意的一点是,这种方式获取到的Class对象和它们的基本数据类型.class的防暑获取到的内容是一样的
//即 Integer.TYPE == int.class
以上的四种方法中,通常推荐使用第一种和第二种,下面两种方式一般不推荐使用
当我们获取到了一个类的Class实例之后,接下来就是获取它的具体信息
二、获取该类的成员变量
获取类的成员变量有三种方式
1、获取该类及其父类的所有公共的属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
运行结果:
grade
id
name
2、获取该类本身的所有属性,包括其私有的属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}
运行结果:
grade
className
3、根据其属性的名称获取对应的属性
Field field = clazz.getDeclaredField("className");
System.out.println(field.getName());
运行结果:
className
获取到该类的属性之后,我们可以对其属性进行赋值
以第三种方法为例
//因为Student类中的className属性是私有的,所以这里需要调用Field类中的setAccessible方法将其设置为外部可以访问的
field.setAccessible(true);
Object o = clazz.newInstance();//通过clazz.newInstance方法创建该类的实例对象
field.set(o,"Java反射班");//对其进行赋值,这里需要传入其实例对象
System.out.println(((Student)o).getClassName());
运行结果:
Java反射班
三、获取该类的成员方法
和获取该类的成员变量一样,获取成员方法也有三种方法
1、获取该类及其父类的所有方法(不包括私有方法和构造方法)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
运行结果:
toString
getClassName
setClassName
eatting
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
//可以看到,这里获取了该类以及该类的父类和Object类的所有的公共方法
2、获取该类本身的所有方法(包括其自身的私有方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
运行结果:
add
toString
getClassName
setClassName
3、根据方法名获取对应的方法
Method method = clazz.getDeclaredMethod("add", Integer.class, Integer.class);
//在使用这种方式的时候,除了需要传入方法的名称之外,还需要传入其参数列表的Class对象
获取到该类的方法之后,接下来就是方法调用了,依然以第三种方法为例
method.setAccessible(true);//允许访问该类的私有方法
Object o = clazz.newInstance();//创建该类的实例
Object invoke = method.invoke(o, 123,123);//通过method.invoke()方法进行调用,参数分别是该类的实例和要调用方法的参数列表,多个用,隔开
System.out.println(invoke);
运行结果:
246
四、获取该类的构造方法
同样,获取构造器也有三种方式
1、获取其自身的公共构造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
运行结果:
com.gavin.reflect.Student
com.gavin.reflect.Student
2、获取该类的所有构造器
Constructor<?>[] declaredConstructor = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructor) {
System.out.println(constructor.getName());
}
运行结果:
com.gavin.reflect.Student
com.gavin.reflect.Student
com.gavin.reflect.Student
3、根据构造器的参数列表来获取对应的构造器
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,String.class);
运行结果:
com.gavin.reflect.Student
接下来就是根据反射得到的构造方法来进行对象的创建了
还是以第三种为例
Student student = (Student)constructor.newInstance("一年级", "Java反射班");
//constructor.newInstance()方法的返回值是Object类型,这里是将Object强转为Student
System.out.println(student);
运行结果:
Student{grade='一年级', className='Java反射班'}
杂言:Java的反射机制在整个Java生态中无比重要,很多的框架都是用到反射机制,比如Mybatis,比如Spring的AOP等等
反射的内容非常之多,本篇介绍的只是它最基础、最常用的一些用法,更多的内容请参考JDK API中的Class类,其中有完整的内容。