Java 15中的隐藏类是咋回事?

Java 从1.1 版就有匿名类,但是,匿名类并不是匿名的。你不需要为它们命名,它们是由 Java 编译器命名的。如果你熟悉命令javap,则可以“反汇编”JAR 文件并查看编译器为匿名类生成的名称。

Java 15 引入了没有名称的隐藏类。它不是语言的一部分,而是 JDK 的一部分。没有用于创建隐藏类的语言元素,但 JDK 方法和类可以提供帮助。

在本文中,我们将讨论

  • 什么是隐藏类,它们出现的原因是什么​

  • 如何使用隐藏类

  • 如何使用 JDK 方法加载隐藏类

  • 如何使用SourceBuddy轻松创建和加载隐藏类。

什么是隐藏类?

hidden … 不能被其他类的字节码直接使用的类,可能不容易理解。

一个隐藏类被加载到 JVM 中,当一个类是源代码或字节码格式时,它不能被“隐藏”。该术语只能指加载的类,将它们称为秘密加载的类可能更合适。

当一个类以特定方式加载时,它会隐藏起来,以便它在其他代码部分面前保持秘密。保持隐藏并不意味着其他代码不能使用这个类。只要他们“知道”这个秘密,他们就可以。最大的区别是这个类不是“广而告之”的,因为你无法使用名称找到它。

当你以创建隐藏类的隐藏方式加载类时,你就拥有对此类的引用。使用反射方法,你可以多次实例化类,然后可以调用方法、设置和获取字段。如果该类实现了一个接口或继承了一个类,你可以将实例引用强制转换为接口和类,并在不进行反射的情况下调用这些方法。

该类被隐藏有两个原因:

  • 它没有其他类可以引用的名称

  • 没有从类加载器到类的引用。

当在引用隐藏类的变量上调用getName()或者getSimpleName()时,你将获得一些字符串,这些是说人话给人看的名称,与其他类无关。当一个类引用另一个类时,它需要规范名称。getCanonicalName()返回null。规范名称是类的实际名称,在隐藏类的情况下不存在。

由于没有规范名称就无法通过类加载器找到该类,因此加载器没有理由保留对该类的引用。当它不能将引用提供给任何对象时,为什么要保留?保留引用只会产生一个副作用:只要类加载器处于活动状态,就会阻止 GC 卸载类。

由于没有来自类加载器的引用,GC 可以在类对象不再使用时立即将其卸载。

隐藏类有什么用?

JEP371描述了隐藏类的原因:允许框架将类定义为框架不可发现的实现细节,这样它们就不能被其他类链接,也不能通过反射发现

许多框架使用动态创建的类,在大多数情况下它们是代理类。代理类实现一个接口或继承另一个类,当被调用时,它实际会调用接口或原始类的实例。它通常还会做一些额外的事情,否则代理类和实例就没有存在的理由了。

当你的代码需要将请求 bean 注入会话 bean 时,Spring 框架就是一个例子。(或者将任何其他生命周期较短的 bean 转换为更长的 bean)多个线程可以同时服务于不同的请求,所有请求都属于同一个会话。所有这些线程都会看到相同的会话 bean,但它们会神奇地看到它们的请求 bean是一个扩展请求 bean 类的代理对象。当你调用请求 bean 上的方法时,你将调用代理实例。它检查线程及其服务的请求并将调用转发到适配的请求 bean。

另一个例子是 JPA 延迟加载。你可以有一个 SQL 表,其中每一行都引用前一行。当尝试加载最后一条记录时,它会自动加载前一条记录,这确实会加载前一条记录。它将加载整个表,除非将该字段注释为lazy。这意味着只有在需要时才必须加载数据库中的实际数据。

当加载记录时,你会得到一个代理对象。这个代理对象知道它指的是哪条记录,并且只有在调用方法时才会从数据库中加载记录。

相同的机制用于面向方面的编程和许多其他情况。

可以仅使用 JDK 反射 API 创建代理类,只要目标类实现要代理的接口即可。如果没有这样的接口,你可以使用ByteBuddy库。

当创建这样的类时,不需要为这些类起任何名字,你就获得了对类的引用和对实例的引用。框架将引用注入它需要的字段,然后代码将它们用作任何对象。它不需要知道类的名称,只需要知道它是目标类或接口的实例即可。但是,某些代码可能会发现该名称。这些类有一些反射可以发现的名称。一些“聪明”的人可能会发现它并玩一些巧妙的把戏,以后可能会遇到维护问题。如果根本没有名字会更好吗?可能是的,它会更干净。因此:隐藏类就出现了。

除此之外,还有一个隐藏类的原因。只要类有名称,就可以通过名称发现它。类加载器必须使类保持活动状态以使其可被发现。类加载器具有对加载类的引用。这意味着垃圾收集器将无法收集该类,即使它不再被使用。

如果一个类没有名字,类加载器就不需要保留对这个类的引用。类加载器不会保留对隐藏类的引用,除非明确指示它们这样做。当一个隐藏类的所有实例都被收集,并且没有对该类的引用时,垃圾收集器会将其识别为垃圾。类加载器不会将类保存在内存中。

