从 Java 8 转换到 Java 11 java时间戳转换成年月日时分秒毫秒
lipiwang 2024-10-27 13:28 7 浏览 0 评论
Java 8 2014年就发布了,到目前为止仍然是最流行的Java 版本,可以说Java 8是最后一个“古典” Java,因为JDK/JRE 没有引入模块化结构,所有的东西都是紧耦合的。对比Java 8 Java 11有了很多的改进,比如性能和内存用量等等,所以把旧有遗留代码移植到 Java 11上是对过去投资的一个保护。 然而将代码从 Java 8 转换到 Java 11 时,并没有一种适用于所有情况的解决方案。 对于不重要的应用程序来说,从 Java 8 迁移到 Java 11 可能意味着很大的工作量。 潜在问题包括:删除的 API、弃用的包、内部 API 的使用、对类加载程序的更改,以及对垃圾回收的更改。
通常,解决方法是尝试在不重新编译的情况下在 Java 11 上运行,或者先使用 JDK 11 进行编译。 如果目标是尽快启动并运行应用程序,则通常情况下,最佳方法是直接在 Java 11 上运行。 对于库,目标将是发布使用 JDK 11 编译和测试的项目。
迁移到 Java 11 值得付出这样的努力。 自 Java 8 发布以来,我们已添加了多项新功能并对原有功能进行了强化。 这些功能和增强功能可改进启动、性能和内存使用情况,并提供与容器更好的集成。 此外还对 API 进行了添加和修改,这可以提高开发人员的工作效率。
本文档介绍了用于检查代码的工具。 它还介绍了你可能遇到的问题以及解决这些问题的建议。 你还应参阅其他指南,如 Oracle JDK Migration Guide(Oracle JDK 迁移指南)。 本文不介绍如何将现有代码模块化。
工具箱
Java 11 有两个用于探查潜在问题的工具:jdeprscan 和 jdeps。 可以对现有类或 jar 文件运行这两个工具。 无需重新编译即可评估转换工作量。
jdeprscan 可查看是否使用了已弃用或已删除的 API。 使用已弃用的 API 不是阻塞性问题,但值得探讨。 是否有更新的 jar 文件? 是否需要记录某个问题才能解决已弃用 API 的使用问题? 使用已删除的 API 是阻塞性问题,必须予以解决,然后才能尝试在 Java 11 上运行应用程序。
jdeps,一个 Java 类依赖关系分析器。 与 --jdk-internals 选项一起使用时,jdeps 会告诉你哪个类依赖于哪个内部 API。 可以继续使用 Java 11 中的内部 API,但应优先考虑改变这种使用情况。 OpenJDK Wiki 页面 Java Dependency Analysis Tool(Java 依赖关系分析工具)推荐了某些常用 JDK 内部 API 的替换项。
Gradle 和 Maven 都有 jdeps 和 jdeprscan 插件。 建议将以下工具添加到生成脚本中。
Java 编译器本身 javac 是工具箱中的另一个工具。 从 jdeprscan 和 jdeps 获取的警告和错误来自编译器。 使用 jdeprscan 和 jdeps 的优点是,可以在现有的 jar 和类文件(包括第三方库)上运行这两个工具。
jdeprscan 和 jdeps 不能做的是对使用反射来访问封装的 API 进行警告。 反射访问在运行时进行检查。 最终必须在 Java 11 上运行代码才能确切地知道。
使用 jdeprscan
若要使用 jdeprscan,最简单的方法是为其提供一个来自现有生成的 jar 文件。 还可以为其指定目录(如编译器输出目录)或单个类名。 使用 --release 11 选项可获取已弃用 API 的最完整列表。 若要确定要采用的已弃用 API 的优先级,请将设置回退到 --release 8。 在 Java 8 中弃用的 API 的删除时间可能会早于最近弃用的 API。
控制台复制
jdeprscan --release 11 my-application.jar
如果无法解析依赖类,jdeprscan 工具会生成错误消息。 例如,error: cannot find class org/apache/logging/log4j/Logger 。 建议将依赖类添加到 --class-path 或使用应用程序 class-path,但该工具会在没有它的情况下继续扫描。 参数为 ??class?path。 class-path 参数的其他变体将不起作用。
控制台复制
jdeprscan --release 11 --class-path log4j-api-2.13.0.jar my-application.jar
error: cannot find class sun/misc/BASE64Encoder
class com/company/Util uses deprecated method java/lang/Double::<init>(D)V
此输出告诉我们,com.company.Util 类在调用 java.lang.Double 类的已弃用构造函数。 javadoc 会建议用来代替已弃用 API 的 API。 无论如何都无法解决“error: cannot find class sun/misc/BASE64Encoder”问题,因为它是已删除的 API。 自 Java 8 发布以来,应使用 java.util.Base64。
运行 jdeprscan --release 11 --list 即可了解自 Java 8 后弃用的具体 API。 若要获取已删除 API 的列表,请运行 jdeprscan --release 11 --list --for-removal。
使用 jdeps
可以使用 jdeps 通过 --jdk-internals 选项来查找 JDK 内部 API 上的依赖项。 此示例需要 --multi-release 11 命令行选项,因为 log4j-core-2.13.0.jar 是多版本 jar 文件。 没有此选项,jdeps 会在找到多版本 jar 文件的情况下发出错误消息。 此选项指定要检查的类文件的版本。
控制台复制
jdeps --jdk-internals --multi-release 11 --class-path log4j-core-2.13.0.jar my-application.jar
Util.class -> JDK removed internal API
Util.class -> jdk.base
Util.class -> jdk.unsupported
com.company.Util -> sun.misc.BASE64Encoder JDK internal API (JDK removed internal API)
com.company.Util -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.company.Util -> sun.nio.ch.Util JDK internal API (java.base)
Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependence on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
JDK Internal API Suggested Replacement
---------------- ---------------------
sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8
sun.misc.Unsafe See http://openjdk.java.net/jeps/260
输出提供了一些关于避免使用 JDK 内部 API 的好建议! 如果可能,建议使用替换 API。 在括号中提供封装了包的模块的名称。 如果需要显式中断封装,则可将模块名称与 --add-exports 或 --add-opens 配合使用。
使用 sun.misc.BASE64Encoder 或 sun.misc.BASE64Decoder 会导致 Java 11 中出现 java.lang.NoClassDefFoundError。 使用这些 API 的代码必须经过修改才能使用 java.util.Base64。
尝试不使用来自 jdk.unsupported 模块的任何 API。 此模块中的 API 将引用 JDK 增强方案 (JEP) 260 作为建议的替换方案。 简而言之,JEP 260 指出,在替换 API 可用之前,会一直支持使用内部 API。 虽然你的代码使用的是 JDK 内部 API,但至少在一段时间内它是可以正常运行的。 请看看 JEP 260,因为它指出了某些内部 API 的替换项。 例如,可以使用变量句柄来代替某个 sun.misc.Unsafe API。
除了扫描 JDK 内部 API 的使用情况,jdeps 还可以执行其他操作。 它是一项有用的工具,可以用来分析依赖关系和生成模块信息文件。 有关详细信息,请参阅文档。
使用 javac
如果使用 JDK 11 进行编译,则需要更新才能生成脚本、工具、测试框架和包含的库。 使用 javac 的 -Xlint:unchecked 选项可获取 JDK 内部 API 的使用详情和其他警告。 可能还需要使用 --add-opens 或 --add-reads 向编译器公开封装的包(请参阅 JEP 261)。
库可以考虑以多版本 jar 文件形式打包。 多版本 jar 文件允许同时支持同一 jar 文件中的 Java 8 和 Java 11 运行时。 它们增加了生成的复杂性。 如何生成多版本 jar 超出了本文档的讨论范围。
在 Java 11 上运行
大多数应用程序在不修改的情况下应该可以在 Java 11 上运行。 首先要尝试的是在不重新编译代码的情况下在 Java 11 上运行。 直接运行的目的是查看执行时会出现哪些警告和错误。 此方法可以
让应用程序在 Java 11 上更快地运行,因为可以尽量减少那些必须完成的关注事项。
你可能会遇到的大多数问题都可以得到解决,无需重新编译代码。 如果需要在代码中修复问题,请进行修复,但继续使用 JDK 8 进行编译。 如果可能,请在使用 JDK 11 进行编译 之前,让应用程序使用 java 版本 11 运行 。
检查命令行选项
在 Java 11 上运行之前,请对命令行选项进行快速扫描。 已删除的选项会导致 Java 虚拟机 (JVM) 退出。 如果使用 GC 日志记录选项,则此检查尤其重要,因为它们已明显不同于 Java 8 中的情况。 JaCoLine 工具是一项很好的工具,用于检查命令行选项的问题。
检查第三方库
你不能控制的第三方库是潜在的问题来源。 可以主动将第三方库更新到较新的版本。 也可查看运行应用程序时哪些库未使用,仅更新那些必需的库。 将所有库更新到最新版本的问题在于,如果应用程序中存在错误,则更难找到根本原因。 发生此错误是因为更新了某个库吗? 或者,此错误是由运行时中的某些更改引起的吗? 仅更新所需内容的问题在于,可能需要多次迭代才能解决问题。
此处的建议是尽可能少做更改,将第三方库单独进行更新。 如果更新第三方库,则往往需要与 Java 11 兼容的最新且最好的版本。 根据当前版本的落后程度,你可能需要采取更谨慎的方法,升级到第一个与 Java 9+ 兼容的版本。
除了查看发行说明以外,还可以使用 jdeps 和 jdeprscan 来评估 jar 文件。 此外,OpenJDK 质量组还维护一个质量外展 Wiki 页面,其中列出了根据 OpenJDK 版本对多个免费开源软件 (FOSS) 项目进行的测试的状态。
显式设置垃圾回收
并行垃圾回收器(并行 GC)是 Java 8 中的默认 GC。 如果应用程序使用默认值,则应使用命令行选项 -XX:+UseParallelGC 显式设置 GC。 Java 9 中的默认值已更改为 Garbage First 垃圾回收器 (G1GC)。 若要对 Java 8 与 Java 11 上运行的应用程序进行公平比较,GC 设置必须相同。 应推迟使用 GC 设置进行的试验,直到应用程序在 Java 11 上经过验证。
显式设置默认选项
如果在作用点 VM 上运行,则设置命令行选项 -XX:+PrintCommandLineFlags 会转储由 VM 设置的选项的值,特别是由 GC 设置的默认值。 在 Java 8 上使用此标志运行,在 Java 11 上运行时使用输出的选项。 大多数情况下,Java 8 到 11 中的默认值是相同的。 但是,使用 Java 8 中的设置可确保奇偶校验。
建议设置命令行选项 --illegal-access=warn。 在 Java 11 中,使用反射访问 JDK 内部 API 会生成一个“非法的反射访问”警告。 默认情况下,系统仅对第一次非法访问发出警告。 设置 --illegal-access=warn 会导致系统对每一次 非法反射访问发出警告。 如果将选项设置为 warn,则会发现更多非法访问案例。 但是,你也会收到大量冗余警告。
在 Java 11 上运行应用程序后,设置 --illegal-access=deny 即可模拟 Java 运行时的未来行为。 从 Java 16 开始,默认设置将为 --illegal-access=deny。
ClassLoader 注意事项
在 Java 8 中,可以将系统类加载程序强制转换为 URLClassLoader。 这通常由需要在运行时将类注入到 classpath 的应用程序和库完成。 类加载程序层次结构在 Java 11 中已更改。 系统类加载程序(也称为应用程序类加载程序)现在是一个内部类。 强制转换为 URLClassLoader 会在运行时引发 ClassCastException。 Java 11 无法通过 API 在运行时动态增强 classpath,但可以通过反射来实现这一点,它会显示有关如何使用内部 API 的显著警告。
在 Java 11 中,启动类加载程序只加载核心模块。 如果创建一个具有 null 父项的类加载程序,则它可能找不到全部平台类。 在 Java 11 中,需要在此类情况下传递 ClassLoader.getPlatformClassLoader() 而不是 null 作为父类加载程序。
区域设置数据更改
Java 11 中区域设置数据的默认源已通过 JEP 252 更改为 Unicode 联合会的公共区域设置数据存储库。 这可能会影响本地化的格式设置。 如有必要,请将系统属性设置为 java.locale.providers=COMPAT,SPI,还原到 Java 8 区域设置行为。
潜在问题
下面是可能会遇到的一些常见问题。
- 无法识别的 VM 选项
- 无法识别的选项
- VM 警告:忽略选项
- VM 警告:选项 <option> 已弃用
- 警告:发生非法的反射访问操作
- java.lang.reflect.InaccessibleObjectException
- java.lang.NoClassDefFoundError
- -Xbootclasspath/p 不再是受支持的选项
- java.lang.UnsupportedClassVersionError
无法识别的选项
如果删除了某个命令行选项,则应用程序会输出 Unrecognized option: 或 Unrecognized VM option,后跟有问题的选项的名称。 无法识别的选项会导致 VM 退出。 已弃用但未删除的选项会生成 VM 警告。
通常情况下,已删除的选项没有替换项,唯一办法是从命令行中删除该选项。 垃圾回收日志记录的选项是一个例外。 GC 日志记录已在 Java 9 中重新实现,可以使用统一 JVM 日志记录框架。 请参阅 Java SE 11 工具参考的允许通过 JVM 统一日志记录框架进行日志记录部分中的“表2-2 将旧的垃圾回收日志记录标志映射到 Xlog 配置”。
VM 警告
使用弃用的选项会生成警告。 当某个选项被替换或不再有用时,即表明它已被弃用。 与使用删除的选项一样,应从命令行中删除这些选项。 “VM Warning: Option <option> was deprecated”警告意味着,该选项仍受支持,但以后可能会取消该支持。 不再受支持的选项会生成“VM Warning: Ignoring option”警告。 不再受支持的选项不影响运行时。
Web 页面 VM 选项资源管理器提供了自 JDK 7 以后在 Java 中添加或删除的选项的详尽列表。
错误:无法创建 Java 虚拟机
当 JVM 遇到无法识别的选项时,会输出此错误消息。
警告:发生非法的反射访问操作
当 Java 代码使用反射访问 JDK 内部 API 时,运行时会发出“非法的反射访问”警告。
控制台复制
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by my.sample.Main (file:/C:/sample/) to method sun.nio.ch.Util.getTemporaryDirectBuffer(int)
WARNING: Please consider reporting this to the maintainers of com.company.Main
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
这意味着,模块未导出通过反射访问的包。 此包在模块中封装 ,本质上是内部 API。 在 Java 11 上启动并运行应用程序时,第一项操作可能就是忽略此警告。 Java 11 运行时允许反射访问,因此旧代码可以继续运行。
若要解决此警告,请查找不使用内部 API 的已更新代码。 如果无法使用更新的代码解决该问题,则可使用 --add-exports 或 --add-opens 命令行选项来启用对包的访问权限。 这些选项允许从一个模块访问另一个模块的未导出类型。
--add-exports 选项允许目标模块访问源模块的命名包的公共 类型。 有时,代码会使用 setAccessible(true) 访问非公共成员和 API。 这称为深度反射 。 在这种情况下,请使用 --add-opens,允许代码访问包的非公共成员。 如果不确定是使用 --add-exports 还是 --add-opens,请从 --add-exports 着手。
应将 --add-exports 或 --add-opens 选项视为一种权宜解决方案,而不是长期解决方案。 使用这些选项会打破模块系统的封装,该封装是为了防止 JDK 内部 API 被使用。 如果删除或更改内部 API,应用程序会发生故障。 Java 16 会拒绝反射访问,但通过命令行选项(如 --add-opens)启用访问的情况除外。 若要模拟未来行为,请在命令行中设置 --illegal-access=deny。
发出上述示例中的警告是因为 sun.nio.ch 包不是由 java.base 模块导出的。 换言之,模块 java.base 的 module-info.java 文件中没有 exports sun.nio.ch;。 这可以通过 --add-exports=java.base/sun.nio.ch=ALL-UNNAMED 来解决。 未在模块中定义的类隐式属于未命名 模块,在字面上命名为 ALL-UNNAMED。
java.lang.reflect.InaccessibleObjectException
此异常指示你尝试在已封装类的字段或方法上调用 setAccessible(true)。 也可能会收到一个“非法的反射访问”警告。 使用 --add-opens 选项可以让代码访问包的非公共成员。 异常消息会告知你,模块未将包打开到试图调用 setAccessible 的模块。 如果模块是“未命名模块”,请使用 UNNAMED-MODULE 作为 --add-opens 选项中的目标模块。
shell复制
java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.ArrayList jdk.internal.loader.URLClassPath.loaders accessible:
module java.base does not "opens jdk.internal.loader" to unnamed module @6442b0a6
$ java --add-opens=java.base/jdk.internal.loader=UNNAMED-MODULE example.Main
java.lang.NoClassDefFoundError
NoClassDefFoundError 最有可能是由拆分包或引用删除的模块导致的。
拆分包导致的 NoClassDefFoundError
如果在多个库中找到某个包,则该包为拆分包。 拆分包问题的症状是,你知道某个类会在 class-path 上,但找不到该类。
使用 module-path 时才会出现此问题。 Java 模块系统通过将包限制为一个命名的模块来优化类查找。 执行类查找时,运行时会优先处理 module-path 而不是 class-path。 如果包在某个模块和 class-path 之间拆分,则只使用该模块来执行类查找。 这可能导致 NoClassDefFound 错误。
若要检查拆分包,一个简单的方法是将模块路径和类路径插入 jdeps,使用应用程序类文件的路径作为 <path>。 如果有拆分包,jdeps 会输出警告:Warning: split package: <package-name> <module-path> <split-path>。
可以通过使用 --patch-module <module-name>=<path>[,<path>] 将拆分包添加到命名模块中来解决此问题。
使用 Java EE 或 CORBA 模块导致的 NoClassDefFoundError
如果应用程序在 Java 8 上运行但却引发 java.lang.NoClassDefFoundError 或 java.lang.ClassNotFoundException,则可能是应用程序在使用 Java EE 或 CORBA 模块中的包。 这些模块在 Java 9 弃用,在 Java 11 中删除。
若要解决此问题,请向项目添加运行时依赖项。
-Xbootclasspath/p 不再是受支持的选项
已删除对 -Xbootclasspath/p 的支持。 请改用 --patch-module。 --patch-module 选项在 JEP 261 中介绍。 查找标为“修补模块内容”的部分。 可以将 --patch-module 与 javac 和 java 配合使用,以便重写或增强模块中的类。
实际上, --patch-module 执行的操作是将修补模块插入模块系统的类查找。 模块系统会首先从修补模块获取类。 这与在 Java 8 中预挂起 bootclasspath 的效果相同。
UnsupportedClassVersionError
此异常表示你尝试在较低版本的 Java 上运行使用较高版本的 Java 编译的代码。 例如,在 Java 11 上运行其 jar 是使用 JDK 13 编译的程序。
后续步骤
在 Java 11 上运行应用程序后,请考虑将库移出 class-path,然后再将其移入 module-path。 查找应用程序所依赖的库的已更新版本。 选择模块库(如果可用)。 尽可能使用 module-path,即使不打算在应用程序中使用模块。 与 class-path 相比,使用 module-path 可以获得更好的类加载性能。
相关推荐
- 《每日电讯报》研发数字工具,教你更有效率地报道新闻
-
为鼓励新闻编辑部持续创新,《每日电讯报》正在尝试有战略地研发数字工具。网站的数字媒体主任马尔科姆o科尔斯(MalcolmColes)表示,《每日电讯报》正试图去“创建一些可持续资产”,以便于让记者们...
- html5学得好不好,看掌握多少标签
-
html5你了解了多少?如果你还是入门阶段的话,或者还是一知半解的话,那么我们专门为你们收集的html5常用的标签大全对你就很有帮助了,你需要了解了html5有哪些标签你才能够更好的。驾驭html5...
- 前端分享-少年了解过iframe么(我想了解少年)
-
iframe就像是HTML的「内嵌画布」,允许在页面中加载独立网页,如同在画布上叠加另一幅动态画卷。核心特性包括:独立上下文:每个iframe都拥有独立的DOM/CSS/JS环境(类似浏...
- 做SEO要知道什么是AJAX(人能看到但搜索引擎看不到的内容)
-
一个明显的,人能看到但搜索引擎不能看到的内容是AJAX。那么什么是AJAX呢?其实,了解过的基本上也都清楚,AJAX不是新的编程语言,而是一种使用现有标准的新方法。AJAX最大的优点是在不重新加...
- 介绍最前沿的人工智能创新,‘无反向传播’神经网络训练方法?
-
图像由GoogleImageFX生成前言:本文整理自NoProp原始论文与实践代码,并结合多个公开实现细节进行了全流程复现。对神经网络训练机制的探索仍在不断演进,如果你也在研究反向传播之...
- 说说我们对HTML6的期许(对html的看法)
-
HTML5概述HTML5是HTML语言最受欢迎的版本之一,它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了article,section,header这样的标签来帮助开发者更好...
- 浏览器中在线预览pdf文件,pdf.mjs插件实现web预览pdf
-
背景:本来只是淘宝上卖卖袜子,想着扩展一下业务,准备做同名“来家居”海外袜子馆外贸项目,碰到pdf在线预览的需求,就找了pdf.js插件进行实践后把此方法记录下来,可以通过多种方法来实现,每种方法都有...
- SVG 在前端的7种使用方法,你还知道哪几种?
-
本文简介点赞+关注+收藏=学会了技术一直在演变,在网页中使用SVG的方法也层出不穷。每个时期都有对应的最优解。所以我打算把我知道的7种SVG的使用方法列举出来,有备无患~如果你还...
- HTML5常用标签大全(html5em标签)
-
HTML前端开发最终取决于掌握标签的多少HTML大概有七八百个标签楼主这里给大家总结了下HTML常用标签标签描述<!--...-->定义注释。<!DOCTYPE>定义文档类型...
- "伪君子Snoop Dogg!"... WHAT?| MetroDaily 24/7
-
TUE.01-新作品-虽说年纪大了会有点糊涂,但是最近SnoopDogg的这波操作实在是让粉丝们有点迷,甚至有人表示没想到他是这样的"伪君子"......而这一切都源于他近日在IG上Po出的一...
- 莎夏·班克斯盼望表哥Snoop Dogg为其作出场曲
-
NXT女子冠军莎夏·班克斯(SashaBanks)近日接受了迈阿密先驱报采访,访谈纪要如下:关于她出众的形象:“我一向喜欢与众不同。为了能让人眼前一亮,我的装束总是非常前卫、非常抢眼,这样才能让观众...
- 喜欢Snoop!全球第一间「史努比博物馆」海外分馆在东京!
-
1950年起,由美國漫畫家CharlesM.Schulz創作的作品《Snoopy》史努比,其鮮明的可愛角色與幽默的劇情內容,至今仍成為許多大朋友與小朋友心中的最愛。為了紀念作者所設立的全球首...
- Vetements 推出 Snoop Dogg 肖像「天价」T-Shirt
-
Vetements的CEOGuramGvasalia早前才透露品牌经营策略的秘密–Vetements如何成为人人热议的话题品牌。但似乎他仍有更多需要解释的东西–这个法国奢侈品牌最新...
- 狗爷Snoop Dogg的《I Wanna Thank Me》巡回演唱会旧金山站
-
西海岸匪帮说唱歌手SnoopDogg在《IWannaThankMe》巡回演唱会旧金山站表演(图片来自ICphoto)西海岸匪帮说唱歌手SnoopDogg(图片来自ICphoto)西海...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)