反射

反射

1 反射

java反射机制是在运行状态中,对于任意的一个类,都能够知道这个类的所有的属性和方法,对于任意一个对象,都能够调用它的任意的属性和方法。这种动态获取信息以及动态调用对象的方法的功能称之为java语言的反射机制。

要想解剖一个类,必须先要获取这个类的字节码文件对象。

需要掌握的单词:

  • Class 类
  • Constructor 构造方法
  • Method 方法
  • Field 属性
  • instance 实例对象
  • invoke 执行

2 获取字节码文件对象

  • 通过类名.class 获取类的字节码文件对象
  • 通过对象.getClass() 获取这个对象对应的实际类的字节码文件对象
  • 通过Class.forName(类的全路径名) 获取指定的类的字节码文件对象
public static void main(String[] args) throws ClassNotFoundException {
        // 通过类名.class获取
        Class<String> c = String.class;
        System.out.println(c);
        // 接口
        Class<List> listClass = List.class;
        System.out.println(listClass);
        // 数组
        Class<int[]> aClass = int[].class;
        System.out.println(aClass);
        // 基本数据类型
        Class<Double> doubleClass = double.class;
        System.out.println(doubleClass);

        // 通过对象名.getClass()
        Object obj = "abc";
        Class<?> aClass1 = obj.getClass();
        System.out.println(aClass1);

        // 通过Class.forName(全路径类名)获取
        Class<?> aClass2 = Class.forName("java.util.Date");
        System.out.println(aClass2);

    }

3 构造方法

private static void demo3()throws Exception {
        // 获取字节码文件对象
        Class<Person> personClass = Person.class;
        // 获取任意指定的构造方法
        Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class,int.class,String.class);
        // 暴力破解
        constructor.setAccessible(true);
        // 调用构造方法创建对象
        Person person = constructor.newInstance("张三", 18, "郑州");
        System.out.println(person);

        // 获取所有公共的构造方法
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor1 : constructors) {
            System.out.println(constructor1);
        }

        // 获取所有的构造方法
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
    }

4 属性

private static void demo4() throws Exception {
        // 获取字节码文件对象
        Class<Person> personClass = Person.class;
        // 获取指定的公共的属性
//        personClass.getField("");
        // 获取指定的任意属性
        Field name = personClass.getDeclaredField("name");
        System.out.println(name);
        // 暴力破解
        name.setAccessible(true);
        // 给属性赋值
        Person person = new Person("aa",20);
        System.out.println(person);
        name.set(person,"张无忌");
        System.out.println(person);
        // 获取属性值
        Object o = name.get(person);
        System.out.println(o);

        // 获取所有的公共属性
        for (Field field : personClass.getFields()) {
            System.out.println(field);
        }

        // 获取所有的属性
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
    }

5 方法

private static void demo5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person("aa",19);
        // 获取字节码文件对象
        Class<Person> personClass = Person.class;
        // 获取公共的方法
        // 参数一: 方法的名称
        // 参数二: 方法的参数类型
        Method method = personClass.getMethod("eat");
        System.out.println(method);
        // 调用方法
        method.invoke(person);

        // 获取非公共的方法
        Method sleep = personClass.getDeclaredMethod("sleep", String.class);
        System.out.println(sleep);
        // 调用非公共的方法
        // 暴力破解
        sleep.setAccessible(true);
        Object invoke = sleep.invoke(person, "时代少年团,我们喜欢你");
        System.out.println(invoke);

        // 获取所有的公共方法(包含继承过来的)
//        for (Method personClassMethod : personClass.getMethods()) {
//            System.out.println(personClassMethod);
//        }

        // 获取所有的方法(不包含继承的)
//        for (Method declaredMethod : personClass.getDeclaredMethods()) {
//            System.out.println(declaredMethod);
//        }
    }

6 反射的劣势

  • 打破封装原则
  • 跳过泛型的检查
private static void demo6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<String> list = new ArrayList<>();
        // 获取字节码文件对象
        Class<? extends List> listClass = list.getClass();
        // 获取add方法
        Method method = listClass.getDeclaredMethod("add", Object.class);
        // 执行方法
        method.invoke(list,123);
        System.out.println(list);
    }

7 相关的获取方法

