掌握Java反射:高级开发人员的技巧和诀窍

1、反射的定义

反射的定义:Java的反射机制是在运行状态中对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么我们就可以修改部分类型信息。这种动态获取信息以及动态调用对象方法的功能称为反射机制。

反射机制简单来说就是在运行状态中可以获取类或者对象中的任意方法和属性(不管是public修饰的还是privet修饰的)

举例说明:假设一所大学开学,那么这所大学的大学生在开学的这段时间里,得乘坐各种不同的交通工具来学校,有些学生会乘坐飞机、火车、大巴去学校,乘坐这几种交通工具的学生,得先去对应不同的站过安检才能上对应的交通工具,那么过安检的时候需要把行旅箱以及随身带的东西放进安检机器中进行扫描,扫描的时候就可以通过对应的设备看到行旅箱以及随身所带的东西是否有违禁物品

2、 反射的用途

  • 当某个类中的某些属性和方法是私有的或者只对系统应用开放的时候,就可以采用反射来获取所需的私有属性和方法

  • 反射最重要的用途就是开发各种通用框架

3、 反射相关的重要类

类名

用途

Class类

代表类的实体,在运行的Java应用程序中表示类和接口

Field类

代表类的成员变量/类的属性

Method类

代表类的方法

Constructor类

代表类的构造方法

4、 反射机制的起源:Class类

Class代表类的实体,在运行的Java应用程序中表示类和接口

Java文件被编译后就会生成.class的文件,JVM此时就要去解读.class文件,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是java.lang.class。当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们可以通过Java的反射机制应用到这个实例,就去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类

5、 Class类的相关方法

常用获得类相关方法

方法

用途

getClassLoader()

获得类的加载器

getDeclareClasses()

返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)

forName(String classNme)

根据类名返回类的对象

newInstance()

创建类的实例

getName()

获得类的完整路径名字

常用获得类中属性相关的方法(以下方法返回值为Field)

方法

用途

getField(String name)

获得某个公有的属性对象

getFields()

获得所有公有的属性对象

getDeclaredField(String name)

获得某个属性对象

getDeclaredFields()

获得所有属性对象

获得类中注解相关的方法

方法

用途

getAnnotation(Class annotationClass)

返回该类中与参数类型匹配的公有注解对象

getAnnotations()

返回该类所有的公有注解对象

getDeclaredAnnotation(Class annotationClass)

返回该类中与参数类型匹配的所有注解对象

getDeclaredAnnotations()

返回该类所有的注解对象

获得类中方法相关的方法 (以下方法返回值为Method 相关)

方法

用途

getMethod(String name,Class...<?> parameterTypes)

获得该类某个公有方法

getMethod()

获得该类所有公有的方法

getDeclaredMethod(String name,Class...<?> parameterTypes)

获得该类某个方法

getDeclaredMethod()

获得该类所有方法

获得类中构造器相关的方法(以下方法返回值为Constructor相关)

方法

用途

getConstructor(Class...<?> parameterTypes )

获得该类中与参数类型匹配的公有构造方法

getConstructors()

获得该类的所有公有构造方法

getDeclaredConstructor(Class...<?> parameterTypes)

获得该类中与参数类型匹配的构造方法

getDeclaredConstructors()

获得该类所有构造方法

6、 获得 Class 对象

我们想要进行反射,首先得拿到需要反射类的Class对象,通过 Class 对象的核心方法,来进行反射。

获得Class对象分别有三种方法:

  • 使用类对象的 getClass() 方法

  • 使用 .class 方法,仅时候在编译前就已经明确要操作的Class

  • 使用Class.forName("类得全路径名"),这个forName是Class这个类的静态方法,这个获取Class对象的前提条件就是知道类的全路径名

示例:

public class Student {
    private String name = "张三";//私有属性
    public int age = 18;//公共属性
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    //带参数的私有构造方法
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    //私有方法
    private void eat(){
        System.out.println("i am eat");
    }
    //公共方法
    public void sleep(){
        System.out.println("i am sleep");
    }
    //带有参数的私有方法
    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

现在我们就通过获取 Class 对象的三种方法,分别来获取上述的 Student 类的 Class 对象

使用类对象的 getClass() 方法获取:

public static void main(String[] args) {
        Student student = new Student();
        Class<?> s1 = student.getClass();
    }

使用 .class 方法获取:

直接通过 类名.class 的方式获得到的Class对象的方法最为安全可靠,程序性能也会更高,直接通过 类型.class 的方式获得到,也就说明任何一个类都有一个隐含的静态成员变量class

public static void main(String[] args) {
        Class<?> s2 = Student.class;
    }

使用Class.forName("类得全路径名") 方法获取:

通过 Class 对象的 forName() 静态方法来获取,可能会抛出 ClassNotFoundException 异常

public static void main(String[] args) {
        Class<?> s3 = null;
        try {
            s3 = Class.forName("Student");//这里是类的全路径,如果有包需要加包的路径
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

注意:一个类在 JVM中只会有一个 Class 实例,那么也就是说上面我们获取的 s1、s2、s3 这些 Class 对象,其实都是一个 Class 对象

System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s2.equals(s3));

打印结果:

7、 反射的使用

我们目前已经知道了如何获取到一个类的Class实例,那么接下来我们就通过 Class 实例来进行反射的使用

我们还是使用上述的 Student 类来进行反射的使用

7.1 通过反射创建一个 Student 对象

1、通过公共构造方法创建对象

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<?> studentClass = Student.class;//获取类的Class实例
        Object objectStudent = studentClass.newInstance();//通过Class实例,创建一个对象
        Student student = (Student) objectStudent;
        System.out.println("获得的学生对象:"+student);
    }

2、通过私有构造方法创建对象

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> studentClass = Student.class;
        Constructor<?> declaredConstructorStudent = studentClass.getDeclaredConstructor(String.class,int.class);
        declaredConstructorStudent.setAccessible(true);//设置为true后可修改访问权限
        Object objectStudent = declaredConstructorStudent.newInstance("李四",18);
        Student student = (Student)objectStudent;
        System.out.println(student);
    }

7.2 通过反射获取私有属性

public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<?> studentClass = Student.class;
        Field field = studentClass.getDeclaredField("name");
        field.setAccessible(true);
        Object objectStudent = studentClass.newInstance();
        Student student = (Student)objectStudent;
        String name = (String) field.get(student);
        System.out.println(name);
    }

7.3 通过反射获取私有方法

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> studentClass = Student.class;
        Method method = studentClass.getDeclaredMethod("function",String.class);
        System.out.println("私有方法的方法名:"+method.getName());
        method.setAccessible(true);
        Object objectStudent = studentClass.newInstance();
        Student student = (Student)objectStudent;
        method.invoke(student,"私有方法的参数");
    }

注:反射机制不能反射枚举

猜你喜欢

转载自blog.csdn.net/m0_66488562/article/details/128924743