java编译器jdk版本_以编程方式确定Java类的JDK编译版本
java编译器jdk版本
当需要确定使用哪个JDK版本来编译特定的Java .class
文件时, 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在我的博客文章Autoboxing,Unboxing和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章 (“类文件格式”)中描述了这些有效字节(magic,minor_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 Map<Integer, String> majorVersionToJdkVersion;static{final Map<Integer, 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_name>"System.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_name>"System.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 "\t"ClassVersion.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.html
java编译器jdk版本
java编译器jdk版本_以编程方式确定Java类的JDK编译版本相关推荐
- linux下java调用python脚本,java - 在Linux Terminal中以编程方式从Java调用python脚本 - 堆栈内存溢出...
我正在开发一个Java应用程序,用于检查源文件中的补丁程序(是否存在). 用于检测补丁程序更改的核心逻辑位于python脚本[titled'patch.py']中,并且我的Java应用程序与此Py ...
- 以编程方式确定Java类的JDK编译版本
当需要确定使用哪个JDK版本来编译特定的Java .class文件时, 通常使用的方法是使用javap并在javap输出中查找列出的"主要版本". 我在博客文章Autoboxing ...
- java生成和读取keystore_java-如何以编程方式创建新的KeyStore?
java-如何以编程方式创建新的KeyStore? 我正在尝试以编程方式在Java中创建新的密钥库. 如下代码: KeyStore keyStore = KeyStore.getInstance(Ke ...
- 如何以编程方式确定Java中的操作系统?
本文翻译自:How do I programmatically determine operating system in Java? I would like to determine the op ...
- java直接调用复制文件,java中文件复制的4种方式,java文件的复制
java中文件复制的4种方式,java文件的复制 今天一个同事问我文件复制的问题,他一个100M的文件复制的指定目录下竟然成了1G多,吓我一跳,后来看了他的代码发现是自己通过字节流复制的,定义的字节数 ...
- java 编译器获得型号_关于编译器构造:如何找到已编译类的目标Java版本?
重复: Tool to read and display Java .class versions 如果我有一个已编译的Java类,是否可以仅从类文件中得知其目标版本兼容性是什么? 具体来说,我有许多 ...
- java编程访问hdfs_以编程方式访问Java基本类型的大小
java编程访问hdfs 许多不熟悉Java的开发人员首先要了解的一件事是Java的基本原始数据类型 ,其固定(与平台无关)的大小(以位或字节为单位用二进制补码表示 )以及它们的范围(Java中所有数 ...
- 以编程方式访问Java基本类型的大小
许多不熟悉Java的开发人员首先要了解的一件事是Java的基本原始数据类型 ,其固定(与平台无关)的大小(以位或字节为单位用二进制补码表示 )以及它们的范围(Java中所有数字类型都是带符号的) ). ...
- java 柯里化_函数式编程(Java描述)——Java中的函数及其柯里化
本文继续上一篇的内容 在Java中,函数可以表现为一个普通的方法.一个lambda表达式,又或者方法引用,甚至是匿名类.本文不会介绍匿名类这种形式. 方法 Java中的方法,Java使用方法这一概念来 ...
最新文章
- 从微信AI首席顾问到金融文档智能
- Amdahl’s law (阿姆达尔定律)的演化和思考
- pythonfor循环range_python之for循环与range()函数
- 管理信息系统数据库设计标准(草稿)
- 攻占CNS!4篇Science+2篇Nature+1篇Cell,2019年内地学者开门红
- mysql 动态传入表名 存储过程_MySQL之常见约束以及事务和存储过程
- ubantu20下python安装和卸载
- python爬虫网络出错怎么办_python爬虫之headers处理、网络超时问题处理
- Springcloud学习系列之Ribbon自定义负载均衡规则
- 城市是最好的产业平台 —— 让企业成为城市的名片,城市成为企业的展厅
- Golang的Panic和Recover
- 【错误解决】Ubuntu 配置ibus中文输入法后却不能添加
- 积水成渊——数据中心用水效率分析
- python股票涨停_Python与量化投资-股票复盘工具-2018-02-02
- 融合DE 端和FE端数据,利用小波变换生成时频图,再分别利用DCNN、KNN和DNN进行对比实验(python代码)
- 【JavaWeb学习】HTML
- 使用CoreLocation定位
- 对php的感受100字_团建活动后的感想50字-100字《5篇》
- xp访问win10计算机名,win10共享打印机给xp凭证问题_xp连接win10打印机凭证不足解决方法...
- 接触角测试视频的 自动接触角测量
热门文章
- P4424-[HNOI/AHOI2018]寻宝游戏【结论】
- jzoj6801-NOIP2020.9.19模拟patrick【树状数组】
- P1903-[国家集训队]数颜色/维护队列【带修莫队】
- P3100-[USACO14JAN]建造滑雪场【贪心,dp】
- P2014-选课【树形dp,背包】
- Stack(nowcoder 11253-K)
- Sentinel(二十六)之Sentinel Dashboard中修改规则同步到Nacos
- Spring-SpringMVC父子容器
- Tomcat 使用apr优化
- 关联分析:FP-Growth算法