private static void demo7() throws NoSuchFieldException, NoSuchMethodException {
        // 获取枚举的字节码文件对象
        Class<Season> seasonClass = Season.class;
        // 获取枚举常量  如果不是枚举类,返回null
        Season[] enumConstants = seasonClass.getEnumConstants();
        for (Season enumConstant : enumConstants) {
            System.out.println(enumConstant);
        }

        Class<String> stringClass = String.class;
        // 获取一个类实现的接口
        Class<?>[] interfaces = stringClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }

        // 获取父类
        Class<? super String> superclass = stringClass.getSuperclass();
        System.out.println(superclass);

        // 获取全路径类名
        String className = stringClass.getName();
        System.out.println(className);
        // 获取类名
        String simpleName = stringClass.getSimpleName();
        System.out.println(simpleName);
        // 获取包
        Package aPackage = stringClass.getPackage();
        System.out.println(aPackage);
        // 获取包名
        String packageName = stringClass.getPackageName();
        System.out.println(packageName);

        // 获取属性的声明类型
        Field field = stringClass.getDeclaredField("hash");
        Class<?> type = field.getType();
        System.out.println(type);
        // 获取属性/方法/构造的修饰符
        int modifiers = field.getModifiers();
        System.out.println(modifiers);

        Method method = stringClass.getDeclaredMethod("getBytes",String.class);
        // 获取方法的声明类 不能获取继承的方法
        System.out.println(method.getDeclaringClass());
        // 获取方法的返回值类型
        Class<?> returnType = method.getReturnType();
        System.out.println(returnType);
        // 获取方法声明的编译时异常
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

        // 获取方法的参数个数
        int parameterCount = method.getParameterCount();
        System.out.println(parameterCount);
        // 获取方法的参数类型
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType);
        }

        // 获取方法的泛型
        for (TypeVariable<Method> typeParameter : method.getTypeParameters()) {
            System.out.println(typeParameter);
        }
    }

image-20250716113328179

8 相关的判断方法

private static void demo8() {
        // 判断是否是一个数组
        System.out.println(int[].class.isArray());
        // 判断是否是一个枚举
        System.out.println(Season.class.isEnum());
        // 判断是否是一个接口
        System.out.println(List.class.isInterface());
        // 判断是否是基本数据类型
        System.out.println(int.class.isPrimitive());
        // 判断是否是匿名内部类
        Object obj = new Object(){};
        System.out.println(obj.getClass().isAnonymousClass());
        // 判断是否是方法内部类
        System.out.println(obj.getClass().isLocalClass());
        // 判断是否是成员内部类
        System.out.println(obj.getClass().isMemberClass());
        // 判断是否是静态内部类
        System.out.println(obj.getClass().isSynthetic());
        // 判断类和对象的关系
        String str = "abc";
//        System.out.println(str instanceof String);
        System.out.println(String.class.isInstance(str));
        // 判断类和类之间的关系
        System.out.println(Object.class.isAssignableFrom(String.class));
        // 判断接口和类之间的关系
        System.out.println(Serializable.class.isAssignableFrom(String.class));
    }

9 注解

9.1 自定义注解


// 数组中只有一个元素,可以省略{}
// 如果传递值时只有一个,并且属性值是value,那么可以省略 value =
@FirstAnno("abc")
public class TestDemo2 {

    private int age;

    public static void main(String[] args) {
        
        int i = 10;
    }
}

@interface FirstAnno{
    // 注解中可以定义属性,会使用public static final修饰
//    int i = 10;

    // 定义属性
    String value();

//    double d();
    // 默认值
    char c() default 'a';
    // 注解中的属性只能是基本数据类型/String/Class/注解/枚举以及它们所对应的一维数组的形式
//    Object obj();
//    int[] arr();
}

9.2 元注解

对注解进行注解的注解

@Target({ElementType.TYPE,ElementType.FIELD})  // 限定注解的使用位置
@Retention(RetentionPolicy.RUNTIME) // 限定注解的存活时期
@Documented  //可以让注解生成到java的文档中
@Inherited  // 会注解到类的子类上  表示注解也可以继承了
    /** 类 接口 注解 枚举  Record */
    TYPE,

    /** 属性 */
    FIELD,

    /** 方法 */
    METHOD,

    /** 形参 */
    PARAMETER,

    /** 构造方法 */
    CONSTRUCTOR,

    /** 局部变量 */
    LOCAL_VARIABLE,
    /**
     * 源代码
     */
    SOURCE,

    /**
     * 编译后的class文件
     */
    CLASS,

    /**
     * 运行时状态
     */
    RUNTIME

