天天看点

Java之Class.forName方法详解

  • 一、前言
  • 二、案例
  • 三、详解

一、前言:

在说明Class类的静态方法forName()之前,先清楚有关Class类的几个概念:

1、 Class类封装了类或接口的运行时状态

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,这些标示纪录了每个对象所属的类。

虚拟机通常使用运行时类型信息选择正确方法去执行,用来保存这些类型信息的类是Class类。

2、Class类型的对象,是加载类时自动创建的

Class 没有公共构造方法。Class 对象是在加载类时,由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

3、虚拟机为每种类型管理一个独一无二的Class对象

每个类(型)都有一个Class对象。

运行程序时,Java虚拟机(JVM)首先检查所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

1.基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。

2.每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

3.一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

以上说法查看Class源码会发现,

Book.class.getName()

最终调用的:

private transient String name;
public String getName() {
    String name = this.name;
    if (name == null)
        this.name = name = getName0();
    return name;
}
           

此时Book也是一个独一无二的Class对象,即对象中的对象。

二、案例:

Book.java类

package com.junit.demo;

public class Book {
    private static final String defName = "《程序猿植发》";

    static {
        System.out.println("我是静态代码块,输出: " + defName);
    }

    //打印生产日期:
    public static String printProduceDate(String name) {
        return "我是静态方法printProduceDate,输出: " + name + ", produce is:" + System.currentTimeMillis();
    }

    private String name;

    public Book() {
        System.out.println("我是Book声明的构造方法!");
        name = defName;
    }

    public String toString(String msg) {
        return name + msg;
    }
}
           

执行方法:

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
    //1-将指定类加载到JVM中(ClassNotFoundException)
    Class aClass = Class.forName("com.junit.demo.Book");
    System.out.println(aClass);
    //2.1-访问静态方法:NoSuchMethodException,InvocationTargetException
    Method method = aClass.getMethod("printProduceDate", String.class);
    String result = (String) method.invoke(aClass, "《程序猿的颈椎自传》");
    System.out.println(result);
    System.out.println("---------------------------\n");
    //2.2-初始化对象:
    Book obj = (Book) aClass.newInstance();
    System.out.println("得到对象后访问get方法:" + obj.toString(""));
    System.out.println("---------------------------\n");
    //2.3-初始化对象后访问方法:
    Method method3 = aClass.getMethod("toString", String.class);
    String result3 = (String) method3.invoke(aClass.newInstance()/*obj*/, "这本书是我的伙伴!");
    System.out.println(result3);
    System.out.println("---------------------------\n");

    System.out.println(Book.class.getName());
}
           

输出:

我是静态代码块,输出: 《程序猿植发》
class com.junit.demo.Book
我是静态方法printProduceDate,输出: 《程序猿的颈椎自传》, produce is:1626682894095
---------------------------

我是Book声明的构造方法!
得到对象后访问get方法:《程序猿植发》
---------------------------

我是Book声明的构造方法!
《程序猿植发》这本书是我的伙伴!
---------------------------

com.junit.demo.Book
           

三、详解:

1、访问静态方法:

// 由Class获取方法:第一个参数为方法名,第二个参数为方法的参数类型。
// 如add(int a,int b)则getMethod("add",int.class,int.class)。当然,也可以是Java对象。
Method method = aClass.getMethod("printProduceDate", String.class);
// 引用方法:(引用实例/调用静态方法可为null,参数值/有多个用逗号隔开),参数值要和参数类型的数量匹配!
String result = (String) method.invoke(aClass, "《程序猿的颈椎自传》");
           

简写:

2、访问实例方法:

  • 重要:

    aClass.newInstance();

    ,实例化指定对象。

    和 new Book() 效果一样。

//方法一:直接转化实例化后的对象,直接调用方法
Book book= (Book) aClass.newInstance();
// book.setName('xxx'); or book.getName(); or more...

//方法二:使用invoke调用指定实例a的指定方法b
Method method3 = aClass.getMethod("toString", String.class);
//这里的book可以是已实例化的对象,或者使用 aClass.newInstance() 传入,详见简写:
String result3 = (String) method3.invoke(book, "这本书是我的伙伴!");
           

简写:

值得注意的是,如果是类似于工具类可用于全部类访问的,可以使用一个实例化对象,而不需要每次都newInstance。

另外,方法一适用于需要映射的类是已知或少数时,反之需要统一按指定字符串反射调用方法的话,需使用方法二。

end.