Подтвердить что ты не робот

Почему класс может загружать собственный внутренний класс другого класса?

У меня

class A {
    private static class B {
        B() {
        }
    }
}

хотя B является частным Я загружаю A $B.class из другого класса без проблем. Почему это разрешено?

class C {
    public static void main(String[] args) throws Exception {
        System.out.println(Class.forName("A$B").newInstance());
    }
}

Выход

[email protected]

UPDATE

Я понимаю, что ограничение на загрузку любых классов выражается намеренно, но должно быть разумное объяснение, почему.

Обратите внимание, что пакет private B {} конструктор существует специально. Если я удалю его, я получу

java.lang.IllegalAccessException: класс B не может получить доступ к члену класса A $B с модификаторами "private"

4b9b3361

Ответ 1

Это поведение соответствует javadoc:

Обратите внимание, что этот метод не проверяет, доступен ли запрашиваемый класс его вызывающему.

Другими словами, forName может обращаться к не доступным классам и не будет бросать IllegalAccessExcessException. И поскольку конструктор является приватным пакетом, то есть (я предполагаю), доступным из вашего местоположения вызова, newInstance также не генерирует никаких исключений.

Если конструктор недоступен (либо потому, что вы перемещаете его на другой пакет, либо закрываете его), newInstance генерирует исключение.

Относительно вашего обновления, если вы удалите конструктор, вы вызовете конструктор по умолчанию, который будет иметь тот же доступ, что и класс, в этом случае private (cf JLS 8.8.3: если класс объявлен приватным, то конструктор по умолчанию неявно получает доступный модификатор доступа).

Ответ 2

Класс loader может загружать любой класс, но правила доступа применяются при создании экземпляра.

Итак, почему ваш код способен создавать экземпляр класса?

Внутренние классы - это большой взлом на уровне байтового кода, потому что они были реализованы как байт-код, совместимый с Java 1.0.

Таким образом, этот частный внутренний класс на уровне исходного кода на самом деле защищен пакетом на уровне байтового кода. Вы можете убедиться, что перемещая класс C в другой пакет и делая конструктор B public:

Exception in thread "main" java.lang.IllegalAccessException: 
Class something.C can not access a member of class A$B with modifiers "public"

Отражение позволяет переопределять правила доступа с помощью setAccessible(), но здесь это не так.

Ответ 3

Отражение обычно используется программами, для которых требуется изучить или изменить поведение среды выполнения приложений на виртуальной машине Java.

Он инструмент, который позволяет разбить некоторые правила, можно бросить его на ногу или правильно использовать.

Отражение обеспечивает механизм доступа к информации о классах с помощью средств, которые обычно недоступны при регулярном взаимодействии между классами и объектами.

Одним из таких способов является доступ к закрытым полям из внешних классов и объекты.

Тем не менее, как getDeclaredField(), так и setAccessible() на самом деле проверяется менеджером безопасности и генерирует исключение, когда вашему коду не разрешено это делать. Многие рефлексивные трюки могут не работать с активным менеджером безопасности.

Ответ 4

В то время как отражения - хорошее сравнение, загрузчик классов выходит за рамки того, что может сделать библиотека отражения. Например, загрузчик классов может создавать классы, которые вы не можете сделать с отражениями. Он также может

  • инициирует создание экземпляров enum.
  • дает ссылку на класс до его инициализации.
  • "переопределить" класс в загрузчике родительского класса.
  • классы триггеров выгружаются при его выгрузке.
  • дает вам байтовый код для классов, предоставляя вам необработанные детали реализации.
  • дает вам поток для ресурсов некласса в пути к классу.

Подобно отражениям, он может загружать класс с частными членами, и это было бы бессмысленно, если бы оно не могло.

Ответ 5

Использование отражения Даже вы можете изменить модификаторы

class MyClass{
private int value;
}

Field value = MyClass.class.getDeclaredField("value");
value.setAccessible(true);