9.3 反射注解

public class TestDemo3 {
    public static void main(String[] args) throws NoSuchMethodException {
        System.out.println("有一天,不小心闯了一个红灯");
        // 判断Police上面有没有注解
        if (Police.class.isAnnotationPresent(Level.class)){
            // 获取注解的属性值
            Level annotation = Police.class.getAnnotation(Level.class);
            String value = annotation.value();
            if ("辅警".equals(value)){
                System.out.println("送50块钱");
            }else if ("民警".equals(value)){
                System.out.println("拘留1天");
            }else if ("交警".equals(value)){
                System.out.println("扣6分,罚200");
            }else if ("刑警".equals(value)){
                System.out.println("判刑1年");
            }
        }else {
            System.out.println("假警察,扭送到派出所");
        }

        Method method = Police.class.getMethod("eat");
        if (method.isAnnotationPresent(Level.class)){
            String value = method.getAnnotation(Level.class).value();
            if ("民警".equals(value)){
                System.out.println("民警辛苦了");
            }
        }else {
            System.out.println("呵呵呵呵呵");
        }
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Level{
    String value();
}

@Level("刑警")
class Police{
    @Level("民警")
    public void eat(){
        System.out.println("人都要吃饭");
    }
}

课堂练习:定义方法拷贝对象,新对象的属性值和原对象相同

// 定义方法拷贝对象,新对象的属性值和原对象相同
    public static Object clone(Object obj) throws InstantiationException, IllegalAccessException {
        // 获取这个对象的实际类型对应的字节码文件对象
        Class<?> aClass = obj.getClass();
        // 利用字节码文件对象创建新的对象
        Object newObj = aClass.newInstance();
        // 获取这个类型中的所有的属性
        Field[] declaredFields = aClass.getDeclaredFields();
        // 依次获取原对象的每一个属性的值往新对象对应的属性赋值
        for (Field declaredField : declaredFields) {
            // 暴力破解
            declaredField.setAccessible(true);
            // 获取属性值
            Object of = declaredField.get(obj);
            // 将属性值设置到新对象上
            declaredField.set(newObj,of);
        }
        // 返回新对象
        return newObj;
    }

版本二:

// 定义方法拷贝对象,新对象的属性值和原对象相同
    public static Object clone(Object obj) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        // 获取这个对象的实际类型对应的字节码文件对象
        Class<?> aClass = obj.getClass();
        // 利用字节码文件对象创建新的对象  调用的是无参构造
//        Object newObj = aClass.newInstance();
        // 获取构造方法  一个类中至少有一个构造方法,所以这里随便拿一个构造方法
//        Student(String,int,int)
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructors()[0];
        // 获取构造方法的参数类型
        Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
        // 定义一个数组,存放构造方法中属性的默认值
        Object[] objs = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            // 获取每一个参数类型
            Class<?> parameterType = parameterTypes[i];
            // 判断参数类型是否是基本数据类型
            if (parameterType.isPrimitive()){
                if (parameterType == int.class || parameterType == short.class || parameterType == byte.class){
                    objs[i] = 0;
                }else if(parameterType == long.class){
                    objs[i] = 0L;
                }else if (parameterType == float.class){
                    objs[i] = 0.0F;
                }else if (parameterType == double.class){
                    objs[i] = 0.0;
                }else if (parameterType == char.class){
                    objs[i] = '\u0000';
                }else {
                    objs[i] = false;
                }
            }else {
                // 引用数据类型
                objs[i] = null;
            }
        }
        // Student(String,User,Person,int)
        // new Student(null,null,null,0);
        Object newObj = declaredConstructor.newInstance(objs);

        // 获取这个类型中的所有的属性
        Field[] declaredFields = aClass.getDeclaredFields();
        // 依次获取原对象的每一个属性的值往新对象对应的属性赋值
        for (Field declaredField : declaredFields) {
            // 暴力破解
            declaredField.setAccessible(true);
            // 获取属性值
            Object of = declaredField.get(obj);
            // 将属性值设置到新对象上
            declaredField.set(newObj,of);
        }
        // 返回新对象
        return newObj;
    }
posted @ 2025-07-16 22:34  小胡coding  阅读(8)  评论(0)    收藏  举报