新增的类以及常用的方法有哪些?

类加载机制核心类与常用方法(详细说明 + 代码示例)

在 Java 类加载机制中,核心操作依赖 java.lang.Class(类元数据入口)、java.lang.ClassLoader(类加载器基类)及其子类。这些类提供了丰富的 API,用于获取类元数据、手动加载类、反射操作、验证类加载信息等。本文将详细介绍核心类的常用方法,结合场景化代码说明其用法。

一、核心类概述

核心类 作用
java.lang.Class 堆中代表类的元数据对象,是访问方法区类信息的唯一入口,支持反射操作。
java.lang.ClassLoader 类加载器基类,定义类加载的核心逻辑(如 loadClass),支持自定义加载规则。
子类(系统类加载器) AppClassLoader(应用类加载器)、ExtClassLoader(扩展类加载器),负责默认类加载。

二、java.lang.Class 类(核心中的核心)

Class 类是所有类的 "元类",每个被加载的类在堆中只会生成一个 Class 实例。其方法主要用于获取类信息、实例化对象、反射调用方法 / 字段等。

1. 获取 Class 对象的 3 种核心方式(必须掌握)

这是所有反射和类加载操作的前提,3 种方式对应不同场景:

java 复制代码
public class ClassGetDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 方式1:通过类名.class(编译期确定,不触发类初始化,仅加载)
        Class<Parent> class1 = Parent.class;
        System.out.println("方式1:" + class1.getName()); // 输出:Parent

        // 方式2:通过实例对象.getClass()(运行期确定,类已加载并初始化)
        Parent parent = new Parent();
        Class<? extends Parent> class2 = parent.getClass();
        System.out.println("方式2:" + class2.getName()); // 输出:Parent

        // 方式3:通过Class.forName(全限定名)(运行期动态加载,触发类初始化)
        Class<?> class3 = Class.forName("Parent"); // 全限定名=包名+类名(无包则直接写类名)
        System.out.println("方式3:" + class3.getName()); // 输出:Parent

        // 验证:3种方式获取的是同一个Class对象(单例)
        System.out.println("class1 == class2:" + (class1 == class2)); // true
        System.out.println("class1 == class3:" + (class1 == class3)); // true
    }
}

class Parent {
    static {
        System.out.println("Parent 初始化(仅方式3触发)");
    }
}
关键区别:
  • 方式 1(类名.class):编译期解析,不触发类初始化,仅加载类;
  • 方式 2(对象.getClass()):运行期获取,类已实例化(必然已初始化);
  • 方式 3(Class.forName()):动态加载(支持字符串拼接类名),默认触发类初始化 (可通过重载方法关闭:Class.forName(name, false, classLoader))。

2. 常用核心方法(按功能分类)

(1)获取类的基本信息
java 复制代码
public class ClassInfoDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("Child");

        // 1. 类名相关
        System.out.println("全限定名:" + clazz.getName()); // 输出:Child
        System.out.println("简单类名:" + clazz.getSimpleName()); // 输出:Child
        System.out.println("类的修饰符:" + java.lang.reflect.Modifier.toString(clazz.getModifiers())); // 输出:public

        // 2. 父类与接口
        System.out.println("父类:" + clazz.getSuperclass().getName()); // 输出:Parent
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println("实现的接口数:" + interfaces.length); // 若未实现接口则为0

        // 3. 类的类型判断
        System.out.println("是否为接口:" + clazz.isInterface()); // false
        System.out.println("是否为数组:" + clazz.isArray()); // false
        System.out.println("是否为基本类型:" + clazz.isPrimitive()); // false(int.class才是true)
        System.out.println("是否为枚举:" + clazz.isEnum()); // false
    }
}

