什么是反射?
Java中的反射是指在运行时动态地获取和操作类信息的能力。通过反射,可以在运行时获取类的构造方法、方法、字段等信息,并且可以在不知道类名的情况下创建对象、调用方法和访问字段。使用反射机制可以极大地提高程序的灵活性和可扩展性,但也会带来性能上的损失。
反射的作用是什么?
Java反射是指程序在运行时可以动态地获取一个类的信息,包括类的属性、方法、构造方法等,以及可以动态地调用类的方法、获取和设置类的属性。Java反射提供了一种机制,使得程序能够在运行时检查和操作类、方法、属性等元素,而无需在编译时就确定这些元素的信息。它在一些框架和组件中被广泛使用,例如Spring、Hibernate等,可以实现动态配置、动态加载类和实现动态代理等功能。
如何通过反射获取class对象
获取class对象的方式有三种:
- 类名.class
- 对象.getClass()
- Class.forName("类全路径")
首先我们先创建一个User对象并生成构造函数以及Get\Set方法,toString方法,为了方便测试写一个私有方法一个公有方法。
public class User {
public String name;
public int age;
private String gender;
private void priMethod(String name){
System.out.println("私有方法");
}
private void priMethod(){
System.out.println("私有方法");
}
public void pubMethod(){
System.out.println("公有方法");
}
public void pubMethod(String name){
System.out.println("公有方法");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
private User(int age) {
this.age = age;
}
public User() {
}
public User(String name) {
this.name = name;
}
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
现在来利用三种方式来获取class对象。
//1、类名.class
Class clazz = User.class;
//2、对象.getClass()
Class clazz2 = new User().getClass();
//3、Class.forName("类全路径")
Class clazz3 = Class.forName("com.study.reflect2.User");
通过class对象获取对象实例
得到class对象后就可以进行实例化。
//实例化
User user = (User) clazz.getDeclaredConstructor().newInstance();
User user2 = (user)Class.forName("com.study.reflect2.User").newInstance();
通过实例获取对象构造方法
1、获取public修饰的空参构造
Constructor constructor = clazz.getConstructor();
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
2、获取public修饰的所有构造
Constructor[] constructors = clazz.getConstructors();
3、获取public修饰的指定参数构造器
Constructor constructor1 = clazz.getConstructor(String.class);
Constructor constructor2 = clazz.getConstructor(String.class, int.class, String.class);
4、获取所有构造器(包括private修饰的)
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
获取构造函数后即可根据其参数来实例化对象,public修饰的构造函数可以利用 newInstance() 方法直接实例化,而private修饰的构造函数可以通过 constructor.setAccessible(true) 来暴力操作。
注意:实际上 setAccessible(true) 是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查,值为 false 则指示反射的对象应该实施 Java 语言访问检查。同时,由于JDK的安全检查耗时较多,所以通过setAccessible(true) 的方式关闭安全检查就可以达到提升反射速度的目的。
通过class对象获取对象属性
1、获取public修饰的指定属性
Field name = clazz.getField("name");
Field age = clazz.getField("age");
2、获取指定属性(包括private修饰的)
Field name1 = clazz.getDeclaredField("name");
Field age1 = clazz.getDeclaredField("age");
Field gender1 = clazz.getDeclaredField("gender");
3、获取public修饰的所有属性
Field[] fields = clazz.getFields();
4、获取所有属性(包括private修饰的)
Field[] declaredFields = clazz.getDeclaredFields();
获取属性后可以利用实例化对象进行赋值操作:
Field[] fields = clazz.getDeclaredFields();
User user = (User)clazz.getDeclaredConstructor().newInstance();
for (Field field : fields) {
if (field.getName().equals("name")){
field.set(user,"张三");
}
}
同样的,私有化属性无法直接操作,可以通过 field.setAccessible(true) 来暴力操作。
通过class对象获取对象方法
1、获取公有方法(可以指定参数类型)
Method pubMethod = clazz.getMethod("pubMethod");
Method pubMethod1 = clazz.getMethod("pubMethod",String.class);
2、获取所有方法(private + public 可以指定参数类型)
Method priMethod1 = clazz.getDeclaredMethod("priMethod");
Method priMethod2 = clazz.getDeclaredMethod("priMethod",String.class);
Method pubMethod1 = clazz.getDeclaredMethod("pubMethod");
Method pubMethod2 = clazz.getDeclaredMethod("pubMethod",String.class);
3、获取所有公有方法(包括父类的方法)
Method[] methods = clazz.getMethods();
4、获取所有方法(private + public 不包含父类方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
反射会忽略泛型,可以插入不同类型数据
反射获取注解
不论是类、属性、方法、构造函数,都可以利用反射来获取其注解,并且如果注解有值,还可以获取注解的值。
自定义注解
@Target({ElementType.CONSTRUCTOR, ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface study {
String name() default "";
int age() default 0;
String[] hobby();
}
获取注解
Annotation annotation = clazz.getAnnotation(study.class);
Annotation annotation = constructor.getAnnotation(study.class);
Annotation annotation = field.getAnnotation(study.class);
Annotation annotation = method.getAnnotation(study.class);
在获取注解时可以直接将获取到的注解转成注解类型,这样我们就可以获取注解的值。
study study = (com.study.reflect.study) clazz.getAnnotation(study.class);
study study = (com.study.reflect.study) constructor.getAnnotation(study.class);
study study = (com.study.reflect.study) field.getAnnotation(study.class);
study study = (com.study.reflect.study) method.getAnnotation(study.class);
//获取内容
String name = study.name;
int age = study.age;
String[] hobby = study.hobby;
反射原理
程序在执行之前首先都会将.java文件编译为.class文件,在使用反射时,jvm会将对应的.class文件通过类加载器加载到内存中,获取到.class文件后,就可以进行进一步操作了。
其他操作
待完善~~