簡體   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