Java - 反射
介绍:
是一门Java提供的专门技术, 这门技术让 Java 成为一门准动态语言
存在性能问题, 会占用额外内存, 速度也会较慢
一个类在内存中只有一个 Class
对象
一个类被加载后, 类的整个结构都会封装在 Class
对象中
Class 类:
class
的类, 所有类都指向了 Class
类
包含了类的所有信息
Class
只能由系统创建- 一个加载的类在JVM中只会有一个
Class
实例 - 一个
Class
对象对应的是一个加载到JVM中的一个 .class 文件 - 每个类的实例都会记得自己是由哪个
Class
实例所生成 - 通过
Class
可以完整的得到一个类中的所有被加载的结构 Class
类是Reflection
的根源, 针对任何你想动态加载 \ 运行的类, 唯有获得相应的Class
对象
所有类都有 Class
对象, void/enum
也有
只要类型和维度一样, 不管对象里面的值一不一样, Class
对象都是同一个对象
内存分析:
类加载过程:
- 加载: 将class文件字节码内容加载在内存中, 并将这些静态数据转换为方法区的运行时数据结构, 然后生成一个代表这个类的java.lang.Class对象
- 链接: 将Java类的二进制代码合并到JVM的运行状态之中的过程
- 验证: 确保加载的类信息符合JVM规范, 没有安全方面的问题
- 准备: 正式为类变量(static) 分配内存并设置类变量默认初始值的阶段, 这些内存都将在方法区中进行分配
- 解析: 虚拟机常量池内的符合引用(常量名)替换为直接引用(地址)的过程
- 初始化:
- 执行类构造器
<clinit>()
方法的过程. 类构造器<clinit>()
方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句产生的. (类构造器是构造类)
- 执行类构造器
获取字节码对象:
通过类名称获取:
className.class
class Test {
void test() {
try {
Class<User> userClass = User.class;
User user = userClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过对象获取:
object.class
class Test {
void test() {
User user = new User();
Class<User> userClass = (Class<User>) user.getClass();
}
}
通过 Class 的方法获取:
Class.forName(类路径)
class Test {
void test() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> userClass = Class.forName("practise.User");
Constructor<?> constructor = userClass.getDeclaredConstructor();
User user = (User) constructor.newInstance();
}
}
字节码对象的方法:
有 Declared
可以拿到所有不被权限限制
没有 Declared
只可以拿到可以访问的(包括父类)
获取构造器:
方法 | 描述 |
---|---|
Constructor<?>[] getConstructors() | 以列表的形式返回所有的可以访问的(包括父类)构造函数 |
Constructor<?>[] getDeclaredConstructors() | 以列表的形式返回所有的自身声明的构造函数, 不受权限限制 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 获取特定的可以访问的(包括父类)构造函数, 参数为对应构造函数的参数的字节码 |
Constructor<T> getDeclaredConstructor() | 获得自身声明的构造器, 不受权限限制 |
获取方法:
方法 | 描述 |
---|---|
Method getMethod(String name, Class<?>... parameterTypes) | 获取指定方法 |
Method[] getMethods() throws SecurityException | 获取所有可以访问的(包括父类)方法 |
Method getDeclaredMethod() | |
Method[] getDeclaredMethods() |
获取属性:
方法 | 描述 |
---|---|
Field getField(String name) throws NoSuchFieldException, SecurityException | 获取属性, 形参为属性名 |
Field[] getFields() throws SecurityException | 获取属性 |
Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException | 获取属性 |
Field[] getDeclaredFields() throws SecurityException | 获取属性 |
Constructor 方法:
方法 | 描述 |
---|---|
String getName() | 获取构造函数名 |
native int getModifiers() | 获取构造函数访问修饰符. public-0x00000001, private-0x00000002, protected-0x00000004, static-0x00000008, final-0x00000010, synchronized-0x00000020, volatile-0x00000040, transient-0x00000080, native-0x00000100, interface-0x00000200, abstract-0x00000400 |
int getParameterCount() | 获取构造函数参数个数 |
Class<?>[] getParameterTypes() | 获取构造函数参数类型 |
Method 方法:
方法 | 描述 |
---|---|
void setAccessible(boolean flag) | true 打破封装 |
Object invoke(Object obj, Object... args) | 执行方法, obj 是要执行此方法的类, args 是参数数组 |
通过反射实现对象Clone:
class TestReflect {
@Test
void test01() {
User user = new User("Erzbir, 18, 1");
User user1 = new User();
copyObject(user, user1);
System.out.println(user1);
}
// 通过反射复制(拷贝)对象
private void copyObject(User user, User user1) {
try {
Class<?> clazz = user.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object o = field.get(user);
field.set(user1, o);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}