一、前言:
在说明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.