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



三、详解:

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

getMethod()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。

getDeclaredMethod()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。

详细举例:

Class aClass = Class.forName("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, "《程序猿的颈椎自传》");

简写:

aClass.getMethod("printProduceDate", String.class).invoke(null, "《程序猿的颈椎自传》");
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, "这本书是我的伙伴!");

简写:

aClass.getMethod("toString", String.class).invoke(aClass.newInstance(), "xxx");

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

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

3、访问私有方法:

一睹为快:


举例:

package com.junit.demo;

public class Netbar {
    private Double random = Math.random();

    public Double getRandom() {
        return random;
    }

    private Netbar createNew() {
        return new Netbar();
    }

    private static Netbar getInstance() {
        return new Netbar();
    }

    public static void main(String[] args) {
        Netbar netbar = new Netbar();
        System.out.println("Random:: " + netbar.getRandom());
        Netbar netbar2 = netbar.createNew();
        System.out.println("Random2:: " + netbar2.getRandom());
        Netbar netbar3 = Netbar.getInstance();
        System.out.println("Random3:: " + netbar3.getRandom());
    }
}

结果为打印的3个值都是不同的,这是正常的访问方式,下面用反射来访问。

  • 获取私有非静态方法:
public static void main(String[] args) throws Exception {
    Class aClass = Class.forName("com.junit.demo.Netbar");
    Method method = aClass.getDeclaredMethod("createNew");//这里可以带参数
    method.setAccessible(true);//对所有属性设置访问权限  当类中的成员变量为private时 必须设置此项
    Netbar netbar = (Netbar) method.invoke(aClass.newInstance());
    System.out.println("Random2:: " + netbar.getRandom());
}
  • 获取私有静态方法:
public static void main(String[] args) throws Exception {
    Class aClass = Class.forName("com.junit.demo.Netbar");
    Method method = aClass.getDeclaredMethod("getInstance");//这里可以带参数
    method.setAccessible(true);//对所有属性设置访问权限  当类中的成员变量为private时 必须设置此项
    Netbar netbar = (Netbar) method.invoke(null);
    System.out.println("Random2:: " + netbar.getRandom());
}






end.

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值