class Parent {}
class Child extends Parent implements Runnable {
    @Override
    public void run() {}
}
(2)实例化对象(反射创建实例)
java 复制代码
public class ClassNewInstanceDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz = Class.forName("User");

        // 方式1:调用无参构造器(要求无参构造器存在且可访问)
        User user1 = (User) clazz.newInstance(); // JDK9+已过时,推荐方式2
        System.out.println("user1:" + user1); // 输出:User{name='null', age=0}

        // 方式2:通过Constructor调用构造器(支持有参、私有构造器)
        // 2.1 调用有参构造器(String, int)
        Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
        User user2 = (User) constructor.newInstance("张三", 20);
        System.out.println("user2:" + user2); // 输出:User{name='张三', age=20}

        // 2.2 调用私有构造器(setAccessible(true)打破访问限制)
        Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
        privateConstructor.setAccessible(true); // 允许访问私有构造器
        User user3 = (User) privateConstructor.newInstance("李四");
        System.out.println("user3:" + user3); // 输出:User{name='李四', age=0}
    }
}

class User {
    private String name;
    private int age;

    // 无参构造器(方式1依赖)
    public User() {}

    // 有参构造器(方式2.1)
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 私有构造器(方式2.2)
    private User(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}
(3)获取类的字段(静态 / 成员变量)
java 复制代码
public class ClassFieldDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class<?> clazz = Class.forName("User");
        User user = (User) clazz.newInstance();

        // 1. 获取public字段(包括父类)
        Field publicField = clazz.getField("publicAge"); // 要求字段是public
        publicField.set(user, 25); // 给实例赋值
        System.out.println("publicAge:" + publicField.get(user)); // 输出:25

        // 2. 获取当前类的所有字段(包括private,不包括父类)
        Field privateField = clazz.getDeclaredField("name");
        privateField.setAccessible(true); // 打破private限制
        privateField.set(user, "王五"); // 给私有字段赋值
        System.out.println("私有name:" + privateField.get(user)); // 输出:王五

        // 3. 获取静态字段(类级别的字段,无需实例)
        Field staticField = clazz.getDeclaredField("staticName");
        staticField.setAccessible(true);
        System.out.println("静态字段原值:" + staticField.get(null)); // 静态字段用null作为参数
        staticField.set(null, "静态修改后"); // 修改静态字段
        System.out.println("静态字段新值:" + staticField.get(null)); // 输出:静态修改后

        // 4. 获取字段的详细信息
        System.out.println("字段类型:" + privateField.getType().getName()); // 输出:java.lang.String
        System.out.println("字段修饰符:" + Modifier.toString(privateField.getModifiers())); // 输出:private
    }
}

class User {
    private String name;
    public int publicAge;
    private static String staticName = "静态初始值";

    public User() {}
}
(4)获取类的方法(静态 / 成员方法)
java 复制代码
public class ClassMethodDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> clazz = Class.forName("User");
        User user = (User) clazz.newInstance();

        // 1. 调用public成员方法(无参)
        Method publicMethod = clazz.getMethod("show");
        publicMethod.invoke(user); // 输出:User.show() 执行

        // 2. 调用public成员方法(有参)
        Method publicParamMethod = clazz.getMethod("sayHello", String.class);
        String result = (String) publicParamMethod.invoke(user, "赵六");
        System.out.println("方法返回值:" + result); // 输出:Hello, 赵六

        // 3. 调用private成员方法
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(user); // 输出:User.privateMethod() 执行

        // 4. 调用静态方法(无需实例,参数传null)
        Method staticMethod = clazz.getMethod("staticMethod");
        staticMethod.invoke(null); // 输出:User.staticMethod() 执行

        // 5. 获取方法的详细信息
        System.out.println("方法参数类型:" + Arrays.toString(publicParamMethod.getParameterTypes())); // 输出:[class java.lang.String]
        System.out.println("方法返回值类型:" + publicParamMethod.getReturnType().getName()); // 输出:java.lang.String
    }
}

class User {
    public void show() {
        System.out.println("User.show() 执行");
    }

    public String sayHello(String name) {
        return "Hello, " + name;
    }

    private void privateMethod() {
        System.out.println("User.privateMethod() 执行");
    }