这样,当长时间运行的代码创建大量类时,框架不会过度消耗内存。收集未使用类的更好框架不需要为这些临时类创建单独的类加载器。没有必要创建一个短暂的、一次性的类加载器来使类也是一次性的。

支持使用不可发现的类扩展访问控制嵌套。

这是JEP371目标列表中的第二个要点。JVM 可以加载隐藏类,使它们成为嵌套的成员。什么是嵌套?

曾几何时,Java 1.0 版没有内部类。然后,Java 1.1 版本引入了内部类,但没有改变 JVM 结构。JVM 对内部类一无所知。Java 编译器从内部类创建常规(几乎所有)顶级类。它发明了一些有趣的名字,比如A$B就代表类B存在于类A中

虽然有一些可见性的黑客攻击。内部类与顶级类具有相同的可见性。一个编译单元(文件)内的任何私有内容都是可见的。然而,可见性也是由 JVM 强制执行的。但是 JVM 看到两个顶级类。编译器在需要克服此问题的任何地方在类中生成桥接方法。它们是 JVM 的包级别,当被调用时,它们将调用传递给私有方法。

然后在 25 年后出现了 Java 11,并引入了 nest control。从 Java 11 开始,每个类都与另一个类或自身有关系,后者是该类的嵌套宿主。具有相同嵌套host的类可以看到彼此的私有成员。JVM 不再需要桥接方法。

当加载一个隐藏的类时,你可以指定它成为与创建查找对象的类相同的嵌套(具有相同的嵌套宿主)的成员。

支持主动卸载不可发现的类,以便框架可以灵活地根据需要定义任意数量的类。

这是很重要的一点。当创建一个类时,只要类加载器处于活动状态,它就会保留在内存中。类加载器保留对它们加载的所有类的引用,某些代码可能会要求类加载器按名称返回加载的类对象。应用程序逻辑可能早就忘记了类;没有人会需要它。尽管如此,垃圾收集器仍无法收集它,因为类加载器中存在引用。一个解决方案是为每个新的非隐藏动态创建的类创建一个新的类加载器,但可能会误杀。

默认情况下,加载隐藏类的类加载器不会保留对隐藏类的引用。与嵌套host一样,可以提供不同的选项。

没有名称,不可发现,但保留一个额外的引用,这样 GC 就不会丢弃它。

弃用非标准 API sun.misc.Unsafe::defineAnonymousClass,以便在未来的版本中将其删除。

通过这些,我们讨论了什么是隐藏类。你应该对它们的性质及其重要性有深入的了解。

在下文中,我将讨论如何使用 JDK API 创建隐藏类,然后使用SourceBuddy

创建隐藏类

在本文中,我们将在运行时从文本、Java 源代码动态创建一个类,然后将生成的字节码加载为隐藏类。本文的示例项目仅包含单元测试文件。类是TestHiddenClassLoader。我们将隐藏类的源代码存储在一个字段变量中。 

1.     private static final String CODE1 = """2.             package com.javax0.blog.hiddenclasses;3.4.             public class MySpecialClass implements TestHiddenClassLoader.Hello {5.6.                 @Override7.                 public void hello() {8.                     System.out.println("Hello, from the hidden class.");9.                 }10.             }11.             """;

接口也在同一个类中。

1.     interface Hello {2.         void hello();3.     }

单元测试:

1.         final var byteCode = Compiler.java().from(CODE1).compile().get();2.         final var lookup = MethodHandles.lookup();3.         final var classLookup = lookup.defineHiddenClass(byteCode, true);4.         final var helloClass = (Class<Hello>) classLookup.lookupClass();5.6.         final var hello = helloClass.getConstructor().newInstance();7.         hello.hello();

我们在此代码中使用 SourceBuddy 库将 Java 源代码编译为字节代码。示例的第一行就是这样做的。我们使用 SourceBuddy 版本 2.1.0。

我们需要一个查找对象来加载编译后的字节码作为隐藏类。该对象在第二行创建。第三行和第四行使用查找对象来加载隐藏的类。第 3 行定义了将其加载到 JVM 中的类。第二个参数 ,true,初始化类,那是static{}块执行的时候。最后一行调用接口定义的方法hello()

现在局部变量hello是一个对象的实例,一个隐藏类。什么是隐藏类的名称、简单名称和规范名称?我们把它打印出来。

1.         System.out.println("1. " + hello.getClass());2.         System.out.println("2. " + hello.getClass().getClassLoader());3.         System.out.println("3. " + this.getClass().getClassLoader());4.         System.out.println("4. " + hello.getClass().getSimpleName());5.         System.out.println("5. " + hello.getClass().getName());6.         System.out.println("6. " + hello.getClass().getCanonicalName());7.         System.out.println("7. " + lookup.getClass());8.         System.out.println("8. " + lookup.getClass().getClassLoader());

输出:

Hello, from the hidden class.1. class com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b0c002. jdk.internal.loader.ClassLoaders$AppClassLoader@5b37e0d23. jdk.internal.loader.ClassLoaders$AppClassLoader@5b37e0d24. MySpecialClass/0x00000008011b0c005. com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b0c006. null7. class java.lang.invoke.MethodHandles$Lookup8. null

