Logo

郎哥编程

使用反射机制加载类

2019-06-26 72

在Java程序设计中,加载类的方式一般是使用new运算符,new运算符会创建类的一个实例,类的实例也称为实例对象或对象,并返回创建的实例对象的引用。

下面的代码定义了一个Book类和BookManager类:Book类是一个实体类,有bookName和bookAuthor属性;在BookManager类定义了loadBookWithNew()方法,该方法使用new运算符加载Book类,并创建Book类的实例对象,BookManager类的main函数调用了该方法。

Book类代码:

package entity;
/**
 *
  *   作者:  编程训练营
  *   说明:图书实体类
 *
*/
public class Book {
   
    //图书名称
    private String bookName;
   
    //图书作者
    private String bookAuthor;
   
    /**
     * 无参构造函数
     */
    public Book() {
        super();
    }
 
    /**
     * 带参构造函数
     * @param bookName
     * @param bookAuthor
     */
    public Book(String bookName,String bookAuthor)
    {
        super();
        this.bookName = bookName;
        this.bookAuthor = bookAuthor;
    }
 
    /**
     *
     *  重写父类的toString()
     *     以字符串方式返回类信息
     */
    @Override
    public String toString() {
        return "Book [bookName=" + bookName + ", bookAuthor=" + bookAuthor + "]";
    }
 
    /**
     * 返回Book类的bookName属性
     */
    public String getBookName() {
        return bookName;
    }
 
    /**
     * @param 设置Book类的bookName属性
     */
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
 
    /**
     * 返回Book类的bookAuthor属性
     */
    public String getBookAuthor() {
        return bookAuthor;
    }
 
    /**
     * @param 设置Book类的bookAuthor属性
     */
    public void setBookAuthor(String bookAuthor) {
        this.bookAuthor = bookAuthor;
    }
}

BookManager类代码:

package reflect;
import entity.Book;
/**
 * 作者:编程训练营
 * 说明:演示Book类的加载方式
 *
 */
public class BookManager {
 
    public static void main(String[] args) {
       
        //使用new运算符加载类
        loadBookWithNew();
    }
   
   
    /**
     * 使用new运算符加载Book类
     */
    public static void loadBookWithNew()
    {
        Book tempBook = new Book();
        tempBook.setBookName("java技术深入理解");
        tempBook.setBookAuthor("编程训练营");
        //以字符串方式输出类信息
        System.out.println(tempBook.toString());
    }
}

当程序中需要加载类时,通用方法是使用new运算符,new运算符是如何把类实例化为对象的呢?

我们知道Java类经过编译后会生成class文件,也就是字节码文件。new会调用类加载器(ClassLoader)来加载类,类加载器根据new传入的类名称在运行的Java程序路径中查找要加载的类,如果找到与该类名称相对应的字节码文件,类加载器会把类的字节码文件读入内存,并创建一个java.lang.Class对象(类被实例化为对象)。如果该类有父类,类加载器也会把父类加载到内存中。类加载器完成类的加载任务后,new会完成类加载的后续工作,最后返回实例化对象的引用给对象变量。

对象变量会调用类提供的方法执行类提供的功能,获取或设置类的属性。例如:在前面案例代码BookManager类的loadBookWithNew方法中,实例化的类对象tempBook会调用Book类提供的set方法来设置Book类的属性,同时调用Book类的toString()方法以字符串方式获取类的信息,类的这些属性和方法在代码编译之前是必须要确定的。如果用new加载类后,类缺少这些方法和属性,编译器就会报错。因此new加载类属于静态加载,要接受编译器的检查。

静态加载主要的问题是编译之前要确定好类的功能和方法,当类修改后需要重新编译整个Java程序。对商业应用的分布式Java程序或Java WEB程序来说,这显然不是最好的方式,因为我们希望在不停止程序服务的情况下,更新局部的程序功能。

针对前面的案例代码,现在有个新的需求。需要在前面案例代码的Book类添加价格属性,并修改toString方法。希望仅编译Book类,不需要重新编译项目下所有的类。

要实现上面的需求,就需要用到Java语言的反射机制。在本课内容中,先不具体介绍反射机制的工作原理及构成。先用反射代码实现上面的需求,对反射有感性认识后,再具体介绍反射的工作原理及构成。

下面的函数代码使用反射机制来加载Book类。在前面案例的BookManager类中加入下面的loadBookWithRelflect()方法,并在main函数中调用该方法,可以实现和loadBookWithNew()方法同样的效果。

/**
     * 使用反射加载Book类
     */
    public static void loadBookWithRelflect()
    {
        try {
            Class<?> classBook = Class.forName("entity.Book");
            Object objectBook = classBook.newInstance();
            Book tempBook = (Book) objectBook;
            tempBook.setBookName("java技术深入理解");
            tempBook.setBookAuthor("编程训练营");
            System.out.println(tempBook.toString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
 
}

loadBookWithRelflect()虽然使用了反射机制加载Book类,但Book类添加属性后,还是需要修改loadBookWithRelflect()的函数代码,并在代码中调用设置Book新属性的方法,因此还是需要重新编译BookManager类。

需要重新编译BookManager类,主要原因是要修改loadBookWithRelflect方法内的代码,在loadBookWithRelflect方法内添加设置Book类新属性的代码。试想一下,如果我们把Book类的方法名及传入的参数类型以及需要设置的属性值写到配置文件中,loadBookWithRelflect方法从配置文件中读取所有方法名以及方法的参数类型和属性值,然后使用Method类的invoke方法执行从配置文件中读取的方法。这样就实现了在Book中类添加新的属性和属性设置方法时,只需要将Book类的属性设置方法和属性值写入到配置文件中即可,而无需重新修改和编译BookManager类的需求。貌似有点Spring框架配置文件的影子哦~~

下面的代码给出了如何使用Method类的invoke方法执行从配置文件中读取的方法。案例代码并没有从配置文件中读取Book类的方法名称,主要是考虑到代码不要过于复杂,只要说明问题就好。代码声明了Book类的方法名,并在Class对象的getMethod方法中传入声明的方法名,最后调用Method类的invoke执行方法。有兴趣的同学完全可以把Book类的方法名称及参数类型写入到XML文件中,dynamicLoadBook方法从配置文件中读取方法名称和参数类型,真正实现Book类的动态加载。

/**
     * 使用反射加载Book类
     * 使用方法名调用方法
     */
    public static void dynamicLoadBook()
    {
        try {
            Class<?> classBook = Class.forName("entity.Book");
            //设置Book类的setBookName方法名
            String setBookName = "setBookName";
            //设置Book类的setBookAuthor方法名
            String setBookAuthor = "setBookAuthor";
          //设置Book类的toString方法名
            String toString = "toString";
            Object objectBook = classBook.newInstance();
            //使用Class对象的getMethod方法获取Book类的公开方法
            Method methodA = classBook.getMethod(setBookName,String.class);
            //使用Method的invoke方法执行Book类的公开方法
            methodA.invoke(objectBook,"java技术深入理解");
            Method methodB = classBook.getMethod(setBookAuthor,String.class);
            methodB.invoke(objectBook,"编程训练营");
            Method methodC = classBook.getMethod(toString);
            System.out.println(methodC.invoke(objectBook));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
 
}

现在我们对反射机制已经有了感性认识,后面的课程内容会逐渐揭开反射机制神秘的面纱。


代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论