Java 14 Отражение головоломки в классе в не экспортированном пакете не запрещает доступ - PullRequest
0 голосов
/ 03 мая 2020

Я играю с Jigsaw, у меня есть простой воспроизводимый код.

package university.harvard;

public class Pilot{
    public static void main(final String args[]){
        callInNotAExportedPackage();
    }
    private static void callInNotAExportedPackage(){
        try{
            final Class<?>classy = Class.forName("javax.swing.JButton");
            System.out.println(classy.newInstance());
        }catch(final Exception e){
            e.printStackTrace();
        }
    }           

}

У меня есть module-info. java вот так.

module John{
    exports university.harvard;
}

Я могу скомпилировать модуль этой командой.

C:\Ocp11>javac -d out --module-source-path src -m John

Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

Я получаю сообщения об устаревании, но компиляция прошла успешно. На этом этапе я говорю хорошо, что компилятор не знает, что я попытаюсь вызвать класс в неэкспортированном пакете.

И когда я запускаю модуль

C:\Ocp11>java -p out -m John/university.harvard.Pilot

, я вижу, что экземпляр извлекается отражением без проблем.

javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.
swing.plaf.BorderUIResource$CompoundBorderUIResource@3498ed,flags=296,maximumSiz
e=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,
margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintB
order=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rollo
verSelectedIcon=,selectedIcon=,text=,defaultCapable=true]

Но что это? Я думал, что Jigsaw заблокирует меня

Если я добавлю полное имя класса в код, подобный этому.

final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);         

И на этот раз Jigsaw реагирует правильно.

C:\Ocp11>javac -d out --module-source-path src -m John
src\John\Pilot.java:10: error: package javax.swing is not visible
            final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
                       ^
  (package javax.swing is declared in module java.desktop, but module John does
not read it)
src\John\Pilot.java:10: error: package javax.swing is not visible
            final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
                                                     ^
  (package javax.swing is declared in module java.desktop, but module John does
not read it)
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors

Но я не ставлю полностью определенное имя класса, которое могу обойти Jigsaw: (

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

Я использую

C:\Ocp11>java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

1 Ответ

1 голос
/ 05 мая 2020

Заголовок вопроса гласит:

Java 14 Пазл-отражение в классе в не экспортированном пакете не запрещает доступ

Но на самом деле, класс, который вы ' пытается получить доступ к is в экспортированном пакете. Класс javax.swing.JButton находится в пакете javax.swing модуля java.desktop, и этот пакет экспортируется:

// [License & Javadoc]
module java.desktop {
    // [other exports and requires]
    exports javax.swing;
    // [other exports, opens, uses and provides]
}

Поскольку модуль доступен во время выполнения, Class.forName("javax.swing.JButton") будет , а не сгенерировать исключение (в противном случае было бы выброшено java.lang.ClassNotFoundException). Он, вероятно, доступен, потому что это модуль root (см. здесь и здесь ).

Даже если класс не будет в экспортированном пакете, это будет работать:

Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run

Однако (javax.swing.JButton)classy.newInstance(); не скомпилируется - не потому, что пакет не экспортирован, а потому, что ваш модуль, John, не читает его. Чтобы это работало, ваш модуль должен иметь requires java.desktop;, например:

module John {
    requires java.desktop;

    exports university.harvard;
}

Таким образом, модуль John сможет читать все экспортированные пакеты java.desktop.

Вызов classy.newInstance(); (без приведения типа) скомпилируется, потому что компилятор не знает тип нового экземпляра.

Вот несколько примеров того, что будет работать во время выполнения и во время компиляции и почему (нет) :

// (1) will compile and run:
Class<?> curveClass = Class.forName("sun.java2d.marlin.Curve");

// (2) will compile but not run, even if the module `requires java.desktop;`:
Object curveObject = curveClass.newInstance();

// (3) will not compile and not run, even if the module has `requires java.desktop;`:
sun.java2d.marlin.Curve curve = (sun.java2d.marlin.Curve) curveClass.newInstance();

// (4) will compile and run:
Class<?> jButtonClass = Class.forName("javax.swing.JButton");

// (5) will compile and run, even if the module does not have `requires java.desktop;`:
Object jButtonObject = jButtonClass.newInstance();

// (6) will only compile if the module `requires java.desktop;`:
javax.swing.JButton jButton = (javax.swing.JButton) jButtonClass.newInstance();
  1. Содержит только информацию / характеристики класса и ничего более, поэтому этот тип доступа в порядке.
  2. Компилируется, потому что компилятор не знает, что новый экземпляр имеет тип sun.java2d.marlin.Curve. Однако во время выполнения доступ будет заблокирован.
  3. даже не скомпилируется, поскольку компилятор знает тип и, следовательно, знает, что доступ будет недопустимым.
  4. То же, что (1)
  5. Компилируется по той же причине, что и с (2), и запускается, потому что пакет фактически экспортируется
  6. Компилируется, только если модуль имеет requires java.desktop;, поскольку компилятор знает тип
...