2014-10-23 22 views
4

Я получил два примера:Когда класс загружен в JVM

Пример 1:

public class A { 

} 

public class B { 

    public void m(A a) { 

    } 

} 
public class C { 

    public static void main(String[] args) { 
      B b = new B(); 
      System.out.println("hello!"); 
    } 

} 

компилировать все три класса. Удалите A.class. Запустите main. Никакое исключение не выбрасывается.

Пример 2:

public class D { 

} 

public class E { 

    public void omg(D d) { 

    } 

    public static void main(String[] args) { 
     E e = new E(); 
    } 


} 

Обобщение классов. Удалите D.class. Выполнить основной метод.

Exception in thread "main" java.lang.NoClassDefFoundError: D 
    at java.lang.Class.getDeclaredMethods0(Native Method) 
    at java.lang.Class.privateGetDeclaredMethods(Unknown Source) 
    at java.lang.Class.getMethod0(Unknown Source) 
    at java.lang.Class.getMethod(Unknown Source) 
    at sun.launcher.LauncherHelper.getMainMethod(Unknown Source) 
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source) 
Caused by: java.lang.ClassNotFoundException: D 
    at java.net.URLClassLoader$1.run(Unknown Source) 

Почему? D никогда не упоминается.

+2

Отличный вопрос!Думаю, что смотреть на выход «javap» было бы хорошим началом, чтобы узнать, что на самом деле выполняется, чтобы убедиться, что компилятор не оптимизировал некоторые вызовы. Это, вероятно, содержит ответ: http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.2 –

+0

Класс не используется, но на него ссылаются. Вы используете его как аргумент метода. Класс сканируется для всех типов, которые ему нужны (в основном, для всего импорта), и они будут загружены в загрузчик классов. Вы можете видеть, когда классы загружаются, запустив JVM с параметром '-verbose: class'. –

+2

@ M.Deinum и в первом примере? – user1745356

ответ

1

Ваш класс имеет ссылку на класс D в рамках метода public void omg(D d).

Обычно JVM от Sun/Oracle использует ленивое разрешение, поэтому это не имеет значения, если вы не используете этот метод, однако вы можете видеть из трассировки стека, что поиск основного метода осуществляется с помощью отражающей операция Class.getMethod, которая делает разницу.

Вы можете проверить это с помощью следующего кода:

public class B { 

    public static void main(String[] args) { 
    D.main(args); 
    } 
} 
class D { 

    public void foo(E e) {} 
    public static void main(String[] args) { 
    System.out.println("hello world"); 
    } 
} 
class E { } 

Здесь, удаление E.class после компиляции и запуска java B не будет выдавать ошибку. Теперь вставьте отражающий метод поиск в код:

public class B { 

    public static void main(String[] args) { 
    try { 
     D.class.getMethod("main", String[].class); 
    } catch(NoSuchMethodException ex) { 
     ex.printStackTrace(); 
    } 
    D.main(args); 
    } 
} 
class D { 

    public void foo(E e) {} 
    public static void main(String[] args) { 
    System.out.println("hello world"); 
    } 
} 
class E { } 

Теперь, убрав класс E после компиляции производит java.lang.NoClassDefFoundError: E при запуске с java B. Таким образом, поиск по ручному запуску метода воспроизводит поведение, как в вашем примере исходного кода, хотя класс D не является классом main.


Обратите внимание, что вы можете решить эту проблему путем удаления модификатора public из метода foo. Причина в том, что Class.getMethod рассматривает только public методы и пропускает все остальные.

Это также относится к вашему исходному коду примера: удаление public по методу omg приведет к исчезновению NoClassDefFoundError.

4

Оба допускаются спецификацией JavaVM. В Chapter 5. Loading, Linking, and Initializing мы имеем:

Например, реализация Java Virtual Machine может выбрать для решения каждой символической ссылки в классе или интерфейсе индивидуально, когда он используется («ленивым» или «поздно» разрешение) или разрешить их все сразу, когда класс проверяется («нетерпеливое» или «статическое» разрешение).

Я полагаю, что Sun/Oracle выбрали «статическое» разрешение для начального («основного») класса, потому что, скорее всего, методы в основном классе должны быть вызваны очень скоро.

+1

Либо это, либо он начинает разрешаться после вызова первого метода класса. –

+1

Нет, разрешение ведет себя так же, как и со всеми другими классами. См. [Мой ответ] (http://stackoverflow.com/a/26528180/2711488) ... – Holger

Смежные вопросы