Java反射机制
反射的概念
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它任意一个方法和属性。这种动态获取类信息以及动态调用对象方法的功能称为Java语言的反射机制。
Class类
Class类的介绍
- Class类是Java中十分重要的一个类,我们想要使用反射,是离不开这个类的。
- 当JVM加载一个class文件后,就会在内存中生成一个与之对应的Class对象,这个对象保存着该class文件的全部信息。
- 同一类型的多个实例对象,它们的Class对象都是同一个,即在内存中每个Class对象都是独一无二的。
Class对象的获取
获取Class对象的方式有三种。
方式一:Class类的静态方法获取
使用该方式,意味着你的Java代码只有字节码文件,并没有进内存,我们需要手动的将字节码文件加载到内存,然后生成对应的Class对象、并获取Class对象
Class.forName("全类名")
:将字节码文件加载进内存,返回Class对象
*多用于配置文件,将类名定义在配置文件中。读取文件加载类
方式二:类名获取
如果说内存中已经存在这个Class对象,那么我们就不需要再重新把字节码文件加载内存获取Class对象了。我们可以直接通过类名获取对应的Class对象
类名.class
:通过类名的属性获取
*多用于参数的传递,例如方法中需要传递类
方式三:对象获取
若存在类实例对象,那么我们也可以通过该对象来获取该实例对象的Class对象。
对象.getClass()
: getClass()方法在Object类中定义着。
*多用于对象获取字节码的方式
代码测试:
Person类:
package domain;
public class Person {
public String name;
public String sex;
private int age;
private String address;
// 不同访问权限的成员变量 a b c d
public String a;
protected String b;
String c; // default
private String d;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
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;
}
public Person() {
}
// 公有构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 私有构造方法
private Person(String name) {
this.name = name;
}
// 公共成员方法
public void eat() {
System.out.println("吃饭吃饭-------");
}
// 公共成员方法
public void eat(String name) {
System.out.println("吃"+name);
}
// 私有成员方法
private void sleep() {
System.out.println("睡觉睡觉-------");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", address='" + address + '\'' +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
测试类:
import domain.Person;
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 1、 Class类的静态方法获取 通过全类名
Class<?> clazz1 = Class.forName("domain.Person");
System.out.println(clazz1);
// 2、 类名获取
Class<Person> clazz2 = Person.class;
System.out.println(clazz2);
// 3、 对象获取
Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz3);
System.out.println("---------------------");
// 看看这三个对象是否是同一个
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}
}
输出演示:
结论:
同一字节码文件(*.class)在一次程序运行过程中,只会被加载一次。不论通过哪一种方式获取的Class对象都是同一个。
通过Class对象获取 并 使用成员变量
获取成员变量
Field[] getFields()
:获取所有public成员变量Field getField(String name)
:根据名称获取某个public成员变量Field[] getDeclaredFields()
:获取所有成员变量(任意权限修饰的,包括private)Field getDeclaredField(String name)
:根据名称获取某个成员变量(任意权限修饰的,包括private)
代码测试:
import domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class clazz1 = Person.class;
System.out.println("------------ public成员变量们 ---------------");
Field[] fields = clazz1.getFields();
for (Field field: fields) {
System.out.println(field);
}
System.out.println("------------ 通过名称获取某个public成员变量 ---------------");
Field name = clazz1.getField("name");
System.out.println(name);
System.out.println("------------ 所有成员变量们 ---------------");
Field[] declaredFields = clazz1.getDeclaredFields();
for (Field field: declaredFields) {
System.out.println(field);
}
System.out.println("------------ 通过名称获取某个成员变量 ---------------");
Field d = clazz1.getDeclaredField("d");
System.out.println(d);
}
}
输出显示:
使用成员变量
上面我们获取了某个类的成员变量Field,那么我们能拿成员变量干什么呢?
其实无非就是两个操作:设置值 和 获取值
Field getField(String name)
:获取值void set(Object obj, Object value)
:设置值
演示代码:
import domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Person p1 = new Person("coderzpw",22);
Class clazz1 = p1.getClass();
Field name = clazz1.getField("name");
// 通过反射 获取 p1对象的name
Object name1 = name.get(p1);
System.out.println(name1);
// 通过反射 设置 p1对象的name
name.set(p1, "小虎牙");
System.out.println(p1.name);
}
}
输出显示:
其实上面我们使用的成员变量name它是一个public的。我相信大家肯定会有疑问,私有的成员变量我们可以获取并设置吗?
接下来我们来试一下:
import domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Person p1 = new Person();
p1.setD("小D");
Class clazz1 = p1.getClass();
Field d = clazz1.getDeclaredField("d");
// 通过反射 获取 p1对象的d
Object d1 = d.get(p1);
// System.out.println(d1);
// // 通过反射 设置 p1对象的d
// d.set(p1, "大D");
// System.out.println(p1.getD());
}
}
结果:
直接报错了,告诉我们是非法操作!!!
难道我们真的无法通过反射使用p1的d属性吗? 当然可以使用,在反射面前一切访问权限都形同虚设。
看我操作:
import domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Person p1 = new Person();
p1.setD("小D");
Class clazz1 = p1.getClass();
// 获取private成员变量 d
Field d = clazz1.getDeclaredField("d");
// 忽略访问权限修饰符的安全检查
d.setAccessible(true); // 暴力反射
// 通过反射 获取 p1对象的d
Object d1 = d.get(p1);
System.out.println(d1);
// 通过反射 设置 p1对象的d
d.set(p1, "大D");
System.out.println(p1.getD());
}
}
输出演示:
获取、设置成功!!!其实跟上面代码不同点,就是加了d.setAccessible(true);
,这一点是关键
// 忽略访问权限修饰符的安全检查
d.setAccessible(true); // 暴力反射
通过Class对象获取 并 使用构造方法
获取构造方法对象
Constructor<T> getConstructor(Class<?>... parameterTypes)
:根据你的参数类型,获取public修饰的构造方法Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
:根据你的参数类型,获取任意权限修饰的构造方法,包括privateT newInstance()
:直接通过Class对象调用无参构造方法,创建实例
使用构造方法对象创建实例
T newInstance(Object ... initargs)
: 传入对应的参数即可
代码案例:
import domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
Class clazz1 = Person.class;
// 通过public的构造方法 初始化一个对象
Constructor constructor1 = clazz1.getConstructor(String.class, int.class);
Object person1 = constructor1.newInstance("coderzpw", 22);
System.out.println(person1);
// 通过private的构造方法 初始化一个对象(需要加暴力反射代码,不加的话也会报错)
Constructor constructor2 = clazz1.getDeclaredConstructor(String.class);
// 忽略访问权限修饰符的安全检查
constructor2.setAccessible(true); // 暴力反射
Object person2 = constructor2.newInstance("小虎牙");
System.out.println(person2);
// 直接通过Class对象创建实例(必须是无参的)
Object o = clazz1.newInstance();
System.out.println(o);
}
}
输出演示:
通过Class对象获取 并 使用成员方法
获取成员方法对象
Method[] getMethods()
:获取所有public成员方法,包括父类的方法Method[] getDeclaredMethods()
:获取获取任意权限修饰的成员方法,但不包括父类的方法Method getMethod(String name, Class<?>... parameterTypes)
:根据方法名以及参数类型获取指定方法Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:根据方法名以及参数类型获取指定方法(任意访问修饰符修饰的,包括private)
getMethods代码演示:
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Class clazz1 = p1.getClass();
Method[] methods = clazz1.getMethods();
for (Method method: methods) {
System.out.println(method);
}
}
}
输出演示:
getDeclaredMethods代码演示:
import domain.Person;
import java.lang.reflect.Method;
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Class clazz1 = p1.getClass();
// 获取所有的方法(任意访问修饰符,包括private) 但是不包括父类的方法
Method[] declaredMethods = clazz1.getDeclaredMethods();
for (Method m: declaredMethods) {
System.out.println(m);
}
}
}
输出演示:
getMethod 和 getDeclaredMethod 代码演示:
import domain.Person;
import java.lang.reflect.Method;
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Class clazz1 = p1.getClass();
Method eatMethod1 = clazz1.getMethod("eat");
Method eatMethod2 = clazz1.getMethod("eat", String.class);
Method sleepMethod = clazz1.getDeclaredMethod("sleep");
System.out.println(eatMethod1);
System.out.println(eatMethod2);
System.out.println(sleepMethod);
}
}
输出演示:
通过方法对象调用某个对象的方法
Object invoke(Object obj, Object... args)
:调用某个对象的方法,第一个参数是指定对象,其他参数都是方法的形参
invoke代码演示:
import java.lang.reflect.Method;
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Class clazz1 = p1.getClass();
Method eatMethod2 = clazz1.getMethod("eat", String.class);
Method sleepMethod = clazz1.getDeclaredMethod("sleep");
eatMethod2.invoke(p1, "火锅");
// 忽略访问权限修饰符的安全检查
sleepMethod.setAccessible(true); // 暴力反射
sleepMethod.invoke(p1);
}
}
输出演示:
总结
- 其实Class对象、Field对象、Constructor对象和Method对象都有对应的
getName()
方法,获取对应的名字,这里就不演示了 - 无论是获取Field对象、Constructor对象和Method对象,如果是
加s
的都是获取复数,不加s
的是获取指定的一个(一般都需要指定条件) - 一般带Declared的,都可以获取任意访问修饰符修饰的,包括private。但是如果使用private的对象的话,需要提前调用对应的
setAccessible(true)
方法