我们可以看到调用hello()的输出,然后是类对象隐式toString()打印的名称、加载隐藏类的类加载器、简单名称、名称,最后一行是规范名称。最后一个很有趣null,没有显示类名。它是隐藏的。

该类虽然是隐藏的,但具有对加载它的类加载器的引用。当代码执行过程中有任何需要解决的问题时,就需要它。不同之处在于类加载器没有对类的引用。从类到加载器的一个方向存在,但从加载器到类的另一个方向不存在。

类加载器与加载类调用MethodHandles.lookup()的类加载器相同。可以看到,因为我们在测试中打印出了this对象的类加载器。

最后,我们还打印出查找对象的类和类加载器。后者是null,这意味着引导类加载器加载了它。

还应该注意接口hello是包私有的。它对于动态创建的代码仍然可见,因为它在同一个包和模块中。

加载隐藏类时,它与定义接口的包在同一个包中。然而,这还不够,因为我们将在下一节中看到一个示例。同一个类加载器加载接口和隐藏类也是一个要求。这样,接口和隐藏类位于同一个模块中,在本例中为同一个未命名模块。不同的类加载器将类加载到不同的模块中;因此,当你使用不同的类加载器加载一个类时,可能看不到包字段、方法、接口等,即使它们在同一个包中。

查找对象来自同一模块并不是唯一的要求,还要求它与要加载的类来自同一个包,在这一点上很容易搞混淆。

查找对象是java.lang.invoke包中类的一个实例。加载此类的类加载器是null,这意味着是引导类加载器。引导类加载器是用 C/C++ 而不是 Java 实现的。没有对应的Java对象代表这个类加载器;因此不能引用它,通过从getClassloader()返回null来解决。有一个模块、包和类“属于”查找对象,代码的模块、包和类称为MethodHandles.lookup()方法。

不能从一个包为另一个包创建隐藏类。如果尝试这样做,就像下面的示例代码一样:

1.         try {2.             final var byteCode = Compiler.java()3.                     .from("package B; class A{}").compile().get();4.             MethodHandles.lookup().defineHiddenClass(byteCode, true);5.         } catch (Throwable t) {6.             System.out.println(t);7.         }

还是测试类的com.javax0.blog.hiddenclasses.TestHiddenClassLoader。要加载的类与MethodHandles.lookup() 的调用者不在同一个包中。它将导致打印输出:

java.lang.IllegalArgumentException: B.A not in same package as lookup class

轻松创建隐藏类

在上一节中,我们动态创建了一个新类,并隐藏加载了新类。加载是使用我们从MethodHandles类中获取的查找对象完成的。在本节中,我们将了解如何通过调用 SourceBuddy 的 API 来实现同样的目的。

创建类 saying hello 的代码如下:

1.         final var hello = Compiler.java()2.                 .from(CODE1.replaceAll("\\.Hello", ".PublicHello")).hidden()3.                 .compile().load().newInstance(PublicHello.class);4.         hello.hello();

在这段代码中,我们将接口从Hello替换成了PublicHello

1.     public interface PublicHello {2.         void hello();3.     }

与以前的界面基本相同,但public 这个过程比以前简单得多。我们指定源代码;声明它是一个隐藏类调用hidden(),我们编译、加载并请求一个实例转换为PublicHello

如果我们想使用 package-private 接口,比如(不替换HelloPublicHello):

1.         Assertions.assertThrows(IllegalAccessError.class, () ->2.                 Compiler.java().from(CODE1).hidden().compile().load().newInstance(PublicHello.class));

我们会得到如下错误。

java.lang.IllegalAccessError: class com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b1c00 cannot access its superinterface com.javax0.blog.hiddenclasses.TestHiddenClassLoader$Hello (com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b1c00 is in unnamed module of loader com.javax0.sourcebuddy.ByteClassLoader @4e5ed836; com.javax0.blog.hiddenclasses.TestHiddenClassLoader$Hello is in unnamed module of loader 'app')

原因在错误消息中解释得很清楚。接口和实现它的类在两个不同的模块中。两者都是未命名模块,但它们并不相同。在 Java 中,从 Java 9 开始,有了模块,当应用程序不使用模块时,它实际上创建了伪模块,将类放在那里。JDK 类仍在模块中,例如java.base.

如上创建的隐藏类创建使用单独的类加载器来加载动态编写的 Java 类。单独的类加载器将类加载到它的模块中。不同模块中的代码无法看到来自其他模块的类,除非它们是公共的。

尽管 SourceBuddy 做了一些小技巧来加载一个隐藏的类,但它无法克服这个限制。

加载隐藏类需要查找对象。应用程序通常提供此对象。上面的调用没有指定任何查找对象,但 SourceBuddy 仍然需要,为此它创造了一个。查找对象会记住调用的类MethodHandles.lookup()来创建一个。加载隐藏的类时,要求查找对象“属于”该类的包。查找对象已创建,从该包中的类调用它。查找对象将“属于”该类,因此属于该类的包。

