反射
概述
反射就是把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
我们拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值。
而且我们也可以通过反射去创建对象。这也算是创建对象的一种方式吧。这里说一下创建对象的几种方式。
- 直接new一个对象。
- 使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去。
- 使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象。
- 利用反射,获取构造方法,调用newInstance(Object … initargs)方法创建对象。
反射就是框架设计的灵魂
Java反射机制
Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
通过反射获取Class类对象的三种方式
Class.forName("全类名");
Object.class;
Object.getClass();
第一种方式(源码):通过对象的全类名,获取Class对象
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
java.lang包下的Class<?>对象中的静态方法forName(String className),这里的className参数就是我们传递的对象的全类名,该方法会返回给我们一个Class<?>对象。
第三种方式(源码):通过对象.getClass()方法获取Class对象
public final native Class<?> getClass();
Object对象中的getClass()方法,返回一个Class<?>对象。
示例代码
package com.xue.util;
import com.xue.domain.Employee;
/**
* 反射
*/
public class ReflexionUtil {
public static void main(String[] args) {
try {
// 1.通过对象的全类名,获取Class对象
Class<?> clazz = Class.forName("com.xue.domain.Employee");
System.out.println(clazz);
// 2.通过类名.class获取Class对象
Class<Employee> employeeClass = Employee.class;
System.out.println(employeeClass);
// 3.通过对象.getClass()方法获取Class对象
Class<? extends Employee> aClass = new Employee().getClass();
System.out.println(aClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
class com.xue.domain.Employee
class com.xue.domain.Employee
class com.xue.domain.Employee
可以看到,通过反射,我们可以获取到Class对象。
当我们通过反射获取到Class对象以后,就可以进行获取该对象的一些属性和方法,执行该对象的方法,获取和设置对象的属性等一系列的操作。
反射获取构造方法
// 获取该对象的所有的公共的构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
// 获取当前对象的所有构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
示例代码
package com.xue.util;
import java.lang.reflect.Constructor;
/**
* 反射
*/
public class ReflexionUtil {
public static void main(String[] args) {
try {
// 通过对象的全类名,获取Class对象
Class<?> clazz = Class.forName("com.xue.domain.Employee");
// 获取该对象的所有的公共的构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
// 获取当前对象的所有构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
System.out.println("所有的公共的构造方法");
for (Constructor con : constructors) {
System.out.println(con);
}
System.out.println("指定的公共的构造方法");
System.out.println(constructor);
System.out.println("所有构造方法");
for (Constructor declared : declaredConstructors) {
System.out.println(declared);
}
System.out.println("指定的构造方法");
System.out.println(declaredConstructor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
所有的公共的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
public com.xue.domain.Employee()
指定的公共的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
所有构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
private com.xue.domain.Employee(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String,java.lang.String,java.math.BigDecimal)
public com.xue.domain.Employee()
指定的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
我们可以通过反射拿到对象的构造方法。
通过反射获取到该对象的构造方法以后,我们就可以创建该对象。
// 直接创建public对象
newInstance(Object ... initargs);
// 暴力反射 临时取消检查,再创建对象.(非public的)
setAccessible(boolean flag);
示例代码
package com.xue.util;
import java.lang.reflect.Constructor;
/**
* 反射
*/
public class ReflexionUtil {
public static void main(String[] args) {
try {
// 通过对象的全类名,获取Class对象
Class<?> clazz = Class.forName("com.xue.domain.Employee");
// 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
// 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
// 获取该对象的无参构造方法
Constructor<?> noConstructor = clazz.getConstructor();
// 调用newInstance()方法,创建无参对象
Object noInstance = noConstructor.newInstance();
Object instance = constructor.newInstance("张三", 23);
System.out.println("无参对象:" + noInstance);
System.out.println("有参对象:" + instance);
// 暴力反射,取消临时检查,创建对象
declaredConstructor.setAccessible(true);
Object newInstance = declaredConstructor.newInstance("张三", 23);
System.out.println("暴力反射创建私有构造方法的对象:" + newInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
无参对象:Employee{id='null', name='null', age=null, gender='null', post='null', salary=null}
有参对象:Employee{id='null', name='张三', age=23, gender='null', post='null', salary=null}
暴力反射创建私有构造方法的对象:Employee{id='null', name='张三', age=23, gender='null', post='null', salary=null}
调用newInstance()方法创建对象。
如果当前对象的构造方法是私有的,需要对获取的私有构造方法进行暴力反射,取消临时检查。然后才能创建对象。
操作Class类的成员变量的方法
// 获取所有的public修饰的成员变量
Field[] getFields();
// 获取指定的public修饰的成员变量
Field getField(String name);
// 获取所有的成员变量(包含private)
Field[] getDeclaredFields();
// 获取指定的成员变量(包含private)
Field getDeclaredField(String name);
// 获取"obj对象"中"当前Field成员变量"的值
Object get(Object obj);
// 把"obj对象"中"当前Field成员变量"的值设置为"value"
void set(Object obj, Object value);
其他方法
// 获取当前类的全路径类名
String getName()
// 获取当前类的类名
String getSimpleName()
// 获取当前类的属性名
String getName()
// 获取当前类所有的成员变量,包括修饰符和成员变量的全路径名称
Field[] getDeclaredFields()
// 获取成员变量以后,只获取当前成员变量的名称
String getName()
// 获取指定的成员变量的全路径名称
Field getDeclaredField(String name)
示例代码
package com.xue.util;
import com.xue.domain.Employee;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.UUID;
public class ReflexionUtil2 {
public static void main(String[] args) {
try {
// 通过对象的全类名,获取Class对象
Class<?> clazz = Class.forName("com.xue.domain.Employee");
Employee employee = new Employee();
employee.setId(UUID.randomUUID().toString());
employee.setName("张三");
employee.setAge(23);
employee.setGender("男");
employee.setPost("程序员");
employee.setSalary(BigDecimal.valueOf(10000));
System.out.println(employee);
// 获取当前类的全路径类名
String name = clazz.getName();
System.out.println("全路径类名:" + name);
// 获取当前类的类名
String simpleName = clazz.getSimpleName();
System.out.println("类名:" + simpleName);
// 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 取消访问检查
field.setAccessible(true);
// 获取当前成员变量的名称
String fieldName = field.getName();
// 获取当前成员变量的值
Object value = field.get(employee);
System.out.println("key:" + fieldName + ";value:" + value);
if ("name".equals(fieldName)) {
// 设置对象当前成员变量的属性值
field.set(employee, "李四");
}
}
System.out.println(employee);
} catch (Exception e) {
e.printStackTrace();
}
}
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
Employee{id='ca75a29d-34b2-44d9-81dc-b72c91354845', name='张三', age=23, gender='男', post='程序员', salary=10000}
全路径类名:com.xue.domain.Employee
类名:Employee
key:id;value:ca75a29d-34b2-44d9-81dc-b72c91354845
key:name;value:张三
key:age;value:23
key:gender;value:男
key:post;value:程序员
key:salary;value:10000
Employee{id='ca75a29d-34b2-44d9-81dc-b72c91354845', name='李四', age=23, gender='男', post='程序员', salary=10000}
以上示例代码可以看出,通过反射我们可以获取到当前对象的属性和属性值,并且我们可以对当前对象的属性值进行操作。
当我们的类的属性是被private修饰时,我们在获取属性名称和属性值的时候,同样需要取消访问检查。这里和私有构造方法创建对象是一样的。
获取成员方法的方法
// 得到所有的public修饰的成员方法
Method[] getMethods();
// 得到指定的public修饰的成员方法
Method getMethod(String name, Class... parameterTypes);
// 得到所有的成员方法(包含私有)
Method[] getDeclaredMethods();
// 得到指定的成员方法(包含私有)
Method getDeclaredMethod(String name, Class... parameterTypes);
// 调用成员方法并运行
Object invoke(Object obj, Object... args);// 参数一:用obj对象调用该方法;参数二:调用方法传递的参数
示例代码
package com.xue.util;
import com.xue.domain.Employee;
import java.lang.reflect.Method;
public class ReflexionUtil3 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.xue.domain.Employee");
Employee employee = new Employee();
System.out.println(employee);
// 获取所有的public修饰的成员方法
Method[] allMethods = clazz.getMethods();
// 获取所有的成员方法(包含私有)
Method[] allDeclaredMethods = clazz.getDeclaredMethods();
// 获取指定public修饰的成员方法
Method method = clazz.getMethod("setName", String.class);
// 获取指定的成员方法(包含私有)
Method declaredMethod = clazz.getDeclaredMethod("setName", String.class);
// 获取该方法的方法名
String methodName = method.getName();
// 调用该方法
declaredMethod.invoke(employee, "张三");
System.out.println("方法名:" + methodName);
System.out.println(employee);
} catch (Exception e) {
e.printStackTrace();
}
}
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
Employee{id='null', name='null', age=null, gender='null', post='null', salary=null}
方法名:setName
Employee{id='null', name='张三', age=null, gender='null', post='null', salary=null}
通过反射可以获取到该对象的所有的方法,包括私有的和公共的。还可以对获取到的方法进行调用。