    public static void staticMethod() {
        System.out.println("User.staticMethod() 执行");
    }
}
(5)类加载相关方法
java 复制代码
public class ClassLoaderRelatedDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("User");

        // 1. 获取加载当前类的类加载器
        ClassLoader classLoader = clazz.getClassLoader();
        System.out.println("类加载器:" + classLoader); // 输出:sun.misc.Launcher$AppClassLoader@18b4aac2

        // 2. 判断类是否已初始化(JDK9+)
        // System.out.println("是否已初始化:" + clazz.isInitialized()); // true

        // 3. 强制初始化类(仅当未初始化时生效)
        clazz.initialize(); // 若已初始化,调用无效果
    }
}

三、java.lang.ClassLoader 类(类加载器核心)

ClassLoader 是抽象类,所有类加载器(系统类加载器、自定义类加载器)都继承自它。其核心方法用于加载类、获取父加载器、访问类路径等。

1. 核心方法(按功能分类)

(1)加载类的核心方法
java 复制代码
public class ClassLoaderLoadDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 获取系统类加载器(应用程序类加载器)
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器:" + appClassLoader); // AppClassLoader

        // 2. 加载类(遵循双亲委派模型)
        // 方式1:loadClass(String 全限定名):默认不触发类初始化(仅加载、验证、准备)
        Class<?> clazz1 = appClassLoader.loadClass("User");
        System.out.println("loadClass加载的类:" + clazz1.getName()); // User

        // 方式2:Class.forName():默认触发初始化(底层调用ClassLoader的loadClass)
        Class<?> clazz2 = Class.forName("User");
        System.out.println("forName加载的类:" + clazz2.getName()); // User

        // 3. 获取父加载器(验证双亲委派模型)
        ClassLoader extClassLoader = appClassLoader.getParent();
        System.out.println("应用类加载器的父加载器:" + extClassLoader); // ExtClassLoader
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println("扩展类加载器的父加载器:" + bootstrapClassLoader); // null(C++实现)

        // 4. 加载核心类(由启动类加载器加载)
        Class<?> stringClass = Class.forName("java.lang.String");
        System.out.println("String类的加载器:" + stringClass.getClassLoader()); // null(启动类加载器)
    }
}
关键说明:
  • loadClass(String name):核心加载方法,遵循双亲委派模型,默认不触发类初始化(仅完成 "加载→验证→准备");
  • findClass(String name):子类重写此方法,实现自定义加载逻辑(如加载网络字节码、加密字节码),默认抛出 ClassNotFoundException
  • defineClass(byte[] b, int off, int len):将字节数组转化为 Class 对象(最终加载类的核心步骤),子类不能直接调用(需通过 findClass 间接调用)。
