【学习笔记】Java笔记7:反射

87 阅读5分钟

1. 相关介绍

1.1 什么是反射

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

1.2 反射的作用

  • 运行时判断任意对象所属的类
  • 运行时构造任意类的对象
  • 运行时判断任意类所具有的成员变量和方法
  • 运行时获取泛型信息
  • 运行时调用任意对象的成员变量和方法(包括私有成员变量和方法)
  • 运行时处理注解
  • 生成代理对象

2. java.lang.Class

  程序经过javac.exe命令后,会生成字节码(.class)文件,然后java.exe命令对字节码文件进行解释运行,相当于把字节码文件加载到内存中,充当一个运行时类,作为Class的一个实例。即Class的实例就是一个运行时类,一个具体类的Class实例只有一个。

2.1 获取运行时类的实例

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方法一
        Class clazz1 = People.class;
        System.out.println(clazz1);
        
        // 方法二:调用对象的getClass()方法
        People people = new People();
        Class clazz2 = people.getClass();
        System.out.println(clazz2);

        // 方法三:调用Class的静态方法forName(),要写全类名。会抛异常
        // 常用
        Class clazz3 = Class.forName("pers.ljc.test.People");
        System.out.println(clazz3);

        // 方法四:使用类加载器
        ClassLoader classLoader = Test.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("pers.ljc.test.People");

        // 这四个指向的是同一个
        System.out.println(clazz1 == clazz2); // true
        System.out.println(clazz1 == clazz3); // true
        System.out.println(clazz1 == clazz4); // true
    }
}

  可以充当Class实例的有

  • 具体类(外部类,成员内部类,静态内部类,局部内部类,匿名内部类均可)
  • 接口
  • 数组
  • 枚举类
  • 注解
  • 基本数据类型
  • void
  • Class自身
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Object.class;
        Class c2 = Map.class;
        Class c3 = int[][].class;
        Class c4 = ElementType.class;
        Class c5 = Override.class;
        Class c6 = int.class;
        Class c7 = void.class;
        Class c8 = Class.class;

        // 数组的类型和维度一样时,Class就是同一个
        int[] a = new int[20];
        int[] b = new int[100];
        System.out.println(a.getClass() == b.getClass()); // true
    }
}

2.2 创建运行时类的对象

public class Test {
    public static void main(String[] args) throws Exception {
        Class<People> clazz = People.class;
        People people = clazz.newInstance(); // 相当于调用空参构造器
        System.out.println(people);
    }
}

2.3 获取运行时类的结构

  现有如下几种类

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "123";
}
public interface MyInterface {
    void interfaceMethod();
}
public class People<T> implements Serializable {
    private char gender;
    public float weight;

    private void breath() {
        System.out.println("呼吸");
    }

    public void eat() {
        System.out.println("吃");
    }
}
@MyAnnotation(value = "hello")
public class Student extends People<String> implements MyInterface, Comparable<Student> {
    private String name;
    public int age;
    int id;

    public Student() {
    }

    @MyAnnotation(value = "ljc")
    private Student(String name) {
        this.name = name;
    }

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    @MyAnnotation
    private String show(String address) {
        System.out.println("我住在" + address);
        return address;
    }

    public String say(String interest) {
        System.out.println("我的兴趣是" + interest);
        return interest;
    }
    
    private static void sleep() {
        System.out.println("睡觉");
    }
    
    @Override
    public void interfaceMethod() {
        System.out.println("interfaceMethod");
    }

    @Override
    public int compareTo(Student o) {
        return 0;
    }
    // set, get, toString方法...
}

2.3.1 获取属性

public class Test {
    public static void main(String[] args) {
        Class clazz = Student.class;
        // 获取当前运行时类及其父类的public属性
        Field[] fields = clazz.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }
        System.out.println("---------------");
        // 获取当前运行时类的所有属性,不包含父类
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }
}

2.3.2 获取方法

public class Test {
    public static void main(String[] args) {
        Class clazz = Student.class;
        // 获取当前运行时类及其父类(包括Object类)的public方法
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }
        System.out.println("----------------");
        // 获取当前运行时类的所有方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
            // 获取方法的注解,只有被RetentionPolicy.RUNTIME修饰的注解才可以
            Annotation[] annos = m.getAnnotations();
            for (Annotation a : annos) {
                System.out.println(a);
            }
            // 获取方法的修饰符
            System.out.println(Modifier.toString(m.getModifiers()));

            // 通过各种get方法可以获取到方法参数、返回类型、抛出的异常等
        }
    }
}

2.3.3 获取构造器

public class Test {
    public static void main(String[] args) {
        Class clazz = Student.class;
        // 获取当前运行时类的public构造器
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c);
        }
        System.out.println("----------------");
        // 获取当前运行类的所有构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor c : declaredConstructors) {
            System.out.println(c);
        }
    }
}

2.3.4 获取父类

public class Test {
    public static void main(String[] args) {
        Class clazz = Student.class;
        // 获取当前运行时类的父类(无泛型信息)
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
        
        // 获取当前运行时类的带泛型父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
        // 获取泛型类型
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(((Class) actualTypeArguments[0]).getName());
    }
}

2.3.5 获取其他内容

public class Test {
    public static void main(String[] args) {
        Class clazz = Student.class;
        // 获取运行时类的接口
        Class[] interfaces = clazz.getInterfaces();
        for (Class c : interfaces) {
            System.out.println(c);
        }
        // 获取运行时类的包
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
        // 获取运行时类的注解,需要被RetentionPolicy.RUNTIME修饰
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation a : annotations) {
            System.out.println(a);
        }
    }
}

  还有很多方法可以获取到不同的内容,这里不一一演示了。

2.4 调用运行时类的结构

2.4.1 调用属性相关方法

public class Test {
    public static void main(String[] args) throws Exception {
        Class<Student> clazz = Student.class;

        // 创建对象
        Student student = clazz.newInstance();
        // 获取运行时类的属性
        Field name = clazz.getDeclaredField("name");
        // 加了这句才可以为私有属性赋值
        name.setAccessible(true);
        // 为指定对象设置属性值
        name.set(student, "2");

        System.out.println(student);
    }
}

2.4.2 调用指定方法

public class Test {
    public static void main(String[] args) throws Exception {
        Class<Student> clazz = Student.class;

        // 创建对象
        Student student = clazz.newInstance();

        // 获取指定方法,并指明参数列表的类型
        Method show = clazz.getDeclaredMethod("show", String.class);
        show.setAccessible(true);
        // 为指定对象调用该方法
        Object address = show.invoke(student, "广州");
        System.out.println(address);

        // 调用静态方法
        Method sleep = clazz.getDeclaredMethod("sleep");
        sleep.setAccessible(true);
        sleep.invoke(clazz); // 这里的参数为任意对象
    }
}

2.4.3 调用构造器

  这种方式用得少,因为通用性较低(不同的类,构造器不同),一般通过运行时类的newInstance()方法来创建对象。

public class Test {
    public static void main(String[] args) throws Exception {
        Class<Student> clazz = Student.class;

        // 获取构造器,指明构造器的参数列表的类型
        Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class, int.class);
        constructor.setAccessible(true);

        // 通过构造器创建对象
        Student student = constructor.newInstance("小明", 20, 1001);
        System.out.println(student);
    }
}

3. 相关链接