Skip to content
程序猿DD程序猿DD
主页
最新发布
Java新特性
玩转IDEA
旧版博客open in new window
社区交流open in new window
  • Java 18

    • 指定UTF-8为默认字符集
      • 简单Web服务器
        • 新增@snippet标签
        • Java 17

          • 密封类
            • switch的模式匹配(预览)
            • Java 16

              • record类
                • instanceof增强
                  • Stream转List增强
                    • Stream.toList()的性能比较
                    • Java 15

                      • 文本块
                        • 隐藏类
                          • 什么是隐藏类
                            • 隐藏类案例
                          • Java 14

                            • switch表达式增强
                            • Java 13

                              • Java 12

                                • Java 11

                                  • 移除JavaEE和CORBA模块
                                  • Java 10

                                    • 局部变量的类型推断
                                    • Java 9

                                      • 交互式编程环境JShell
                                        • 不可变集合的快捷创建方法

                                        Java 15 新特性:隐藏类

                                        程序猿DD原创2022年5月7日
                                        • Java
                                        • Java 15
                                        大约 3 分钟

                                        此页内容
                                        • 什么是隐藏类
                                        • 隐藏类案例

                                        # Java 15 新特性:隐藏类

                                        # 什么是隐藏类

                                        隐藏类,是一种不能被其他类直接使用的类。引入隐藏类的主要目的是给框架来使用,使得框架可以在运行时生成类,并通过反射间接使用它们。可能有点抽象,不要紧,下面我们通过一个例子来直观的认识它!

                                        本文首发:Java 15 新特性:隐藏类open in new window 本期视频:Java 15 新特性:隐藏类open in new window

                                        # 隐藏类案例

                                        第一步:先创建一个普通的Java类

                                        public class JEP371HiddenClasses {
                                        
                                            public static String hello() {
                                                return "https://www.didispace.com";
                                            }
                                        
                                        }
                                        

                                        第二步:编译一下,或得编译后的class文件。然后使用Base64对文件内容Encode,你可以用各种工具,也可以用下面代码来获取:

                                        String filePath = "JEP371HiddenClasses.class";
                                        byte[] b = Files.readAllBytes(Paths.get(filePath));
                                        log.info(Base64.getEncoder().encodeToString(b));
                                        

                                        执行一下,获取到内容如下:

                                        yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==
                                        

                                        这个内容就是第一步写的类。

                                        第三步:通过反射加载上面生成的类,并调用隐藏类中的hello函数,代码如下:

                                        /**
                                         * 程序猿DD
                                         * <a href="https://www.didispace.com/java-features/">Java新特性解读</a>
                                         */
                                        @Test
                                        void testHiddenClasses() throws Throwable {
                                          // 1. 加载encode之后的隐藏类
                                          String CLASS_INFO = "yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==";
                                          byte[] classInBytes = getDecoder().decode(CLASS_INFO);
                                        
                                          Class<?> proxy = MethodHandles.lookup()
                                            .defineHiddenClass(classInBytes, true, MethodHandles.Lookup.ClassOption.NESTMATE)
                                            .lookupClass();
                                        
                                          // 输出类名
                                          log.info(proxy.getName());
                                          // 输出类有哪些函数
                                          for(Method method : proxy.getDeclaredMethods()) {
                                            log.info(method.getName());
                                          }
                                          // 2. 调用hello函数
                                          MethodHandle mh = MethodHandles.lookup().findStatic(proxy, "hello", MethodType.methodType(String.class));
                                          String result = (String) mh.invokeExact();
                                          log.info(result);
                                        }
                                        

                                        这里的CLASS_INFO内容就是上一步我们处理好的隐藏类内容,先通过Base64将该内容解码,然后通过反射机制来将这个隐藏类的代理创建出来。

                                        可以看到这里创建隐藏类的时候用的是java.lang.invoke.MethodHandles.Lookup#defineHiddenClass,而创建普通类则是用ClassLoader::defineClass的。而这里的三个参数分别是:

                                        • bytes:符合java虚拟机规范的字节码
                                        • initialize:是否要初始化类
                                        • options:java类的类型,详见java.lang.invoke.MethodHandles.Lookup.ClassOption

                                        再下面的操作就是对这个类的一些内容输出和方法执行。最后我们可以尝试执行下这个测试内容,最终会获得如下输出:

                                        17:20:50.360 [main] INFO com.didispace.debug.java15.JEP371Test - com.didispace.debug.java15.JEP371HiddenClasses/0x0000000800cb0c00
                                        17:20:50.361 [main] INFO com.didispace.debug.java15.JEP371Test - hello
                                        17:20:50.361 [main] INFO com.didispace.debug.java15.JEP371Test - https://www.didispace.com
                                        
                                        • 第一行:输出了这个隐藏类的类名
                                        • 第二行:输出了这个隐藏类下的方法名称
                                        • 第三行:调用隐藏类下的hello方法获得的返回内容

                                        是不是还挺简单?如果你跟我一样平时会参与一些基础框架的开发工作的话,一定觉得这个功能还挺不错的吧,又多了一种动态功能的实现手段!

                                        好了,今天的分享就到这里!如果您学习过程中如遇困难?可以加入我们超高质量的技术交流群open in new window,参与交流与讨论,更好的学习与进步!另外,不要走开,关注我!持续更新Java新特性专栏open in new window!

                                        上次编辑于: 2022/5/21 02:29:53
                                        贡献者: dd
                                        上一页
                                        文本块
                                        © 2016 - 2022 didispace.com 版权所有
                                        沪ICP备14037150号-3
                                        Copyright © 2022 程序猿DD