网站制作工具 织梦,免费简历制作软件app,做一个网站难不难,知名的传媒行业网站开发java编译器jdk版本当需要确定使用哪个JDK版本来编译特定的Java .class文件时#xff0c; 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在我的博客文章Autoboxing#xff0c;Unboxing和NoSuchMethodError中引用了这种方法#xff0c;但是在继续以编… java编译器jdk版本 当需要确定使用哪个JDK版本来编译特定的Java .class文件时 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在我的博客文章AutoboxingUnboxing和NoSuchMethodError中引用了这种方法但是在继续以编程方式实现此方法之前请先在此进行详细说明。 以下代码段演示了如何对commons-configuration-1.10.jar包含的Apache Commons Configuration类ServletFilterCommunication运行javap -verbose 。 我在上面显示的屏幕快照中圈了“主要版本”。 在“主要版本”之后列出的数字本例中为49表示用于编译此类的JDK版本是J2SE 5 。 Java类文件的Wikipedia页面列出了与每个JDK版本相对应的“主要版本”数字 主要版本 JDK版本 52 Java SE 8 51 Java SE 7 50 Java SE 6 49 J2SE 5 48 JDK 1.4 47 JDK 1.3 46 JDK 1.2 45 JDK 1.1 这是确定用于编译.class文件的JDK版本的简便方法但是对目录或JAR文件中的多个类执行此操作可能会变得很乏味。 如果我们可以以编程方式检查此主要版本以便可以编写脚本则会更容易。 幸运的是Java确实支持这一点。 Matthias Ernst发布了“ 代码段以编程方式调用javap ”其中他演示了如何使用JDK工具JAR中的JavapEnvironment以编程方式执行javap功能但是有一种更简便的方法来标识.class文件中指示字节用于编译的JDK版本。 博客文章“ 从类格式主要/次要版本信息中识别Java编译器版本 ”和StackOverflow线程“ Java API来找出要为类文件编译的JDK版本 ”演示了如何使用DataInputStream从Java .class文件中读取相关的两个字节。 对用于编译.class文件的JDK版本的基本访问 下一个代码清单演示了访问.class文件的JDK编译版本的简约方法。 final DataInputStream input new DataInputStream(new FileInputStream(pathFileName));
input.skipBytes(4);
final int minorVersion input.readUnsignedShort();
final int majorVersion input.readUnsignedShort(); 该代码在感兴趣的假定的 .class文件上实例化FileInputStream 并且该FileInputStream用于实例化DataInputStream 。 有效.class文件的前四个字节包含数字指示该数字是有效的Java编译类因此被跳过。 接下来的两个字节被读为无符号的short代表次要版本。 在那之后是最重要的两个字节。 它们也以未签名的缩写形式读入代表主要版本。 这个主要版本与JDK的特定版本直接相关。 Java虚拟机规范的 第4章 “类文件格式”中描述了这些有效字节magicminor_version和major_version。 在上面的代码清单中为方便理解仅跳过了“魔术” 4个字节。 但是我更喜欢检查这四个字节以确保它们是.class文件所期望的。 JVM规范解释了对这前四个字节的期望。 它的值为0xCAFEBABE。” 下一个代码清单将修改前面的代码清单并添加检查以确保所讨论的文件在Java编译的.class文件中。 请注意该检查专门使用十六进制表示形式CAFEBABE以提高可读性。 final DataInputStream input new DataInputStream(new FileInputStream(pathFileName));
// The first 4 bytes of a .class file are 0xCAFEBABE and are used to
// identify file as conforming to the class file format.
// Use those to ensure the file being processed is a Java .class file.
final String firstFourBytes Integer.toHexString(input.readUnsignedShort()) Integer.toHexString(input.readUnsignedShort());
if (!firstFourBytes.equalsIgnoreCase(cafebabe))
{throw new IllegalArgumentException(pathFileName is NOT a Java .class file.);
}
final int minorVersion input.readUnsignedShort();
final int majorVersion input.readUnsignedShort(); 在检查了其中最重要的部分之后下面的代码清单提供了我称为ClassVersion.java的Java类的完整清单。 它具有main(String[])函数因此可以从命令行轻松使用其功能。 ClassVersion.java import static java.lang.System.out;import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;/*** Prints out the JDK version used to compile .class files. */
public class ClassVersion
{private static final MapInteger, String majorVersionToJdkVersion;static{final MapInteger, String tempMajorVersionToJdkVersion new HashMap();tempMajorVersionToJdkVersion.put(45, JDK 1.1);tempMajorVersionToJdkVersion.put(46, JDK 1.2);tempMajorVersionToJdkVersion.put(47, JDK 1.3);tempMajorVersionToJdkVersion.put(48, JDK 1.4);tempMajorVersionToJdkVersion.put(49, J2SE 5);tempMajorVersionToJdkVersion.put(50, Java SE 6);tempMajorVersionToJdkVersion.put(51, Java SE 7);tempMajorVersionToJdkVersion.put(52, Java SE 8);majorVersionToJdkVersion Collections.unmodifiableMap(tempMajorVersionToJdkVersion);}/*** Print (to standard output) the major and minor versions of JDK that the* provided .class file was compiled with.** param pathFileName Name of (presumably) .class file from which the major* and minor versions of the JDK used to compile that class are to be* extracted and printed to standard output.*/public static void printCompiledMajorMinorVersions(final String pathFileName){try{final DataInputStream input new DataInputStream(new FileInputStream(pathFileName));printCompiledMajorMinorVersions(input, pathFileName);}catch (FileNotFoundException fnfEx){out.println(ERROR: Unable to find file pathFileName);}}/*** Print (to standard output) the major and minor versions of JDK that the* provided .class file was compiled with.** param input DataInputStream instance assumed to represent a .class file* from which the major and minor versions of the JDK used to compile* that class are to be extracted and printed to standard output.* param dataSourceName Name of source of data from which the provided* DataInputStream came.*/public static void printCompiledMajorMinorVersions(final DataInputStream input, final String dataSourceName){ try{// The first 4 bytes of a .class file are 0xCAFEBABE and are used to// identify file as conforming to the class file format.// Use those to ensure the file being processed is a Java .class file.final String firstFourBytes Integer.toHexString(input.readUnsignedShort()) Integer.toHexString(input.readUnsignedShort());if (!firstFourBytes.equalsIgnoreCase(cafebabe)){throw new IllegalArgumentException(dataSourceName is NOT a Java .class file.);}final int minorVersion input.readUnsignedShort();final int majorVersion input.readUnsignedShort();out.println(dataSourceName was compiled with convertMajorVersionToJdkVersion(majorVersion) ( majorVersion / minorVersion ));}catch (IOException exception){out.println(ERROR: Unable to process file dataSourceName to determine JDK compiled version - exception);}}/*** Accepts a major version and provides the associated name of the JDK* version corresponding to that major version if one exists.** param majorVersion Two-digit major version used in .class file.* return Name of JDK version associated with provided major version.*/public static String convertMajorVersionToJdkVersion(final int majorVersion){return majorVersionToJdkVersion.get(majorVersion) ! null? majorVersionToJdkVersion.get(majorVersion): Unknown JDK version for major version of majorVersion;}public static void main(final String[] arguments){if (arguments.length 1){out.println(USAGE: java ClassVersion nameOfClassFile.class);System.exit(-1);}printCompiledMajorMinorVersions(arguments[0]);}
} 下一个屏幕快照演示了如何针对自己的.class文件运行此类。 如PowerShell控制台的最后一个屏幕快照所示该类的版本是使用JDK 8编译的。 有了这个ClassVersion 我们就能使用Java告诉我们何时编译特定的.class文件。 但是这比简单地使用javap并手动寻找“主要版本”要容易得多。 使它更强大和更易于使用的是在脚本中使用它。 考虑到这一点我现在将重点放在利用此类的Groovy脚本中以识别用于编译JAR或目录中的多个.class文件的JDK版本。 下一个代码清单是可以使用ClassVersion类的Groovy脚本的示例。 该脚本演示了用于编译指定目录及其子目录中的所有.class文件的JDK版本。 displayCompiledJdkVersionsOfClassFilesInDirectory.groovy #!/usr/bin/env groovy// displayCompiledJdkVersionsOfClassFilesInDirectory.groovy
//
// Displays the version of JDK used to compile Java .class files in a provided
// directory and in its subdirectories.
//if (args.length 1)
{println USAGE: displayCompiledJdkVersionsOfClassFilesInDirectory.groovy directory_nameSystem.exit(-1)
}File directory new File(args[0])
String directoryName directory.canonicalPath
if (!directory.isDirectory())
{println ERROR: ${directoryName} is not a directory.System.exit(-2)
}print \nJDK USED FOR .class COMPILATION IN DIRECTORIES UNDER
println ${directoryName}\n
directory.eachFileRecurse
{ file -String fileName file.canonicalPathif (fileName.endsWith(.class)){ClassVersion.printCompiledMajorMinorVersions(fileName)}
}
println \n 接下来显示的是刚刚列出的脚本生成的输出示例。 接下来显示另一个Groovy脚本该脚本可用于标识用于编译指定目录或其子目录之一中的任何JAR文件中的.class文件的JDK版本。 displayCompiledJdkVersionsOfClassFilesInJar.groovy #!/usr/bin/env groovy// displayCompiledJdkVersionsOfClassFilesInJar.groovy
//
// Displays the version of JDK used to compile Java .class files in JARs in the
// specified directory or its subdirectories.
//if (args.length 1)
{println USAGE: displayCompiledJdkVersionsOfClassFilesInJar.groovy jar_nameSystem.exit(-1)
}import java.util.zip.ZipFile
import java.util.zip.ZipExceptionString rootDir args ? args[0] : .
File directory new File(rootDir)
directory.eachFileRecurse
{ file-if (file.isFile() file.name.endsWith(jar)){try{zip new ZipFile(file)entries zip.entries()entries.each{ entry-if (entry.name.endsWith(.class)){println ${file}print \tClassVersion.printCompiledMajorMinorVersions(new DataInputStream(zip.getInputStream(entry)), entry.name)}}}catch (ZipException zipEx){println Unable to open file ${file.name}}}
}
println \n 接下来显示针对本文前面部分使用的JAR运行此脚本的输出的早期部分。 JAR中包含的所有.class文件都具有JDK的版本它们针对打印到标准输出而编译。 其他想法 刚刚显示的脚本演示了一些实用程序这些实用程序是通过能够以编程方式访问用于编译Java类的JDK版本而实现的。 这里是增强这些脚本的其他一些想法。 在某些情况下我使用了这些增强功能但此处并未显示它们以保持更好的清晰度并避免使发布时间更长。 ClassVersion.java可能是用Groovy编写的。 ClassVersion.java返回单个信息而不是将其打印到标准输出则其功能将更加灵活。 同样即使返回返回的字符串也比假设调用者希望将输出写入标准输出更为灵活。 这将是容易巩固上述脚本指示用于编译个体JDK版本.class以及在目录中直接访问文件.class包含在JAR文件的文件从相同的脚本。 演示脚本的一种有用的变体是返回在特定版本的JDK之前或之后使用特定版本的JDK编译的所有.class文件的脚本。 结论 这篇文章的目的是演示以编程方式确定用于将Java源代码编译为.class文件的JDK版本。 文章演示了基于JVM类文件结构的“主要版本”字节确定用于编译的JDK版本然后演示了如何使用Java API读取和处理.class文件以及识别用于编译它们的JDK版本。 最后用Groovy编写的几个示例脚本演示了以编程方式访问此信息的价值。 翻译自: https://www.javacodegeeks.com/2015/02/programmatically-determining-java-classs-jdk-compilation-version.htmljava编译器jdk版本