JDK11如何从指定包加载所有类?已尝试方法遇迭代查找问题
在JDK11中检索指定包下的类
首先,你提到的ClassLoader.getSystemClassLoader().getDefinedPackages()方法返回的是Package[]数组,本身是可以迭代的,比如:
Package[] loadedPackages = ClassLoader.getSystemClassLoader().getDefinedPackages(); for (Package pkg : loadedPackages) { System.out.println("已加载的包:" + pkg.getName()); }
但这个方法只能获取已经被类加载器加载过的包,无法主动扫描指定包下的所有类,也无法查找未加载的类——因为JDK类加载器是按需加载类的,不会预先扫描所有类文件。
要实现指定包下的类检索,JDK11本身没有提供直接的API,需要自己实现扫描逻辑,或者借助第三方库。以下是原生JDK的实现方案:
原生扫描实现(支持文件系统与Jar包)
下面的工具类可以扫描指定包下的所有类,涵盖文件系统目录和Jar包两种常见场景:
import java.io.File; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class PackageScanner { public static List<Class<?>> scanClasses(String packageName) throws IOException, ClassNotFoundException { List<Class<?>> classList = new ArrayList<>(); String packagePath = packageName.replace('.', '/'); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration<URL> resources = classLoader.getResources(packagePath); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); switch (resource.getProtocol()) { case "file": scanFileSystemDirectory(new File(resource.getFile()), packageName, classList); break; case "jar": scanJarFile((JarURLConnection) resource.openConnection(), packagePath, packageName, classList); break; // 可扩展支持其他协议,如jrt(模块化JDK的模块路径) } } return classList; } private static void scanFileSystemDirectory(File dir, String packageName, List<Class<?>> classList) throws ClassNotFoundException { if (!dir.exists() || !dir.isDirectory()) return; File[] files = dir.listFiles(); if (files == null) return; for (File file : files) { if (file.isDirectory()) { scanFileSystemDirectory(file, packageName + "." + file.getName(), classList); } else if (file.getName().endsWith(".class")) { String className = packageName + "." + file.getName().substring(0, file.getName().length() - 6); classList.add(Class.forName(className)); } } } private static void scanJarFile(JarURLConnection conn, String packagePath, String packageName, List<Class<?>> classList) throws IOException, ClassNotFoundException { try (JarFile jarFile = conn.getJarFile()) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); // 过滤指定包下的.class文件,排除内部类(可根据需求调整) if (entryName.startsWith(packagePath) && entryName.endsWith(".class") && !entryName.contains("$")) { String className = entryName.replace('/', '.').substring(0, entryName.length() - 6); classList.add(Class.forName(className)); } } } } }
使用示例
public class Main { public static void main(String[] args) throws Exception { // 扫描java.util包下的所有类 List<Class<?>> classes = PackageScanner.scanClasses("java.util"); for (Class<?> cls : classes) { System.out.println("找到类:" + cls.getName()); } } }
注意事项
- 该方法会加载所有找到的类,可能带来一定性能开销,建议按需使用。
- 对于JDK11的模块化环境(模块路径下的模块),需要额外处理
jrt协议的资源,可扩展上述代码的协议分支。 - 内部类(文件名带
$)默认被排除,可根据需求修改过滤条件。
内容的提问来源于stack exchange,提问作者Steve M
相关产品推荐
相关产品推荐

