反射
基本概念
反射机制:将类的各个组成部分封装为其他对象,这就是反射机制。
通俗来讲,反射就是来操作Java类的一种机制,通过反射可以操作Java中的字节码文件。
反射的核心类是Class类,它是类的类模板,即该类是用来描述Java类的。
描述的基本上就是一个类的组成部分:包名、类名、接口、父类、成员变量(Filed)、构造方法(Construct)、普通方法(Method)、注解(Annotation)等等。
所以说,反射的操作基本上就是对这些类的元素进行操作。
Java在计算机中会经历三个阶段,分别如下:
Source源代码阶段:*.java文件会被编译成*.class字节码文件。
Class对象阶段:*.class字节码文件会被类加载器装进内存,并将其封装成Class对象。
RunTime运行阶段:创建对象的过程。
其中,在Class对象阶段,Class对象将原字节码中所有成员变量封装成Field[],将构造函数封装成Construction[],将成员方法封装成Method[]。
获取Class对象
获取Class对象分别对应Java代码在计算机中的三个阶段:
1、Source源代码阶段:多用于配置文件的读取和加载。
Class.forName("全类名")。
2、Class类对象阶段:多用于参数的传递。
**.class
3、Runtime运行阶段:多用于对象的获取字节码的方式
xxx.getClass();
注意:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
案例分析
下面的案例演示三种Java字节码的获取方式:
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
// 方式一:Class.forName("全类名")
Class clazz1 = Class.forName("com.legend.reflect.Person");
System.out.println("clazz1 = " + clazz1);
// 方式二:类名.class
Class clazz2 = Person.class;
System.out.println("clazz2 = " + clazz2);
// 方式三:对象.getClass()
Class clazz3 = new Person().getClass();
System.out.println("clazz3 = " + clazz3);
boolean isEquals = (clazz1 == clazz2) && (clazz2 == clazz3) ? true : false;
System.out.println("clazz1 == clazz2 == clazz3:" + isEquals);
}
}
输出结果如下所示:
clazz1 = class com.legend.reflect.Person
clazz2 = class com.legend.reflect.Person
clazz3 = class com.legend.reflect.Person
clazz1 == clazz2 == clazz3:true
Class对象功能
Class对象包含一个类的所有信息,这里只介绍主要的。
(1)获取成员变量
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
(2)获取构造函数
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
(3)获取成员方法
Method[] getMethods()
Method getMethod(String name, Class<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
(4)获取全类名
String getName()
(5)获取类加载器
ClassLoader getClassLoader()
Filed成员变量
下面针对Person对象中的三种修饰符:private、protected和public三种类型的成员变量使用反射赋值。
1、定义Person对象,并提供三种修饰符修饰的属性。
public class Person {
public String name;
protected int age;
private String address;
...get和set...
}
2、使用获取成员变量的方式进行反射,只能获取到public的成员变量。
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
Class personClass = Person.class;
// 获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields){
System.out.println(field);
}
}
}
获取所有的变量,必须使用getDeclaredFields()方法来完成。
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
Class personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields){
System.out.println(field);
}
}
}
注意:如果获取单个private或protected属性的值需要使用暴力反射来完成:setAccessible(true);
Constructor构造函数
通过反射的方式来创建对象,使用到的实体类如下:
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
...get和set...
}
然后通过反射的方式来创建该对象:
public class Main {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
Constructor[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors){
System.out.println(constructor);
}
// 获取有参的构造函数来创建对象
Constructor constructor = personClass.getConstructor(String.class, Integer.class);
Object legend = constructor.newInstance("Legend", 30);
System.out.println(legend);
}
}
如果要获取private修饰的构造函数,需要使用getDeclaredConstructor方法。
Method方法对象
通过反射调用对象中的方法,所用到的实例类如下:
public class Person {
private String name;
private Integer age;
public Person() {
}
...get和set...
public void eat(String food){
System.out.println("eat..." + food);
}
}
然后通过反射的方式来执行eat方法:
public class Main {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
// 获取所有public的方法
Method[] methods = personClass.getMethods();
for (Method method : methods){
System.out.println(method);
}
// 执行字节码中的方法
Object o = personClass.newInstance();
Method method = personClass.getMethod("eat", String.class);
method.invoke(o, "apple");
}
}
反射的大致用法如上所示,还可以通过反射的方式去修改配置类并运行。