要拥有来自特定包中的类的查找对象,我们需要该包中的一个类可以给我们一个。如果代码中没有,我们必须动态创建一个。SourceBuddy 正是这样做的。它为类创建 Java 源代码,编译并加载它,实例化它,并调用Supplier<MethodHandles.Lookup>类实现的已定义的get()方法。这是一种似乎违反了Java内置访问控制的技巧。我们似乎在没有为它准备的包中获得了一个新的隐藏类。包在 Java 中受到保护免受外部访问(微不足道)。只能从包外部使用公共和受保护的成员和类。可以使用反射从外部访问包,但只能在同一模块中访问,或者必须显式打开该模块。类似地,使用查找对象加载的对象应该在同一个包中,并且可以访问包的内部成员,如果包中的类提供了该查找,则不会。

从错误信息中我们可以看出,它似乎只是包。实际上,新的隐藏类在同名的包中,但在不同的模块中。

如果你想在同一个包中有一个隐藏的类,而不仅仅是一个同名的包,你需要一个来自该包的查找对象。

在我们的示例中,它很简单。我们的Hello接口与测试代码在同一个包中,这样我们就可以自己创建查找对象:

1.         final var hi = Compiler.java().from(CODE1).hidden(MethodHandles.lookup()).compile()2.                 .load().newInstance(Hello.class);3.         hi.hello();

在实际示例中,访问查找对象可能会稍微复杂一些。当调用 SourceBuddy 的代码与生成的代码位于不同的包中时,查找对象的创建不能在 SourceBuddy 调用代码中。

在下面的例子中,我们将看到如何做到这一点。

我们在com.javax0.blog.hiddenclasses.otherpackage包中有一个类OuterClass

1. package com.javax0.blog.hiddenclasses.otherpackage; 2. 3. import java.lang.invoke.MethodHandles; 4. 5. public class OuterClass { 6.14.     public static MethodHandles.Lookup lookup() {15.         return MethodHandles.lookup();16.     }17. }

这个类有一个lookup()方法,它创建一个查找对象并返回。如果我们从我们的代码中调用这个方法,我们将有一个合适的查找对象。请注意,此类位于不同的包中,与我们的测试代码不同。我们的测试代码在com.javax0.blog.hiddenclasses,更深一层的封装OuterClass。本质上是在不同的包中。

我们还有另一个类用于演示。

1. package com.javax0.blog.hiddenclasses.otherpackage;2.3. class MyPackagePrivateClass {4.5.     void sayHello(){6.         System.out.println("Hello from package private.");7.     }8.9. }

它是一个包含包私有方法的包私有类。如果我们动态创建一个隐藏类,如下例:

1.         final var hidden = Compiler.java().from(""" 2.                 package com.javax0.blog.hiddenclasses.otherpackage; 3. 4.                 public class AnyName_ItWillBeDropped_Anyway { 5.                     public void hi(){ 6.                         new MyPackagePrivateClass().sayHello(); 7.                     } 8.                 }""").hidden(OuterClass.lookup()).compile().load().newInstance(); 9.         final var hi = hidden.getClass().getDeclaredMethod("hi");10.         hi.invoke(hidden);

我们还没有谈到一个话题,那就是如何创建nestmate。

当有一个二进制类文件时,你可以将它作为一个 nestmate 加载到一个提供查找对象的类中。JVM 不关心该类是如何创建的。当我们编译 Java 源代码时,我们只有一种可能,该类必须是内部类。

当使用 SourceBuddy 时,必须将源代码作为内部类提供给你希望隐藏的代码与其嵌套的代码。编译代码时已经提供了源代码和类。不可能向该源代码中插入任何新的内部类,我们必须骗过编译器。

我们提供了一个与稍后要插入内部类同名的类。编译完成后,我们还有外部类和内部类。我们告诉类加载忘记外部,只加载隐藏的内部。

这就是我们要做的。这次我们在这里显示我们用于演示的整个外部类,包括跳过的行。

