专栏首页码匠的流水账java9 opens与exports的区别

java9 opens与exports的区别

本文主要研究下java9 opens与exports的区别

open及exports

open

  • open module 主要用于解决deep reflection问题,open的作用是表示该模块下的所有的包在runtime都允许deep reflection(包括public及private类型) 但是编译时期,仅仅允许该module中声明过exports的包可以访问,如果没有exports则该包的类在编译时期不可读
  • opens package 用于声明该模块的指定包在runtime允许使用反射访问

exports

表示允许在编译时和运行时访问指定包的public成员

open及exports对反射的影响

反射方法

  • 目标类 package com.packt.lib.sub1; public class Sub1Service { public Sub1Service() { System.out.println("Sub1Service being instanced"); } public void publicMethod() { System.out.println("public method called!"); } protected void protectedMethod(){ System.out.println("protected method called..."); } private void privateMethod(){ System.out.println("private method called..."); } }
  • 访问类名反射 Sub1Service sub1Service = new Sub1Service(); Method privateMethod = sub1Service.getClass().getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(sub1Service);
  • 通过包名反射 Optional<Module> optional = ModuleLayer.boot().findModule("packt.lib"); Class clz = Class.forName(optional.get(),"com.packt.lib.sub1.Sub1Service"); Object sub1 = clz.newInstance(); System.out.println(sub1.getClass().getMethods()); Method publicMethod = sub1.getClass().getDeclaredMethod("publicMethod"); publicMethod.invoke(sub1); Method protectedMethod = sub1.getClass().getDeclaredMethod("protectedMethod"); protectedMethod.setAccessible(true); protectedMethod.invoke(sub1); Method privateMethod = sub1.getClass().getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(sub1);

没有exports,也没有opens

  • module-info.java module packt.lib { exports com.packt.lib; } 这里没有exports及opens com.packt.lib.sub1
  • 通过类名反射(编译报错) Exception in thread "main" java.lang.IllegalAccessException: class com.packt.App (in module packt.main) cannot access class com.packt.lib.sub1.Sub1Service (in module packt.lib) because module packt.lib does not export com.packt.lib.sub1 to module packt.main at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361) at java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:107) at java.base/java.lang.Class.newInstance(Class.java:553) at packt.main/com.packt.App.main(App.java:25)
  • 通过包名反射(newInstance运行时报错) Exception in thread "main" java.lang.IllegalAccessException: class com.packt.App (in module packt.main) cannot access class com.packt.lib.sub1.Sub1Service (in module packt.lib) because module packt.lib does not export com.packt.lib.sub1 to module packt.main at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361) at java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:107) at java.base/java.lang.Class.newInstance(Class.java:553) at packt.main/com.packt.App.main(App.java:26)

没有exports,有opens

  • module-info.java module packt.lib { exports com.packt.lib; opens com.packt.lib.sub1; }
  • 通过类名反射 由于没有exports,则编译不过 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.2:compile (default-compile) on project main: Compilation failure [ERROR] /Users/demo/java9-multi-module-demo/main/src/main/java/com/packt/App.java:[30,41] 程序包 com.packt.lib.sub1 不可见 [ERROR] (程序包 com.packt.lib.sub1 已在模块 packt.lib 中声明, 但该模块未导出它)
  • 通过包名反射 像上面那种直接引用包名来反射的,不会报错,因为编译可以通过,运行正常

有exports,没有opens

module packt.lib {
    exports com.packt.lib;
    exports com.packt.lib.sub1;
}
  • 通过类名反射 可以编译通过,运行正常,这是因为默认—illegal-access=permit,如果改为—illegal-access=deny则报错 Sub1Service being instanced Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make private void com.packt.lib.sub1.Sub1Service.privateMethod() accessible: module packt.lib does not "opens com.packt.lib.sub1" to module packt.main at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198) at java.base/java.lang.reflect.Method.setAccessible(Method.java:192) at packt.main/com.packt.App.main(App.java:43) [ERROR] Command execution failed.
  • 通过包名反射 可以编译通过,运行正常,这是因为默认—illegal-access=permit,如果改为—illegal-access=deny则报错 Sub1Service being instanced [Ljava.lang.reflect.Method;@4157f54e public method called! Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make protected void com.packt.lib.sub1.Sub1Service.protectedMethod() accessible: module packt.lib does not "opens com.packt.lib.sub1" to module packt.main at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198) at java.base/java.lang.reflect.Method.setAccessible(Method.java:192) at packt.main/com.packt.App.main(App.java:33) [ERROR] Command execution failed.

