Java反射 Class类和ClassLoader类

在 Java 中,反射机制可以让程序在运行时获取类的信息,并可以动态地创建对象、调用方法、操作属性等。其中,Class 类和 ClassLoader 类是反射机制的核心类,掌握它们的使用是了解反射机制的基础。

Class类
Class 类代表一个类的类型,它可以获取类的信息、属性、方法等,我们可以通过以下三种方式获取类的 Class 对象:
1、使用 .class 语法糖,例如 String.class、Integer.class。

Class<?> clazz = String.class;
System.out.println(clazz);

2、调用对象的 .getClass() 方法,例如 “hello”.getClass()、1.getClass()。

String str = "Hello World";
Class<?> clazz = str.getClass();
System.out.println(clazz);

3、使用 Class.forName() 方法,例如 Class.forName(“java.lang.String”)。

try {
    Class<?> clazz = Class.forName("java.lang.String");
    System.out.println(clazz);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

使用Class类可以获取一个类的信息,包括类名、包名、父类、接口、属性、方法、构造器等等。以下是一些常用的Class类方法:
getName():返回类的完全限定名。
getSimpleName():返回类的简单名称。
getPackage():返回类所在的包。
getSuperclass():返回类的父类。
getInterfaces():返回类实现的接口。
getFields():返回类及其父类中公有的属性。
getDeclaredFields():返回类中所有的属性,包括私有的。
getMethods():返回类及其父类中公有的方法。
getDeclaredMethods():返回类中所有的方法,包括私有的。
getConstructors():返回类中所有的构造器。
getDeclaredConstructors():返回类中所有的构造器,包括私有的。

下面是一些示例代码:

// 获取Class对象的三种方式
Class<String> clazz1 = String.class;
Class<?> clazz2 = "hello".getClass();
Class<?> clazz3 = Class.forName("java.lang.String");

// 获取类的信息
System.out.println(clazz1.getName());
System.out.println(Modifier.toString(clazz1.getModifiers()));
System.out.println(clazz1.getSuperclass().getName());
System.out.println(Arrays.toString(clazz1.getInterfaces()));

// 获取类的属性信息
Field[] fields = clazz1.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

// 获取类的方法信息
Method[] methods = clazz1.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method.getName());
}

// 创建对象
String str = clazz1.newInstance();
System.out.println(str);

// 调用方法
Method lengthMethod = clazz1.getDeclaredMethod("length");
int length = (int) lengthMethod.invoke(str);
System.out.println(length);

ClassLoader类
在 Java 中,每个类都是由一个类加载器(ClassLoader)进行加载的,而 ClassLoader 类就是负责加载类的核心类之一。

Java 中存在三种 ClassLoader 类型:
Bootstrap ClassLoader:也称为启动类加载器,负责加载 JDK 的核心类库,是用原生代码实现的,不是 Java 类,是所有类加载器的父加载器。
Extension ClassLoader:也称为扩展类加载器,负责加载 JDK 的扩展类库,即 jre/lib/ext 目录下的类库。
AppClassLoader:也称为应用程序类加载器,负责加载应用程序类路径(classpath)下的类库,是开发者最常用的加载器。
这三种ClassLoader涉及到一个概念就是双亲委派机制。

ClassLoader 类提供了多种方法来加载类,其中最常用的有以下三种:
loadClass(String name)方法:该方法会加载指定名称的类,该名称必须是全限定类名。它会调用父类加载器的loadClass()方法,如果父类加载器无法加载该类,则会调用该类加载器的findClass()方法进行加载。
findClass(String name)方法:该方法是ClassLoader类的抽象方法,用于根据指定的名称查找类。需要自定义ClassLoader时,需要覆盖该方法。
getResource(String name)方法:该方法用于在类路径下查找具有给定名称的资源。如果该类加载器没有找到该资源,则它会将请求委托给其父级加载器。

以下是一个简单的例子,展示了如何使用ClassLoader加载一个类:

public class MyClassLoader extends ClassLoader {
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if ("com.example.MyClass".equals(name)) {
            byte[] bytes = loadMyClassBytes(); // 加载MyClass的字节码
            return defineClass("com.example.MyClass", bytes, 0, bytes.length);
        }
        return super.loadClass(name);
    }
}

MyClassLoader myClassLoader = new MyClassLoader();
Class<?> myClass = myClassLoader.loadClass("com.example.MyClass");

在这个例子中,我们自定义了一个MyClassLoader类,覆盖了loadClass()方法,如果需要加载的类是com.example.MyClass,则调用自定义的方法进行加载。在loadMyClassBytes()方法中,我们可以使用任意方式获得类的字节码。最后,我们使用defineClass()方法将字节码转换为Class对象。