一、java.lang.Class类
- java程序编译生成.class文件,.class文件中的内容其实就是.java中的内容,只是.class文件是给JVM看的,一般.class文件中存储的是字节码信息,我们能看懂是因为Idea帮我们反编译了
- java是面向对象的编程语言,任何事物都可以定义创建对象。.class文件也是一种事物,也可以定义类,也可以创建对象。
- java.lang.Class类,就是用来专门描述.class字节码文件的类。Class也是有对象的,只是Class类型的对象,程序员无法创建和销毁,是由JVM帮助我们创建和销毁,但是我们可以获取Class类型的文件,从而完成相关操作。
Class类型的定义
注意区别:
- class:定义类的关键字
- Class:类名称,就是一个普通的类名称
二、类的加载
前言,我们已经知道了java编译以后会生成.class文件供JVM运行,.class文件信息会加载到内存方法区存储.class文件的常量池(静态区)。
- 第一次使用类的信息时,.class字节码文件会被加载到内存,存储在方法区中。
- JVM会为加载到方法区的.class文件创建一个Class类型的对象,该对象被保存在堆内存中。相当于堆内存中的Class类型的对象指向了方法区中的.class文件。
- 一个类只会被加载一次,所以Class类型的对象只会有一个。
- 任意类型(基本类型/引用类型)都有Class类型的对象。
tip:Class可以被比喻为手术刀,反射好比是对类的解刨。
三、反射
反射就是通过获取到Class类型的对象,从而操作.class文件。
java中万物皆对象!
对象 | 类型 | 获取方式 | 执行方式 |
---|---|---|---|
.class文件 | Class | 三种方式 | |
成员变量 | Field | getField(…) / getFields() | set(…) / get(…) |
成员方法 | Method | getMethod(…) / getMethods() | invoke() |
构造方法 | Constructor | getConstructor(…) / getConstructors() | newInstance() |
1、获取.class字节码文件(Class类的对象)
三种方式:
-
java.lang.Object类的成员方法
public Class<?> getClass()
:获取Class类型的对象(获取.class字节码文件) -
使用对象的
class
属性任意类型都有一个隐藏的class属性:获取Class类型的对象
-
java.lang.Class类的静态方法(
建议使用
)public static Class<?> forName(String className)
Class类获取类名的方式
(1)public String getSimpleName()
:只获取类名
(2)public String getName()
:获取包名和类名
2、获取构造方法
2.1 步骤:
-
获取Class类型的对象(建议使用Class类型静态
forName
方法的方式) -
通过Class类型的对象获取构造方法对象
java.lang.Class类 成员方法
(1)public Constructor[] getConstructores()
a. 只能获取
public修饰
的所有构造方法。
b. 每个构造方法被封装成一个Constuctor类型的对象,被存储在数组中注意:Constructor类,专门用来描述构造方法
(2)
Constructor getConstructor(Class... parameterTypes)
a. 根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
b. 如果不存在对应的构造方法,则会抛出java.lang.NoSuchMethodException
异常。注意: 1. 参数是可变参数,调用此方法时,可以不写参数,获取的空参构造 2. 可以写参数,给定的参数必须是Class对象 比如: 参数 String name,int age 调用此方法: String.class,int.class
2.2 执行构造方法
java.lang.reflect.Constructor类的成员方法
public T newInstance(Object ... params)
参数:
Object...params
:可变参数,可以传递数组,参数列表,不传递(执行的就是空参构造)- 参数作用:对象中的成员变量,所需要的的具体数据,不传递使用默认值
返回值:
Object类型
:被创建出来的对象,提升为Object类型- 对比:假设已经获取了满参构造方法con,
使用反射之前:Person p = new Person(“张三”,87);
使用反射之后: Person p = (Person)con.newInstance(“张三”,87)
3、获取成员方法
2.1 步骤:
-
获取Class类型的对象(建议使用Class类型静态
forName
方法的方式) -
通过Class类型的对象获取构造方法对象
java.lang.Class类 成员方法
(1)public Method[] getMethods()
a. 只能获取
public修饰
的所有构造方法。
b. 每个构造方法被封装成一个Method类型的对象,被存储在数组中注意:Method类,专门用来描述成员方法
(2)
Method getMethod(String name, 方法的参数类型... 类型.class)
a. 根据参数类型获取成员方法对象,只能获得public修饰的构造方法。
b. 如果不存在对应的成员方法(参数类型传递不对),则会抛出java.lang.NoSuchMethodException
异常。注意: 1. 参数是可变参数,调用此方法时,可以不写参数,获取的空参构造 2. 可以写参数,给定的参数必须是Class对象 比如: 参数 String name,int age 调用此方法: String.class,int.class
2.2 执行成员方法
java.lang.reflect.Method类的成员方法
public Object invoke(Object obj, Object... args)
参数:
Object
:传递指定的对象(通过构造方法创建的对象)。如果obj=null
,则表示该方法是静态方法Object... args
:可变参数,传递方法的参数。如果参数传递不对会出现java.lang.IllegalArgumentException: wrong number of arguments
异常。
返回值:
基本类型会被包装为对应的包装类型,如果是数组类型需要转型,基本类型不会被包装为Object[]
类型。
public class Animal {
public void eat(){
System.out.println("吃东西");
}
public String[] num(int i){
return new String[]{"1","2"};
}
}
/*
反射案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现:
1. 创建配置文件
2. 反射
步骤:
1.创建一个配置文件(config.properties),存储键值对
2.读取配置文件,获取String类型的类名和方法名称
3.获取Class类型的对象
4.获取构造方法
5.获取成员方法
*/
public class Demo {
public static void main(String[] args) throws Exception {
//1.创建一个配置文件(config.properties),存储键值对
Properties prop = new Properties();
//2.读取配置文件,获取String类型的类名和方法名称
Reader reader = new FileReader("config.properties");
prop.load(reader);
String className = prop.getProperty("ClassName");
//3.获取Class类型的对象
Class<?> c = Class.forName(className);
//4.获取构造方法
Constructor<?> con = c.getConstructor();
Animal animal = (Animal) con.newInstance();
//5.获取成员方法
String methodName = prop.getProperty("MethodName");
Method method = c.getMethod(methodName,int.class);
Object invoke = method.invoke(animal,0);
System.out.println(Arrays.toString((Object[]) invoke));
}
}
config.properties配置文件
# Animal的完整类名(包名 + 类名)
ClassName=com.domain.Animal
# 调用的成员函数名称
MethodName=num
4、总结——反射示意图
一个类可以好比是一个人体,有心肝肺。
类加载器
类加载器是负责加载类的对象。将.class文件(硬盘)加载到内存生成Class对象。
1、类加载器的组成
-
BootstrapClassLoader 根类加载器也被称为引导类加载器,负责Java核心类的加载。比如System,String等。
-
ExtClassLoader 扩展类加载器负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录
-
AppClassLoader 系统类加载器负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
2、继承关系
3、类加载器的获取和调用机制
使用 类.class.getClassLoader()
获得加载自己的类加载器
- 类加载器加载机制:全盘负责委托机制
- 全盘负责:A类如果要使用B类(不存在),A类加载器必须负责加载B类。
- 委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。 如果加载,将直接使用。 如果没有
机制,自己再加载。
采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象
转载:https://blog.csdn.net/weixin_38708854/article/details/106871314