直接open整个module,但是没有exports

  • module-info.java open module packt.lib { exports com.packt.lib; }
  • 直接访问类反射 这种情况,如果是直接访问该类来使用反射,由于没有exports该package,则直接编译报错
  • 通过包名反射 这种情况编译可以通过,运行正常

直接open整个module,也有exports

open module packt.lib {
    exports com.packt.lib;
    exports com.packt.lib.sub1;
}

两种访问方式的反射均正常编译及运行。

小结

  • open表示允许运行时通过反射使用open的作用是表示该模块下的所有的包在runtime都允许deep reflection(包括public及private类型);opens package的作用只是允许该包在runtime都允许deep reflection

open及opens都仅仅是开放runtime时期的可以通过反射访问(蕴含了运行时的exports)。

  • exports表示允许访问指定包的public成员(编译及运行时)如果反射不直接通过类名调用,只是运行时通过包名使用,则只需open或opens即可 如果是通过类名来反射,由于用到了该类,需要通过exports指定可以访问,不指定则编译期立即报错 如果是通过类名来反射使用public方法或newInstance,如果没有exports,则运行时报错 如果有exports,但是没有open,由于默认—illegal-access=permit,因此相当于带上了open

doc

  • Java 9 揭秘(2. 模块化系统)

本文分享自微信公众号 - 码匠的流水账(geek_luandun),作者:go4it

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-02-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 聊聊sharding-jdbc的WrapperAdapter

    jdk-12.jdk/Contents/Home/lib/src.zip!/java.sql/java/sql/Wrapper.java

    codecraft
  • 聊聊sharding-jdbc的WrapperAdapter

    jdk-12.jdk/Contents/Home/lib/src.zip!/java.sql/java/sql/Wrapper.java

    codecraft
  • 聊聊debezium的OffsetCommitPolicy

    debezium-v1.1.1.Final/debezium-api/src/main/java/io/debezium/engine/spi/OffsetCo...

    codecraft
  • RMI

    RMI定义:     RMI即远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个j...

    用户1215919
  • 设计模式一:简单工厂模式

    简单工厂模式属于创建型模式,又叫做静态工厂方法(Static Factory Method)。简单工厂模式是由一个工厂对象决定创建哪一种产品类实例。在简单工厂模...

    对弈
  • Java中的时间和日期(一):有关java时间的哪些坑

    从一开始学习java到现在,我们都一直在使用java.util.Date这个对象来表示时间和日期。使用也很方便:

    冬天里的懒猫
  • 深入理解 Java 反射:Field (成员变量)

    深入理解 Java 反射系列: 深入理解 Java 反射:Class (反射的入口) 深入理解 Java 反射:Field (成员变量) 深入理解 Java ...

    张拭心 shixinzhang
  • Python 获取 Access 表字

    关于字段类型(Type):3为数字,202为文本,203为备忘。Windows 下有更简洁的函数 pypyodbc.win_connect_mdb,只需要填路径...

    py3study
  • EOS博彩平台可能是EOS的引爆点

    虽然现在币价一跌再跌,一步步逼近北境极寒之地。但区块链从不缺热闹项目,真是冰火两重天。所有热闹的项目,最后指向一个幕后黑手,EOS。

    凌帅出口
  • day29_Hibernate学习笔记_01

      Hibernate:是一个数据持久化层的ORM框架。   Object:对象,java对象,此处特指JavaBean。   Relational:关系,二维...

    黑泽君

扫码关注云+社区

领取腾讯云代金券