(2)获取资源与类路径
java 复制代码
public class ClassLoaderResourceDemo {
    public static void main(String[] args) {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();

        // 1. 获取类路径下的资源(如配置文件)
        // 相对路径:从classpath根目录开始(src/main/resources下的文件)
        URL resource = classLoader.getResource("config.properties");
        if (resource != null) {
            System.out.println("资源路径:" + resource.getPath());
        } else {
            System.out.println("未找到资源config.properties");
        }

        // 2. 获取所有资源(支持通配符)
        try {
            Enumeration<URL> resources = classLoader.getResources("config.properties");
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                System.out.println("找到资源:" + url.getPath());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 获取资源的输入流(直接读取资源内容)
        try (InputStream is = classLoader.getResourceAsStream("config.properties")) {
            if (is != null) {
                // 读取配置文件(示例:按行读取)
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println("配置内容:" + line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
(3)自定义类加载器示例(重写 findClass)

自定义类加载器的核心是重写 findClass 方法,实现自定义加载逻辑(如下示例加载本地磁盘上的加密字节码):

java 复制代码
/**
 * 自定义类加载器:加载本地磁盘上的.class文件
 */
public class CustomClassLoader extends ClassLoader {
    // 自定义类加载路径(本地磁盘目录)
    private String classPath;

    public CustomClassLoader(String classPath) {
        this.classPath = classPath;
    }

    // 重写findClass:实现自定义加载逻辑
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        try {
            // 1. 将类名转为文件路径(com.example.User → com/example/User.class)
            String filePath = classPath + File.separator + className.replace(".", File.separator) + ".class";

            // 2. 读取.class文件的字节数组
            byte[] classBytes = loadClassBytes(filePath);

            // 3. 将字节数组转为Class对象(核心方法)
            return defineClass(className, classBytes, 0, classBytes.length);
        } catch (IOException e) {
            throw new ClassNotFoundException("类加载失败:" + className, e);
        }
    }

    // 读取文件字节数组
    private byte[] loadClassBytes(String filePath) throws IOException {
        File file = new File(filePath);
        try (InputStream is = new FileInputStream(file);
             ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        }
    }

    // 测试自定义类加载器
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 本地.class文件所在目录(示例:D:/customClasses)
        String classPath = "D:/customClasses";
        CustomClassLoader customLoader = new CustomClassLoader(classPath);

        // 加载自定义路径下的类(全限定名)
        Class<?> clazz = customLoader.loadClass("com.example.Test");
        System.out.println("加载的类:" + clazz.getName()); // com.example.Test
        System.out.println("类加载器:" + clazz.getClassLoader()); // CustomClassLoader@xxx

        // 实例化对象(反射)
        Object obj = clazz.newInstance();
        System.out.println("实例对象:" + obj);
    }
}
自定义类加载器的应用场景:
  • 加载加密的 .class 文件(在 loadClassBytes 中解密);
  • 加载网络上的 .class 文件(从 HTTP 接口获取字节码);
  • 热部署(重新加载修改后的类)。

四、常用方法总结(按场景归类)

应用场景 核心类 常用方法
获取 Class 对象 Class 类名.class对象.getClass()Class.forName()
类信息查询(名称、父类) Class getName()getSuperclass()getModifiers()
实例化对象 Class/Constructor newInstance()Constructor.newInstance()
操作字段(读 / 写) Class/Field getField()getDeclaredField()set()get()
调用方法 Class/Method getMethod()getDeclaredMethod()invoke()
类加载 ClassLoader loadClass()findClass()defineClass()
获取类加载器 Class/ClassLoader getClassLoader()getParent()getSystemClassLoader()
访问资源文件 ClassLoader getResource()getResourceAsStream()

五、关键注意事项

  1. 访问权限 :通过 getDeclaredField()/getDeclaredMethod() 获取私有字段 / 方法后,需调用 setAccessible(true) 打破访问限制;
  2. 双亲委派模型ClassLoader.loadClass() 遵循双亲委派,自定义类加载器重写 findClass() 而非 loadClass(),避免破坏双亲委派;
  3. 类的唯一性 :同一个类由不同类加载器加载,会被 JVM 视为不同类(equals() 返回 false);
  4. 初始化触发Class.forName() 默认触发类初始化,ClassLoader.loadClass() 默认不触发,需根据场景选择。

掌握这些核心类和方法,不仅能深入理解类加载机制,还能灵活运用反射实现复杂功能(如框架中的依赖注入、配置解析)。

相关推荐
七颗糖很甜8 分钟前
“十五五”气象发展规划:聚焦五大核心任务
大数据·python·算法
爱码小白18 分钟前
Python 异常处理 完整学习笔记
开发语言·python
c++之路32 分钟前
C++20概述
java·开发语言·c++20
Championship.23.2437 分钟前
Linux Top 命令族深度解析与实战指南
java·linux·服务器·top·linux调试
芝士就是力量啊 ೄ೨1 小时前
Python如何编写一个简单的类
开发语言·python
橘子海全栈攻城狮1 小时前
【最新源码】养老院系统管理A013
java·spring boot·后端·web安全·微信小程序
胖虎喜欢静香1 小时前
从零到一快速实现 Mini DeepResearch
人工智能·python·开源
逻辑驱动的ken1 小时前
Java高频面试考点18
java·开发语言·数据库·算法·面试·职场和发展·哈希算法
MoonBit月兔1 小时前
「Why MoonBit 」第一期——Singularity Note AI 学习助手
开发语言·人工智能·moonbit
qq_392690661 小时前
Redis怎样应对Redis集群整体宕机带来的雪崩
jvm·数据库·python