1. package com.javax0.blog.hiddenclasses.otherpackage; 2. 3. import java.lang.invoke.MethodHandles; 4. 5. public class OuterClass { 6. 7.     // skip lines 8.     private int z = 55; 9.10.     public int getZ() {11.         return z;12.     }13.     // end skip14.     public static MethodHandles.Lookup lookup() {15.         return MethodHandles.lookup();16.     }17. }

正如你将看到的,它有一个私有字段和一个 getter 来有效地测试更改的值。它也有前面提到的lookup()方法。动态创建内部类的代码如下:

1.         final var inner = Compiler.java().from(""" 2.                         package com.javax0.blog.hiddenclasses.otherpackage; 3. 4.                         public class OuterClass 5.                                                 { 6.                             private int z; 7. 8.                             public static class StaticInner { 9.                                public OuterClass a(){10.                                  final var outer = new OuterClass();11.                                  outer.z++;12.                                  return outer;13.                                }14.                             }15.16.                         }""").nest(MethodHandles.Lookup.ClassOption.NESTMATE).compile().load()17.                 .newInstance("StaticInner");18.         final var m = inner.getClass().getDeclaredMethod("a");19.         final var outer = (OuterClass)m.invoke(inner);20.         Assertions.assertEquals(56, outer.getZ());

source里面有OuterClass,不过只是帮助编译,告诉SourceBuddy嵌套宿主的名字。当我们调用带有NESTMATE选项的nest()方法时,它知道类OuterClass是嵌套宿主。它还标记了类加载器永远不会加载的类。内部类编译为不同的字节码,当被加载后成为OuterClass的nestmate​。

如果你注意本文中讨论的 Java 访问控制的复杂细节,会注意到我们没有提供查找对象。上面的例子仍然有效。这怎么可能?当调用nest()时,SourceBuddy 查找已加载的OuterClass版本并使用反射获取查找对象。为此,外部类必须具有 type 的静态字段或MethodHandles.Lookup方法。OuterClass有一个方法,所以SourceBuddy调用这个方法来获取查找对象。

上面的例子创建了一个静态内部类,也可以以相同的方式创建非静态内部类。​

非静态内部类的创建与静态内部类的创建非常相似:

 1.         final var outer = new OuterClass(); 2.         final var inner = Compiler.java().from(""" 3.                         package com.javax0.blog.hiddenclasses.otherpackage; 4. 5.                         public class OuterClass { 6.                             private int z; 7. 8.                             public class Inner { 9.                                public void a(){10.                                  z++;11.                                }12.                             }13.14.                         }""").nest(MethodHandles.Lookup.ClassOption.NESTMATE).compile().load()15.                 .newInstance("Inner", classes(OuterClass.class), args(outer));16.         final var m = inner.getClass().getDeclaredMethod("a");17.         m.invoke(inner);18.         Assertions.assertEquals(56, outer.getZ());

我们需要一个外部类的实例来实例化内部类。它是变量outer。我们必须通过 SourceBuddy 的 API 将这个变量传递给构造函数newInstance()。此方法调用有一个版本,它接受一个构造函数参数类型Class[]和一个值的数组Object[]。在内部类的情况下,它是外部类和实例。

6.总结

本文讨论了 Java 15 中引入的隐藏类的一些细节,比通常的介绍性文章更深入一些,了解了隐藏类的工作原理以及如何在项目中使用它们。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JAVA开发人员必备是HTML格式的 JavaTM 2 Platform Standard Edition 6 API 规范 本文档是 Java 2 Platform Standard Edition 6.0 的 API 规范。 请参见: 描述 Java 2 Platform 软件包 java.applet 提供创建 applet 所必需的和 applet 用来与其 applet 上下文通信的java.awt 包含用于创建用户界面和绘制图形图像的所有java.awt.color 提供用于颜色空间的java.awt.datatransfer 提供在应用程序之间和在应用程序内部传输数据的接口和java.awt.dnd Drag 和 Drop 是一种直接操作动作,在许多图形用户界面系统都会遇到它,它提供了一种机制,能够在两个与 GUI 显示元素逻辑相关的实体之间传输信息。 java.awt.event 提供处理由 AWT 组件所激发的各事件的接口和java.awt.font 提供与字体相关的和接口。 java.awt.geom 提供用于在与二维几何形状相关的对象上定义和执行操作的 Java 2D java.awt.im 提供输入方法框架所需的和接口。 java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种java.awt.image.renderable 提供用于生成与呈现无关的图像的和接口。 java.awt.print 为通用的打印 API 提供和接口。 java.beans 包含与开发 beans 有关的,即基于 JavaBeansTM 架构的组件。 java.beans.beancontext 提供与 bean 上下文有关的和接口。 java.io 通过数据流、序列化和文件系统提供系统输入和输出。 java.lang 提供利用 Java 编程语言进行程序设计的基础java.lang.annotation 为 Java 编程语言注释设施提供库支持。 java.lang.instrument 提供允许 Java 编程语言代理检测运行在 JVM 上的程序的服务。 java.lang.management 提供管理接口,用于监视和管理 Java 虚拟机以及 Java 虚拟机在其上运行的操作系统。 java.lang.ref 提供了引用对象,支持在某种程度上与垃圾回收器之间的交互。 java.lang.reflect 提供和接口,以获得关于和对象的反射信息。 java.math 提供用于执行任意精度整数算法 (BigInteger) 和任意精度小数算法 (BigDecimal) 的java.net 为实现网络应用程序提供java.nio 定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 java.nio.channels 定义了各种通道,这些通道表示到能够执行 I/O 操作的实体(如文件和套接字)的连接;定义了用于多路复用的、非阻塞 I/O 操作的选择器。 java.nio.channels.spi 用于 java.nio.channels 包的服务提供者java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活提供支持。 java.rmi.dgc 为 RMI 分布式垃圾回收提供了和接口。 java.rmi.registry 提供 RMI 注册表的一个和两个接口。 java.rmi.server 提供支持服务器端 RMI 的和接口。 java.security 为安全框架提供和接口。 java.security.acl 此包和接口已经被 java.security 包取代。 java.security.cert 提供用于解析和管理证书、证书撤消列表 (CRL) 和证书路径的和接口。 java.security.interfaces 提供的接口用于生成 RSA Laboratory Technical Note PKCS#1 定义的 RSA(Rivest、Shamir 和 Adleman AsymmetricCipher 算法)密钥,以及 NIST 的 FIPS-186 定义的 DSA(数字签名算法)密钥。 java.security.spec 提供密钥规范和算法参数规范的和接口。 java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的和接口。 java.text.spi java.text 包的服务提供者java.util 包含 collection 框架、遗留的 collection 、事件模型、日期和时间设施、国际化和各种实用工具(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程很常用的实用工具java.util.concurrent.atomic 的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java.util.logging 提供 JavaTM 2 平台核心日志工具的和接口。 java.util.prefs 此包允许应用程序存储并获取用户和系统首选项和配置数据。 java.util.regex 用于匹配字符序列与正则表达式指定模式的java.util.spi java.util 包的服务提供者java.util.zip 提供用于读写标准 ZIP 和 GZIP 文件格式的javax.accessibility 定义了用户界面组件与提供对这些组件进行访问的辅助技术之间的协定。 javax.crypto 为加密操作提供和接口。 javax.crypto.interfaces 根据 RSA Laboratories' PKCS #3 的定义,提供 Diffie-Hellman 密钥接口。 javax.crypto.spec 为密钥规范和算法参数规范提供和接口。 javax.imageio Java Image I/O API 的主要包。 javax.imageio.event Java Image I/O API 的一个包,用于在读取和写入图像期间处理事件的同步通知。 javax.imageio.metadata 用于处理读写元数据的 Java Image I/O API 的包。 javax.imageio.plugins.bmp 包含供内置 BMP 插件使用的公共的包。 javax.imageio.plugins.jpeg 支持内置 JPEG 插件的javax.imageio.spi 包含用于 reader、writer、transcoder 和流的插件接口以及一个运行时注册表的 Java Image I/O API 包。 javax.imageio.stream Java Image I/O API 的一个包,用来处理从文件和流产生的低级别 I/O。 javax.management 提供 Java Management Extensions 的核心javax.management.loading 提供实现高级动态加载的javax.management.modelmbean 提供了 ModelMBean 的定义。 javax.management.monitor 提供 monitor 的定义。 javax.management.openmbean 提供开放数据型和 Open MBean 描述符javax.management.relation 提供 Relation Service 的定义。 javax.management.remote 对 JMX MBean 服务器进行远程访问使用的接口。 javax.management.remote.rmi RMI 连接器是供 JMX Remote API 使用的一种连接器,后者使用 RMI 将客户端请求传输到远程 MBean 服务器。 javax.management.timer 提供对 Timer MBean(计时器 MBean)的定义。 javax.naming 为访问命名服务提供和接口。 javax.naming.directory 扩展 javax.naming 包以提供访问目录服务的功能。 javax.naming.event 在访问命名和目录服务时提供对事件通知的支持。 javax.naming.ldap 提供对 LDAPv3 扩展操作和控件的支持。 javax.naming.spi 提供一些方法来动态地插入对通过 javax.naming 和相关包访问命名和目录服务的支持。 javax.net 提供用于网络应用程序的javax.net.ssl 提供用于安全套接字包的javax.print 为 JavaTM Print Service API 提供了主要和接口。 javax.print.attribute 提供了描述 JavaTM Print Service 属性的型以及如何分这些属性的和接口。 javax.print.attribute.standard 包 javax.print.attribute.standard 包括特定打印属性的javax.print.event 包 javax.print.event 包含事件和侦听器接口。 javax.rmi 包含 RMI-IIOP 的用户 API。 javax.rmi.CORBA 包含用于 RMI-IIOP 的可移植性 API。 javax.rmi.ssl 通过安全套接字层 (SSL) 或传输层安全 (TLS) 协议提供 RMIClientSocketFactory 和 RMIServerSocketFactory 的实现。 javax.security.auth 此包提供用于进行验证和授权的框架。 javax.security.auth.callback 此包提供与应用程序进行交互所必需的,以便检索信息(例如,包括用户名和密码的验证数据)或显示信息(例如,错误和警告消息)。 javax.security.auth.kerberos 此包包含与 Kerberos 网络验证协议相关的实用工具javax.security.auth.login 此包提供可插入的验证框架。 javax.security.auth.spi 此包提供用于实现可插入验证模块的接口。 javax.security.auth.x500 此包包含应该用来在 Subject 存储 X500 Principal 和 X500 Private Crendentials 的javax.security.cert 为公钥证书提供javax.security.sasl 包含用于支持 SASL 的和接口。 javax.sound.midi 提供用于 MIDI(音乐乐器数字接口)数据的 I/O、序列化和合成的接口和javax.sound.midi.spi 在提供新的 MIDI 设备、MIDI 文件 reader 和 writer、或音库 reader 时提供服务提供者要实现的接口。 javax.sound.sampled 提供用于捕获、处理和回放取样的音频数据的接口和javax.sound.sampled.spi 在提供新音频设备、声音文件 reader 和 writer,或音频格式转换器时,提供将为其创建子的服务提供者的抽象javax.sql 为通过 JavaTM 编程语言进行服务器端数据源访问和处理提供 API。 javax.sql.rowset JDBC RowSet 实现的标准接口和基javax.sql.rowset.serial 提供实用工具,允许 SQL 型与 Java 编程语言数据型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现必须使用的标准和接口。 javax.swing 提供一组“轻量级”(全部是 Java 语言)组件,尽量让这些组件在所有平台上的工作方式都相同。 javax.swing.border 提供围绕 Swing 组件绘制特殊边框的和接口。 javax.swing.colorchooser 包含供 JColorChooser 组件使用的和接口。 javax.swing.event 供 Swing 组件触发的事件使用。 javax.swing.filechooser 包含 JFileChooser 组件使用的和接口。 javax.swing.plaf 提供一个接口和许多抽象,Swing 用它们来提供自己的可插入外观功能。 javax.swing.plaf.basic 提供了根据基本外观构建的用户界面对象。 javax.swing.plaf.metal 提供根据 Java 外观(曾经代称为 Metal)构建的用户界面对象,Java 外观是默认外观。 javax.swing.plaf.multi 提供了组合两个或多个外观的用户界面对象。 javax.swing.plaf.synth Synth 是一个可更换皮肤 (skinnable) 的外观,在其可委托所有绘制。 javax.swing.table 提供用于处理 javax.swing.JTable 的和接口。 javax.swing.text 提供 HTMLEditorKit 和创建 HTML 文本编辑器的支持javax.swing.text.html 提供 HTMLEditorKit 和创建 HTML 文本编辑器的支持javax.swing.text.html.parser 提供默认的 HTML 解析器以及支持javax.swing.text.rtf 提供一个 (RTFEditorKit),用于创建富文本格式(Rich-Text-Format)的文本编辑器。 javax.swing.tree 提供处理 javax.swing.JTree 的和接口。 javax.swing.undo 允许开发人员为应用程序(例如文本编辑器)的撤消/恢复提供支持。 javax.transaction 包含解组期间通过 ORB 机制抛出的三个异常。 javax.transaction.xa 提供定义事务管理器和资源管理器之间的协定的 API,它允许事务管理器添加或删除 JTA 事务的资源对象(由资源管理器驱动程序提供)。 javax.xml 根据 XML 规范定义核心 XML 常量和功能。 javax.xml.bind 为包含解组、编组和验证功能的客户端应用程序提供运行时绑定框架。 javax.xml.bind.annotation 定义将 Java 程序元素定制成 XML 模式映射的注释。 javax.xml.bind.annotation.adapters XmlAdapter 及其规范定义的子允许任意 Java 与 JAXB 一起使用。 javax.xml.bind.attachment 此包由基于 MIME 的包处理器实现,该处理器能够解释并创建基于 MIME 的包格式的已优化的二进制数据。 javax.xml.bind.helpers 仅由 JAXB 提供者用于: 提供某些 javax.xml.bind 接口的部分默认实现。 javax.xml.bind.util 有用的客户端实用工具javax.xml.crypto 用于 XML 加密的通用javax.xml.crypto.dom javax.xml.crypto 包的特定于 DOM 的javax.xml.crypto.dsig 用于生成和验证 XML 数字签名的javax.xml.crypto.dsig.dom javax.xml.crypto.dsig 包特定于 DOM 的javax.xml.crypto.dsig.keyinfo 用来解析和处理 KeyInfo 元素和结构的javax.xml.crypto.dsig.spec XML 数字签名的参数javax.xml.datatype XML/Java 型映射关系。 javax.xml.namespace XML 名称空间处理。 javax.xml.parsers 提供允许处理 XML 文档的javax.xml.soap 提供用于创建和构建 SOAP 消息的 API。 javax.xml.stream javax.xml.stream.events javax.xml.stream.util javax.xml.transform 此包定义了用于处理转换指令,以及执行从源到结果的转换的一般 API。 javax.xml.transform.dom 此包实现特定于 DOM 的转换 API。 javax.xml.transform.sax 此包实现特定于 SAX2 的转换 API。 javax.xml.transform.stax 提供特定于 StAX 的转换 API。 javax.xml.transform.stream 此包实现特定于流和 URI 的转换 API。 javax.xml.validation 此包提供了用于 XML 文档验证的 API。 javax.xml.ws 此包包含核心 JAX-WS API。 javax.xml.ws.handler 该包定义用于消息处理程序的 API。 javax.xml.ws.handler.soap 该包定义用于 SOAP 消息处理程序的 API。 javax.xml.ws.http 该包定义特定于 HTTP 绑定的 API。 javax.xml.ws.soap 该包定义特定于 SOAP 绑定的 API。 javax.xml.ws.spi 该包定义用于 JAX-WS 2.0 的 SPI。 javax.xml.xpath 此包提供了用于 XPath 表达式的计算和访问计算环境的 object-model neutral API。 org.ietf.jgss 此包提供一个框架,该框架允许应用程序开发人员通过利用统一的 API 使用一些来自各种基础安全机制(如 Kerberos)的安全服务,如验证、数据完整性和和数据机密性。 org.omg.CORBA 提供 OMG CORBA API 到 JavaTM 编程语言的映射,包括 ORB ,如果已实现该,则程序员可以使用此作为全功能对象请求代理(Object Request Broker,ORB)。 org.omg.CORBA_2_3 CORBA_2_3 包定义对 Java[tm] Standard Edition 6 现有 CORBA 接口所进行的添加。 org.omg.CORBA_2_3.portable 提供输入和输出值型的各种方法,并包含 org/omg/CORBA/portable 包的其他更新。 org.omg.CORBA.DynAnyPackage 提供与 DynAny 接口一起使用的异常(InvalidValue、Invalid、InvalidSeq 和 TypeMismatch)。 org.omg.CORBA.ORBPackage 提供由 ORB.resolve_initial_references 方法抛出的异常 InvalidName,以及由 ORB 的动态 Any 创建方法抛出的异常 InconsistentTypeCode。 org.omg.CORBA.portable 提供可移植性层,即可以使一个供应商生成的代码运行在另一个供应商 ORB 上的 ORB API 集合。 org.omg.CORBA.TypeCodePackage 提供用户定义的异常 BadKind 和 Bounds,它们将由 TypeCode 的方法抛出。 org.omg.CosNaming 为 Java IDL 提供命名服务。 org.omg.CosNaming.NamingContextExtPackage 此包包含以下在 org.omg.CosNaming.NamingContextExt 使用的: AddressHelper StringNameHelper URLStringHelper InvalidAddress 包规范 有关 Java[tm] Platform, Standard Edition 6 ORB 遵守的官方规范的受支持部分的明确列表,请参阅 Official Specifications for CORBA support in Java[tm] SE 6。 org.omg.CosNaming.NamingContextPackage 此包包含 org.omg.CosNaming 包的 Exception 。 org.omg.Dynamic 此包包含 OMG Portable Interceptor 规范 http://cgi.omg.org/cgi-bin/doc?ptc/2000-08-06 的第 21.9 小节指定的 Dynamic 模块。 org.omg.DynamicAny 提供一些和接口使得在运行时能够遍历与 any 有关联的数据值,并提取数据值的基本成分。 org.omg.DynamicAny.DynAnyFactoryPackage 此包包含 DynamicAny 模块的 DynAnyFactory 接口和异常,该模块在 OMG The Common Object Request Broker: Architecture and Specification http://cgi.omg.org/cgi-bin/doc?formal/99-10-07 的第 9.2.2 小节指定。 org.omg.DynamicAny.DynAnyPackage 此包包含 DynAny 模块的 DynAnyFactory 接口和异常,该模块在 OMG The Common Object Request Broker: Architecture and Specification http://cgi.omg.org/cgi-bin/doc?formal/99-10-07 的第 9.2 小节指定。 org.omg.IOP 此包包含在 OMG 文档 The Common Object Request Broker: Architecture and Specification http://cgi.omg.org/cgi-bin/doc?formal/99-10-07 的 13.6.小节指定的 IOP 模块。 org.omg.IOP.CodecFactoryPackage 此包包含 IOP::CodeFactory 接口指定的异常(作为 Portable Interceptor 规范的一部分)。 org.omg.IOP.CodecPackage 此包根据 IOP::Codec IDL 接口定义生成。 org.omg.Messaging 此包包含 OMG Messaging Interceptor 规范 http://cgi.omg.org/cgi-bin/doc?formal/99-10-07 指定的 Messaging 模块。 org.omg.PortableInterceptor 提供一个注册 ORB 钩子 (hook) 的机制,通过这些钩子 ORB 服务可以截取执行 ORB 的正常流。 org.omg.PortableInterceptor.ORBInitInfoPackage 此包包含 OMG Portable Interceptor 规范 http://cgi.omg.org/cgi-bin/doc?ptc/2000-08-06 的第 21.7.2 小节指定的 PortableInterceptor 模块的 ORBInitInfo 本地接口的异常和 typedef。 org.omg.PortableServer 提供一些和接口,用来生成跨多个供应商 ORB 的可移植应用程序的服务器端。 org.omg.PortableServer.CurrentPackage 提供各种方法实现,这些实现能够访问调用方法的对象的身份。 org.omg.PortableServer.POAManagerPackage 封装 POA 关联的处理状态。 org.omg.PortableServer.POAPackage 允许程序员构造可在不同 ORB 产品间移植的对象实现。 org.omg.PortableServer.portable 提供一些和接口,用来生成跨多个供应商 ORB 的可移植应用程序的服务器端。 org.omg.PortableServer.ServantLocatorPackage 提供定位 servant 的和接口。 org.omg.SendingContext 为值型的编组提供支持。 org.omg.stub.java.rmi 包含用于 java.rmi 包出现的 Remote 型的 RMI-IIOP Stub。 org.w3c.dom 为文档对象模型 (DOM) 提供接口,该模型是 Java API for XML Processing 的组件 API。 org.w3c.dom.bootstrap org.w3c.dom.events org.w3c.dom.ls org.xml.sax 此包提供了核心 SAX API。 org.xml.sax.ext 此包包含适合的 SAX 驱动程序不一定支持的 SAX2 设施的接口。 org.xml.sax.helpers 此包包含“帮助器”,其包括对引导基于 SAX 的应用程序的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI传道士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值