zoukankan      html  css  js  c++  java
  • 一篇文章弄懂 Java 反射的使用

    说到Java反射,必须先把 Java 的字节码搞明白了,也就是 Class , 大 Class
    在之前的文章中,我们知道了Java的大Class就是类的字节码,就是一个普通的类,里面保存的是类的信息,还不太明白Java的大Class的,可以先看一下之前的文章 一篇文章彻底搞懂Java的大Class到底是什么

    先想一个问题

    1. 给我们一个类,我们如何使用?

    这还不简单,通过这个类,创建一个类的对象,再通过这个对象,调用类的方法或者属性

    比如有一个类叫 Student , 里面有一个 name字段和一个 age 字段,还有3个方法, 源码如下:

    package com.model;
    
    public class Student {
        private String name;
        private int age;
    
        public Student(){
        }
    
        public Student(String name,int age){
            this.name = name;
            this.age = age;
        }
    
        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 void show(){
            System.out.println("name=" + this.name + " age=" + this.age);
        }
    }
    
    

    上面的代码很简单,应该都能看懂,我们以这个Student类来实验

    回到上面的问题:如何使用这个类? ,代码如下:

            //1 创建一个对象
            Student s = new Student();
    
            //2 调用对象的方法
            s.setName("李雷");
            s.setAge(23);
    
            //3 打印一下
            s.show();
    

    打印的结果下:
    name=李雷 age=23

    上面就是和 反射 相反的通过正常的方式创建一个类的对象,然后通过对象调用类的方法

    其实我们还可以根据类的字节码来创建对象,然后调用类的方法
    也就是通过某个类的 Class ,来创建对象,然后调用类的方法

    2. 如何获取类的 Class 呢?

    3个方法,以Student为例,演示如下:

    第一种:通过 Class.forName("com.model.Student") 来获取Student的 Class

    代码如下:

    Class cls = Class.forName("com.model.Student");
    

    第二种:通过 Student.class

    Class cls = Student.class
    

    第三种:通过类的对象来获取,调用类的对象的 getClass()方法

    Student s = new Student();
    Class cls = s.getClass()
    

    以上就是三种方法获取一个类的 Class 的方法,必须要牢记,尤其是前 2 个,用的最多

    3. 如何通过Class来创建对象,进而来调用类的方法或者属性呢?

    • 第一步:获取类的 Class 对象
    • 第二步:获取对应的方法的字节码 Method 以及 构造函数的字节码 Constructor ,或者字段的字节码 Field
    • 第三步:通过ConstructornewInstance()方法生成一个类的对象
    • 第四步:通过调用 Methodinvoke()方法完成调用类的代码

    代码演示如下:

            //第一步:获取Student的 Class 对象,即Student的字节码
            Class cls = Class.forName("com.model.Student");
    
            //第二步:获取无参的构造方法的字节码,当然也可以获取有参的
            Constructor constructor = cls.getConstructor();
    
            //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj
            Object obj = constructor.newInstance();
    
            //第四步:获取 setName 方法的字节码,注意参数传方法的名字以及方法中参数的字节码
            //      获取了setName的字节码 method,调用方法必须要有一个对象,所以上面的obj对象就是用来此处的
            //      一定要传进行
            Method method = cls.getMethod("setName", String.class);
            method.invoke(obj,"待兔");
    
            //和上面类似,只不过这次 getMethod 的第二个参数传的是 int.class
            //因为第二个参数是int类型
            Method method1 = cls.getMethod("setAge", int.class);
            method1.invoke(obj,23);
    
            //和上面类似 ,只不过 show()方法是无参的,所以 getMethod 只需要传方法的名字"show" 即可
            Method showMethod = cls.getMethod("show");
    
            //最后:调用showMethod方法,通过调用showMethod的invoke方法,里面传入前面创建的obj对象
            //就达到了调用对象的show方法
            showMethod.invoke(obj);
    

    通过上面的代码演示可以看出,在不知道 Student 类型的情况下,我们只需要知道 Student类的全类名(包名+类名)
    也就是com.model.Student ,就可以获取到 Student类的

    和直接通过 Student s = new Student(); s.show(); 这种方法不一样的是,上面是在运行时通过字符串值知道要运行的类是com.model.Student

    所以,反射就是在运行的时候 ,才知道这个类是什么,并且可以在运行的时候 ,获取这个类的完整信息,并调用对应的方法

    4. 常用的反射API

    4.1 在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象

    获取Class对象有三种方法,上面已经讲过,这里再次贴出来,加深印象

    • 使用 Class.forName 静态方法,前提是你知道类的全类名
      Class cls = Class.forName("com.model.Student"); ,其实这种方法就是加载类的
    • 使用类的 .class 方法
      Class cls = Student.class
      不过这种方法,只适合在编译时就知道操作的 Class
    • 使用类对象的 getClass() 方法。
      Student s = new Student();
      Class cls = s.getClass()

    4.2 获取所有类的的方法

    可以通过 Class对象 getMethods()或者 cls.getDeclaredMethods() 来获取所有的方法的字节码

    两者的区别是:getMethods()获取的方法包括父类的,getDeclaredMethods() 获取的是子类的

    演示 getMethods()

    Method[] methods = cls.getMethods();
    for (Method m : methods) {
        System.out.println(m.getName());
    }
    

    输出出下:

    getName
    setName
    setAge
    show
    getAge
    wait
    wait
    wait
    equals
    toString
    hashCode
    getClass
    notify
    notifyAll

    可以看到,输出了很多父类中的方法(Object类中的方法)

    再来看一下 getDeclaredMethods() 方法

    Method[] methods = cls.getDeclaredMethods();
    for (Method m : methods) {
        System.out.println(m.getName());
    }
    

    输出如下:

    getName
    setName
    setAge
    show
    getAge

    可以看到,只有子类自己的方法,并没有父类的方法

    5. 通过反射创建类的对象需要注意的点

    上面我们通过 Constructor 对象的newInstance()方法,来创建对象
    其实还有一种方法,也可以使用 Class对象的newInstance()

    5.1 第一种:通过 Class 对象 newInstance()方法

    Class cls = Class.forName("com.model.Student");
    Student obj = (Student) cls.newInstance();
    

    5.2 第二种:通过 Constructor 对象的 newInstance() 方法

    //第一步:获取Student的 Class 对象,即Student的字节码
    Class cls = Class.forName("com.model.Student");
    
    //第二步:获取无参的构造方法的字节码,当然也可以获取有参的
    Constructor constructor = cls.getConstructor();
    
    //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj
    Object obj = constructor.newInstance();
    

    ::: warning
    通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。
    下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
    :::

    Class cls = Class.forName("com.model.Student");
    Constructor constructor = cls.getConstructor(String.class,int.class);
    Object obj = constructor.newInstance("tom",23);
    

    通过上面的讲解,应该对反射的用法有了个大致的了解了,Class有很多方法,感兴趣的可以自己写个helloworld调试一下
    不过怎么说,还是要先弄明白 Class 到底是什么,知道了 Class 的本质 ,再来看反射,就很容易了

    还不太明白的一定要看看下面的文章
    一篇文章彻底搞懂Java的大Class到底是什么

  • 相关阅读:
    python中列表排序的方法
    pyrhon3中字符串方法
    python数据探索与数据与清洗概述
    2020年日期表-python实现
    python中字符串离散化的例子
    python中常见的日期处理方法
    如何简单地理解Python中的if __name__ == '__main__'
    我的老爸老了
    关于
    关于
  • 原文地址:https://www.cnblogs.com/start1225/p/14861722.html
Copyright © 2011-2022 走看看