繁体   English   中英

为什么lambda class加载不了?

[英]Why can't lambda class load?

为什么运行以下代码时会出现异常? 去看源码发现这个class是ASM生成的。 这样生成的class不是在方法区吗?

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<? extends Runnable> runClass = test().getClass();
        ClassLoader classLoader = runClass.getClassLoader();

        System.out.println(classLoader.loadClass(Main.class.getName()));
        System.out.println(classLoader.loadClass(runClass.getName()));
    }

    public static Runnable test() {
        return () -> {};
    }
}

结果

class link.yxw.Main
Exception in thread "main" java.lang.ClassNotFoundException: link.yxw.Main$$Lambda$1/1324119927
    at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at link.yxw.Main.main(Main.java:10)

简短的回答是:因为 lambda class 是隐藏的(JDK15 中的JEP 371 对其进行了标准化并暴露了 API,但由于 JDK 已被其他类访问且无法访问) 您可以在类似这样的博文和文章中找到更多信息。


如果我们定义:

public static Runnable lambdaTest() {
    return () -> {
    };
}

public static Runnable anonymTest() {
    return new Runnable() {
        @Override
        public void run() {
        }
    };
}

并运行:

System.out.println(Main.class.isHidden());
System.out.println(anonymTest().getClass().isHidden());
System.out.println(lambdaTest().getClass().isHidden());

我们会得到:

false
false
true

如果我们打印 lambda class 的名称,我们将在不同的 JDK 上得到不同的结果,但都归结为相同的概念:

JDK 8-10: java.lang.ClassNotFoundException: Main$$Lambda$1/791452441
JDK 11+:  java.lang.ClassNotFoundException: Main$$Lambda$1/0x0000000800060840

Class#getName() JavaDoc说:

如果 class 或接口被隐藏,则结果为以下形式的字符串: N + '/' + <suffix> 其中 N 是传递给 Lookup::defineHiddenClass 的 class 文件指示的二进制名称,并且是非限定名称.

如果您想查看实现: 可以在此处找到JDK 9 的本机 JDK 代码(这里是镜像类的简短描述), 这里是 JDK 18 代码(自 15 年以来一直存在)。 如果您想阅读更多内容,我推荐这个 SO answer


最后让我们看看查找 class 进行加载的ClassLoader代码:

protected final Class<?> findLoadedClass(String name) {
    if (!checkName(name))
        return null;
    return findLoadedClass0(name);
}

// true if the name is null or has the potential to be a valid binary name
private static boolean checkName(String name) {
    if ((name == null) || (name.isEmpty()))
        return true;
    if ((name.indexOf('/') != -1) || (name.charAt(0) == '['))
        return false;
    return true;
}

如果 class 名称包含/字符,则将其解析为无效的二进制名称并且无法加载。 如果您想知道[字符 - 它标记了一个数组 class。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM